Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transaction/validate): advance transaction validation when producing blocks #5575

Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -844,4 +844,17 @@ public BalanceContract.TransferContract getTransferContract() {
return null;
}
}

public int getContractSize() {
return this.transaction.getRawData().getContractCount();
}

public byte[] getRefBlockHash() {
return this.transaction.getRawData().getRefBlockHash().toByteArray();
}

public byte[] getRefBlockBytes() {
return this.transaction.getRawData().getRefBlockBytes().toByteArray();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.tron.common.validator;

import org.tron.core.capsule.TransactionCapsule;

public abstract class AbstractTransactionValidator implements Validator<TransactionCapsule> {
private Validator<TransactionCapsule> next;

@Override
public Validator<TransactionCapsule> nextValidator(Validator<TransactionCapsule> next) {
this.next = next;
return this.next;
}

@Override
public String validate(final TransactionCapsule trx) {
String ret = doValidate(trx);
if (ret == null && this.next != null) {
return this.next.validate(trx);
}
return ret;
}

protected abstract String doValidate(final TransactionCapsule trx);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package org.tron.common.validator;

import org.springframework.stereotype.Component;
import org.tron.core.Constant;
import org.tron.core.capsule.TransactionCapsule;

@Component("bigTransactionValidator")
public class BigTransactionValidator extends AbstractTransactionValidator {

@Override
protected String doValidate(TransactionCapsule trx) {
if (trx.getSerializedSize() > Constant.TRANSACTION_MAX_BYTE_SIZE) {
return String.format("too big transaction, the size is %d bytes", trx.getSerializedSize());
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.tron.common.validator;

import org.springframework.stereotype.Component;
import org.tron.core.capsule.TransactionCapsule;

@Component("contractSizeValidator")
public class ContractSizeValidator extends AbstractTransactionValidator {

@Override
protected String doValidate(TransactionCapsule trx) {
int contractSize = trx.getContractSize();
if (contractSize != 1) {
return String.format("contract size should be exactly 1, this is extend feature ,actual :%d",
contractSize);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package org.tron.common.validator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.db.TransactionCache;
import org.tron.core.db.TransactionStore;

@Component("dupTransactionValidator")
public class DupTransactionValidator extends AbstractTransactionValidator {

@Autowired
private TransactionStore transactionStore;

@Autowired
private TransactionCache transactionCache;

@Override
protected String doValidate(TransactionCapsule trx) {
byte[] transactionId = trx.getTransactionId().getBytes();
if (transactionCache.has(transactionId) && transactionStore.has(transactionId)) {
return "dup trans";
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.tron.common.validator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.ChainBaseManager;
import org.tron.core.Constant;
import org.tron.core.capsule.TransactionCapsule;

@Component("expiredTransactionValidator")
public class ExpiredTransactionValidator extends AbstractTransactionValidator {

@Autowired
private ChainBaseManager chainBaseManager;

@Override
protected String doValidate(TransactionCapsule trx) {
long transactionExpiration = trx.getExpiration();
long headBlockTime = chainBaseManager.getHeadBlockTimeStamp();
if (transactionExpiration <= headBlockTime
|| transactionExpiration > headBlockTime + Constant.MAXIMUM_TIME_UNTIL_EXPIRATION) {
return String.format(
"Transaction expiration, transaction expiration time is %d, but headBlockTime is %d",
transactionExpiration, headBlockTime);
}
return null;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package org.tron.common.validator;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.exception.ValidateSignatureException;
import org.tron.core.store.AccountStore;
import org.tron.core.store.DynamicPropertiesStore;

@Component("signatureValidator")
public class SignatureValidator extends AbstractTransactionValidator {

@Autowired
private AccountStore accountStore;
@Autowired
private DynamicPropertiesStore dynamicPropertiesStore;

@Override
protected String doValidate(TransactionCapsule trx) {
try {
trx.validateSignature(accountStore, dynamicPropertiesStore);
return null;
} catch (ValidateSignatureException e) {
return e.getMessage();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package org.tron.common.validator;

import java.util.Arrays;
import org.bouncycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.common.utils.ByteArray;
import org.tron.core.ChainBaseManager;
import org.tron.core.capsule.TransactionCapsule;
import org.tron.core.db.RecentBlockStore;
import org.tron.core.exception.ItemNotFoundException;

@Component("taposTransactionValidator")
public class TaposTransactionValidator extends AbstractTransactionValidator {

@Autowired
private ChainBaseManager chainBaseManager;
@Autowired
private RecentBlockStore recentBlockStore;

@Override
protected String doValidate(TransactionCapsule trx) {
byte[] refBlockHash = trx.getRefBlockHash();
byte[] refBlockNumBytes = trx.getRefBlockBytes();
try {
byte[] blockHash = recentBlockStore.get(refBlockNumBytes).getData();
if (!Arrays.equals(blockHash, refBlockHash)) {
return String.format(
"Tapos failed, different block hash, %s, %s , recent block %s, "
+ "solid block %s head block %s",
ByteArray.toLong(refBlockNumBytes), Hex.toHexString(refBlockHash),
Hex.toHexString(blockHash),
chainBaseManager.getSolidBlockId().getString(),
chainBaseManager.getHeadBlockId().getString());
}
return null;
} catch (ItemNotFoundException e) {
return String
.format("Tapos failed, block not found, ref block %s, %s , solid block %s head block %s",
ByteArray.toLong(refBlockNumBytes), Hex.toHexString(refBlockHash),
chainBaseManager.getSolidBlockId().getString(),
chainBaseManager.getHeadBlockId().getString());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package org.tron.common.validator;

import javax.annotation.PostConstruct;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.tron.core.capsule.TransactionCapsule;

@Component
@Slf4j(topic = "transactionValidator")
public class TransactionValidator {

@Autowired
private Validator<TransactionCapsule> dupTransactionValidator;
@Autowired
private Validator<TransactionCapsule> bigTransactionValidator;
@Autowired
private Validator<TransactionCapsule> expiredTransactionValidator;
@Autowired
private Validator<TransactionCapsule> contractSizeValidator;
@Autowired
private Validator<TransactionCapsule> taposTransactionValidator;
@Autowired
private Validator<TransactionCapsule> signatureValidator;

@PostConstruct
private void prepare() {
dupTransactionValidator
.nextValidator(bigTransactionValidator)
.nextValidator(expiredTransactionValidator)
.nextValidator(contractSizeValidator)
.nextValidator(taposTransactionValidator)
.nextValidator(signatureValidator);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The way you assembled the validator, the coupling is relatively high, can it be changed to a dynamic loading form to reduce the code coupling


public boolean validate(final TransactionCapsule trx) {
try {
String info = dupTransactionValidator.validate(trx);
if (info != null) {
logger.info("invalid transaction {}, {}.", trx.getTransactionId(), info);
return false;
}
return true;
} catch (Exception e) {
logger.warn("validate transaction {}.", trx.getTransactionId(), e);
return false;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package org.tron.common.validator;

public interface Validator<V> {

String validate(final V v);

Validator<V> nextValidator(Validator<V> next);
}
36 changes: 23 additions & 13 deletions framework/src/main/java/org/tron/core/db/Manager.java
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import org.tron.common.utils.SessionOptional;
import org.tron.common.utils.Sha256Hash;
import org.tron.common.utils.StringUtil;
import org.tron.common.validator.TransactionValidator;
import org.tron.common.zksnark.MerkleContainer;
import org.tron.consensus.Consensus;
import org.tron.consensus.base.Param.Miner;
Expand Down Expand Up @@ -260,6 +261,9 @@ public class Manager {
private ExecutorService filterEs;
private static final String filterEsName = "filter";

@Autowired
TransactionValidator transactionValidator;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a private modifier for control


/**
* Cycle thread to rePush Transactions
*/
Expand Down Expand Up @@ -1419,22 +1423,24 @@ public TransactionInfo processTransaction(final TransactionCapsule trxCap, Block
chainBaseManager.getBalanceTraceStore().initCurrentTransactionBalanceTrace(trxCap);
}

validateTapos(trxCap);
validateCommon(trxCap);
if (blockCap == null || blockCap.hasWitnessSignature()) {
validateTapos(trxCap);
validateCommon(trxCap);

if (trxCap.getInstance().getRawData().getContractList().size() != 1) {
throw new ContractSizeNotEqualToOneException(
String.format(
"tx %s contract size should be exactly 1, this is extend feature ,actual :%d",
txId, trxCap.getInstance().getRawData().getContractList().size()));
}
if (trxCap.getInstance().getRawData().getContractList().size() != 1) {
throw new ContractSizeNotEqualToOneException(
String.format(
"tx %s contract size should be exactly 1, this is extend feature ,actual :%d",
txId, trxCap.getInstance().getRawData().getContractList().size()));
}

validateDup(trxCap);
validateDup(trxCap);

if (!trxCap.validateSignature(chainBaseManager.getAccountStore(),
chainBaseManager.getDynamicPropertiesStore())) {
throw new ValidateSignatureException(
String.format(" %s transaction signature validate failed", txId));
if (!trxCap.validateSignature(chainBaseManager.getAccountStore(),
chainBaseManager.getDynamicPropertiesStore())) {
throw new ValidateSignatureException(
String.format(" %s transaction signature validate failed", txId));
}
}

TransactionTrace trace = new TransactionTrace(trxCap, StoreFactory.getInstance(),
Expand Down Expand Up @@ -1597,6 +1603,10 @@ public BlockCapsule generateBlock(Miner miner, long blockTime, long timeout) {
postponedTrxCount++;
continue; // try pack more small trx
}
// check transaction
if (!transactionValidator.validate(trx)) {
continue;
}
//shielded transaction
Transaction transaction = trx.getInstance();
if (isShieldedTransaction(transaction)
Expand Down
Loading