diff --git a/rskj-core/src/main/java/co/rsk/RskContext.java b/rskj-core/src/main/java/co/rsk/RskContext.java index dc4c49a1f7d..169304d22b7 100644 --- a/rskj-core/src/main/java/co/rsk/RskContext.java +++ b/rskj-core/src/main/java/co/rsk/RskContext.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk; import co.rsk.bitcoinj.core.NetworkParameters; @@ -402,7 +401,7 @@ public TxQuotaChecker getTxQuotaChecker() { checkIfNotClosed(); if (this.txQuotaChecker == null) { - this.txQuotaChecker = new TxQuotaChecker(System::currentTimeMillis); + this.txQuotaChecker = new TxQuotaChecker(System::currentTimeMillis, getReceivedTxSignatureCache()); } return txQuotaChecker; } @@ -417,6 +416,16 @@ public synchronized ReceivedTxSignatureCache getReceivedTxSignatureCache() { return receivedTxSignatureCache; } + public BlockTxSignatureCache getBlockTxSignatureCache() { + checkIfNotClosed(); + + if (blockTxSignatureCache == null) { + blockTxSignatureCache = new BlockTxSignatureCache(getReceivedTxSignatureCache()); + } + + return blockTxSignatureCache; + } + public synchronized RepositoryLocator getRepositoryLocator() { checkIfNotClosed(); @@ -485,7 +494,10 @@ public synchronized PrecompiledContracts getPrecompiledContracts() { checkIfNotClosed(); if (precompiledContracts == null) { - precompiledContracts = new PrecompiledContracts(getRskSystemProperties(), getBridgeSupportFactory()); + precompiledContracts = new PrecompiledContracts( + getRskSystemProperties(), + getBridgeSupportFactory(), + getBlockTxSignatureCache()); } return precompiledContracts; @@ -497,7 +509,7 @@ public synchronized BridgeSupportFactory getBridgeSupportFactory() { if (bridgeSupportFactory == null) { bridgeSupportFactory = new BridgeSupportFactory(getBtcBlockStoreFactory(), getRskSystemProperties().getNetworkConstants().getBridgeConstants(), - getRskSystemProperties().getActivationConfig()); + getRskSystemProperties().getActivationConfig(), getBlockTxSignatureCache()); } return bridgeSupportFactory; @@ -815,7 +827,8 @@ public synchronized TraceModule getTraceModule() { getBlockStore(), getReceiptStore(), getBlockExecutor(), - getExecutionBlockRetriever() + getExecutionBlockRetriever(), + getBlockTxSignatureCache() ); } @@ -836,7 +849,7 @@ public synchronized TxPoolModule getTxPoolModule() { checkIfNotClosed(); if (txPoolModule == null) { - txPoolModule = new TxPoolModuleImpl(getTransactionPool()); + txPoolModule = new TxPoolModuleImpl(getTransactionPool(), getReceivedTxSignatureCache()); } return txPoolModule; @@ -1101,7 +1114,7 @@ public synchronized BlockParentDependantValidationRule getBlockParentDependantVa if (blockParentDependantValidationRule == null) { Constants commonConstants = getRskSystemProperties().getNetworkConstants(); blockParentDependantValidationRule = new BlockParentCompositeRule( - new BlockTxsFieldsValidationRule(), + new BlockTxsFieldsValidationRule(getBlockTxSignatureCache()), new BlockTxsValidationRule(getRepositoryLocator(), getBlockTxSignatureCache()), new PrevMinGasPriceRule(), new BlockParentNumberRule(), @@ -1288,7 +1301,8 @@ protected synchronized Web3 buildWeb3() { getBuildInfo(), getBlocksBloomStore(), getWeb3InformationRetriever(), - getSyncProcessor()); + getSyncProcessor(), + getBlockTxSignatureCache()); } protected synchronized Web3InformationRetriever getWeb3InformationRetriever() { @@ -1442,14 +1456,6 @@ private void initializeNativeLibs() { AbstractAltBN128.init(); } - private BlockTxSignatureCache getBlockTxSignatureCache() { - if (blockTxSignatureCache == null) { - blockTxSignatureCache = new BlockTxSignatureCache(getReceivedTxSignatureCache()); - } - - return blockTxSignatureCache; - } - private KeyValueDataSource getBlocksBloomDataSource() { if (this.blocksBloomDataSource == null) { this.blocksBloomDataSource = this.buildBlocksBloomDataSource(); @@ -1766,7 +1772,8 @@ private BlockToMineBuilder getBlockToMineBuilder() { getBlockFactory(), getBlockExecutor(), new MinimumGasPriceCalculator(Coin.valueOf(getMiningConfig().getMinGasPriceTarget())), - new MinerUtils() + new MinerUtils(), + getBlockTxSignatureCache() ); } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/PendingState.java b/rskj-core/src/main/java/co/rsk/core/bc/PendingState.java index 0c794416701..d4ff4529751 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/PendingState.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/PendingState.java @@ -15,17 +15,13 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.core.bc; import co.rsk.core.Coin; import co.rsk.core.RskAddress; import co.rsk.crypto.Keccak256; import co.rsk.db.RepositorySnapshot; -import org.ethereum.core.Repository; -import org.ethereum.core.Transaction; -import org.ethereum.core.TransactionExecutor; -import org.ethereum.core.TransactionSet; +import org.ethereum.core.*; import org.ethereum.util.ByteUtil; import org.ethereum.vm.DataWord; import org.slf4j.Logger; @@ -46,12 +42,13 @@ public class PendingState implements AccountInformationProvider { private final TransactionExecutorFactory transactionExecutorFactory; private final TransactionSet pendingTransactions; private boolean executed = false; + private final SignatureCache signatureCache; - - public PendingState(RepositorySnapshot repository, TransactionSet pendingTransactions, TransactionExecutorFactory transactionExecutorFactory) { + public PendingState(RepositorySnapshot repository, TransactionSet pendingTransactions, TransactionExecutorFactory transactionExecutorFactory, SignatureCache signatureCache) { this.pendingRepository = repository.startTracking(); this.pendingTransactions = pendingTransactions; this.transactionExecutorFactory = transactionExecutorFactory; + this.signatureCache = signatureCache; } @Override @@ -117,13 +114,13 @@ public BigInteger getNonce(RskAddress addr) { // Note that this sort doesn't return the best solution, it is an approximation algorithm to find approximate // solution. (No trivial solution) - public static List sortByPriceTakingIntoAccountSenderAndNonce(List transactions) { + public static List sortByPriceTakingIntoAccountSenderAndNonce(List transactions, SignatureCache signatureCache) { //Priority heap, and list of transactions are ordered by descending gas price. Comparator gasPriceComparator = reverseOrder(Comparator.comparing(Transaction::getGasPrice)); //First create a map to separate txs by each sender. - Map> senderTxs = transactions.stream().collect(Collectors.groupingBy(Transaction::getSender)); + Map> senderTxs = transactions.stream().collect(Collectors.groupingBy(transaction -> transaction.getSender(signatureCache))); //For each sender, order all txs by nonce and then by hash, //finally we order by price in cases where nonce are equal, and then by hash to disambiguate @@ -151,7 +148,7 @@ public static List sortByPriceTakingIntoAccountSenderAndNonce(List< while (txsCount > 0) { Transaction nextTxToAdd = candidateTxs.remove(); sortedTxs.add(nextTxToAdd); - List txs = senderTxs.get(nextTxToAdd.getSender()); + List txs = senderTxs.get(nextTxToAdd.getSender(signatureCache)); if (!txs.isEmpty()) { Transaction tx = txs.remove(0); candidateTxs.add(tx); @@ -172,8 +169,7 @@ private T postExecutionReturn(PostExecutionAction action) { } private void executeTransactions(Repository currentRepository, List pendingTransactions) { - - PendingState.sortByPriceTakingIntoAccountSenderAndNonce(pendingTransactions) + PendingState.sortByPriceTakingIntoAccountSenderAndNonce(pendingTransactions, signatureCache) .forEach(pendingTransaction -> executeTransaction(currentRepository, pendingTransaction)); } diff --git a/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java b/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java index d3e93159a52..1b784dd62fc 100644 --- a/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java +++ b/rskj-core/src/main/java/co/rsk/core/bc/TransactionPoolImpl.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.core.bc; import co.rsk.config.RskSystemProperties; @@ -52,8 +51,8 @@ public class TransactionPoolImpl implements TransactionPool { private static final Logger logger = LoggerFactory.getLogger("txpool"); - private final TransactionSet pendingTransactions = new TransactionSet(); - private final TransactionSet queuedTransactions = new TransactionSet(); + private final TransactionSet pendingTransactions; + private final TransactionSet queuedTransactions; private final Map transactionBlocks = new HashMap<>(); private final Map transactionTimes = new HashMap<>(); @@ -95,7 +94,10 @@ public TransactionPoolImpl(RskSystemProperties config, RepositoryLocator reposit this.quotaChecker = txQuotaChecker; this.gasPriceTracker = gasPriceTracker; - this.validator = new TxPendingValidator(config.getNetworkConstants(), config.getActivationConfig(), config.getNumOfAccountSlots()); + pendingTransactions = new TransactionSet(this.signatureCache); + queuedTransactions = new TransactionSet(this.signatureCache); + + this.validator = new TxPendingValidator(config.getNetworkConstants(), config.getActivationConfig(), config.getNumOfAccountSlots(), signatureCache); if (this.outdatedTimeout > 0) { this.cleanerTimer = Executors.newSingleThreadScheduledExecutor(r -> new Thread(r, "TransactionPoolCleanerTimer")); @@ -158,7 +160,7 @@ public PendingState getPendingState() { private PendingState getPendingState(RepositorySnapshot currentRepository) { removeObsoleteTransactions(this.outdatedThreshold, this.outdatedTimeout); - return new PendingState(currentRepository, new TransactionSet(pendingTransactions), (repository, tx) -> transactionExecutorFactory.newInstance(tx, 0, bestBlock.getCoinbase(), repository, createFakePendingBlock(bestBlock), 0)); + return new PendingState(currentRepository, new TransactionSet(pendingTransactions, signatureCache), (repository, tx) -> transactionExecutorFactory.newInstance(tx, 0, bestBlock.getCoinbase(), repository, createFakePendingBlock(bestBlock), 0), signatureCache); } private RepositorySnapshot getCurrentRepository() { @@ -215,7 +217,7 @@ public synchronized List addTransactions(final List tx private Optional getQueuedSuccessor(Transaction tx) { BigInteger next = tx.getNonceAsInteger().add(BigInteger.ONE); - List txsaccount = this.queuedTransactions.getTransactionsWithSender(tx.getSender()); + List txsaccount = this.queuedTransactions.getTransactionsWithSender(tx.getSender(signatureCache)); if (txsaccount == null) { return Optional.empty(); @@ -243,7 +245,7 @@ private TransactionPoolAddResult internalAddTransaction(final Transaction tx) { Keccak256 hash = tx.getHash(); logger.trace("add transaction {} {}", toBI(tx.getNonce()), tx.getHash()); - Optional replacedTx = pendingTransactions.getTransactionsWithSender(tx.getSender()).stream().filter(t -> t.getNonceAsInteger().equals(tx.getNonceAsInteger())).findFirst(); + Optional replacedTx = pendingTransactions.getTransactionsWithSender(tx.getSender(signatureCache)).stream().filter(t -> t.getNonceAsInteger().equals(tx.getNonceAsInteger())).findFirst(); if (replacedTx.isPresent() && !isBumpingGasPriceForSameNonceTx(tx, replacedTx.get())) { return TransactionPoolAddResult.withError("gas price not enough to bump transaction"); } @@ -253,7 +255,7 @@ private TransactionPoolAddResult internalAddTransaction(final Transaction tx) { final long timestampSeconds = this.getCurrentTimeInSeconds(); transactionTimes.put(hash, timestampSeconds); - BigInteger currentNonce = getPendingState(currentRepository).getNonce(tx.getSender()); + BigInteger currentNonce = getPendingState(currentRepository).getNonce(tx.getSender(signatureCache)); BigInteger txNonce = tx.getNonceAsInteger(); if (txNonce.compareTo(currentNonce) > 0) { this.addQueuedTransaction(tx); @@ -454,8 +456,7 @@ private Block createFakePendingBlock(Block best) { } private TransactionValidationResult shouldAcceptTx(Transaction tx, RepositorySnapshot currentRepository) { - AccountState state = currentRepository.getAccountState(tx.getSender(signatureCache)); - return validator.isValid(tx, bestBlock, state); + return validator.isValid(tx, bestBlock, currentRepository.getAccountState(tx.getSender(signatureCache))); } /** @@ -464,7 +465,7 @@ private TransactionValidationResult shouldAcceptTx(Transaction tx, RepositorySna * @return whether the sender balance is enough to pay for all pending transactions + newTx */ private boolean senderCanPayPendingTransactionsAndNewTx(Transaction newTx, RepositorySnapshot currentRepository) { - List transactions = pendingTransactions.getTransactionsWithSender(newTx.getSender()); + List transactions = pendingTransactions.getTransactionsWithSender(newTx.getSender(signatureCache)); Coin accumTxCost = Coin.ZERO; for (Transaction t : transactions) { @@ -472,7 +473,7 @@ private boolean senderCanPayPendingTransactionsAndNewTx(Transaction newTx, Repos } Coin costWithNewTx = accumTxCost.add(getTxBaseCost(newTx)); - return costWithNewTx.compareTo(currentRepository.getBalance(newTx.getSender())) <= 0; + return costWithNewTx.compareTo(currentRepository.getBalance(newTx.getSender(signatureCache))) <= 0; } private Coin getTxBaseCost(Transaction tx) { @@ -486,7 +487,7 @@ private Coin getTxBaseCost(Transaction tx) { } private long getTransactionCost(Transaction tx, long number) { - return tx.transactionCost(config.getNetworkConstants(), config.getActivationConfig().forBlock(number)); + return tx.transactionCost(config.getNetworkConstants(), config.getActivationConfig().forBlock(number), signatureCache); } } diff --git a/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java b/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java index d887562334a..42b895dd20c 100644 --- a/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java +++ b/rskj-core/src/main/java/co/rsk/mine/BlockToMineBuilder.java @@ -71,6 +71,8 @@ public class BlockToMineBuilder { private final ForkDetectionDataCalculator forkDetectionDataCalculator; + private final SignatureCache signatureCache; + public BlockToMineBuilder( ActivationConfig activationConfig, MiningConfig miningConfig, @@ -85,7 +87,8 @@ public BlockToMineBuilder( BlockFactory blockFactory, BlockExecutor blockExecutor, MinimumGasPriceCalculator minimumGasPriceCalculator, - MinerUtils minerUtils) { + MinerUtils minerUtils, + SignatureCache signatureCache) { this.activationConfig = Objects.requireNonNull(activationConfig); this.miningConfig = Objects.requireNonNull(miningConfig); this.repositoryLocator = Objects.requireNonNull(repositoryLocator); @@ -100,6 +103,7 @@ public BlockToMineBuilder( this.executor = blockExecutor; this.minimumGasPriceCalculator = minimumGasPriceCalculator; this.minerUtils = minerUtils; + this.signatureCache = signatureCache; } /** @@ -157,7 +161,7 @@ private List getUnclesHeaders(BlockHeader newBlockParentHeader) { private List getTransactions(List txsToRemove, BlockHeader parentHeader, Coin minGasPrice) { logger.debug("getting transactions from pending state"); - List txs = minerUtils.getAllTransactions(transactionPool); + List txs = minerUtils.getAllTransactions(transactionPool, signatureCache); logger.debug("{} transaction(s) collected from pending state", txs.size()); final long blockNumber = parentHeader.getNumber() + 1; @@ -170,7 +174,7 @@ private List getTransactions(List txsToRemove, BlockHe final boolean isRskip252Enabled = activationConfig.isActive(ConsensusRule.RSKIP252, blockNumber); - return minerUtils.filterTransactions(txsToRemove, txs, accountNonces, originalRepo, minGasPrice, isRskip252Enabled); + return minerUtils.filterTransactions(txsToRemove, txs, accountNonces, originalRepo, minGasPrice, isRskip252Enabled, signatureCache); } private void removePendingTransactions(List transactions) { diff --git a/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java b/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java index ca2248f8b54..30f7fd0be8e 100644 --- a/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java +++ b/rskj-core/src/main/java/co/rsk/mine/MinerUtils.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.mine; import co.rsk.bitcoinj.core.BtcTransaction; @@ -32,6 +31,7 @@ import org.bouncycastle.util.Arrays; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; +import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.core.TransactionPool; import org.slf4j.Logger; @@ -48,7 +48,6 @@ import java.util.function.Function; public class MinerUtils { - private static final Logger logger = LoggerFactory.getLogger("minerserver"); public static co.rsk.bitcoinj.core.BtcTransaction getBitcoinMergedMiningCoinbaseTransaction(co.rsk.bitcoinj.core.NetworkParameters params, MinerWork work) { @@ -163,21 +162,21 @@ public static byte[] buildMerkleProof( } } - public List getAllTransactions(TransactionPool transactionPool) { + public List getAllTransactions(TransactionPool transactionPool, SignatureCache signatureCache) { List txs = transactionPool.getPendingTransactions(); - return PendingState.sortByPriceTakingIntoAccountSenderAndNonce(txs); + return PendingState.sortByPriceTakingIntoAccountSenderAndNonce(txs, signatureCache); } - public List filterTransactions(List txsToRemove, List txs, Map accountNonces, RepositorySnapshot originalRepo, Coin minGasPrice, boolean isRskip252Enabled) { + public List filterTransactions(List txsToRemove, List txs, Map accountNonces, RepositorySnapshot originalRepo, Coin minGasPrice, boolean isRskip252Enabled, SignatureCache signatureCache) { List txsResult = new ArrayList<>(); for (org.ethereum.core.Transaction tx : txs) { try { Keccak256 hash = tx.getHash(); Coin txValue = tx.getValue(); BigInteger txNonce = new BigInteger(1, tx.getNonce()); - RskAddress txSender = tx.getSender(); + RskAddress txSender = tx.getSender(signatureCache); logger.debug("Examining tx={} sender: {} value: {} nonce: {}", hash, txSender, txValue, txNonce); BigInteger expectedNonce; diff --git a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java index c01e9b8bb4f..74ab6e38636 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/TxPendingValidator.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.net.handler; import co.rsk.core.Coin; @@ -26,6 +25,7 @@ import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.core.AccountState; import org.ethereum.core.Block; +import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,9 +50,12 @@ public class TxPendingValidator { private final Constants constants; private final ActivationConfig activationConfig; - public TxPendingValidator(Constants constants, ActivationConfig activationConfig, int accountSlots) { + private final SignatureCache signatureCache; + + public TxPendingValidator(Constants constants, ActivationConfig activationConfig, int accountSlots, SignatureCache signatureCache) { this.constants = constants; this.activationConfig = activationConfig; + this.signatureCache = signatureCache; validatorSteps.add(new TxNotNullValidator()); validatorSteps.add(new TxValidatorNotRemascTxValidator()); @@ -61,7 +64,7 @@ public TxPendingValidator(Constants constants, ActivationConfig activationConfig validatorSteps.add(new TxValidatorNonceRangeValidator(accountSlots)); validatorSteps.add(new TxValidatorAccountBalanceValidator()); validatorSteps.add(new TxValidatorMinimuGasPriceValidator()); - validatorSteps.add(new TxValidatorIntrinsicGasLimitValidator(constants, activationConfig)); + validatorSteps.add(new TxValidatorIntrinsicGasLimitValidator(constants, activationConfig, signatureCache)); validatorSteps.add(new TxValidatorMaximumGasPriceValidator(activationConfig)); } @@ -69,10 +72,12 @@ public TransactionValidationResult isValid(Transaction tx, Block executionBlock, BigInteger blockGasLimit = BigIntegers.fromUnsignedByteArray(executionBlock.getGasLimit()); Coin minimumGasPrice = executionBlock.getMinimumGasPrice(); long bestBlockNumber = executionBlock.getNumber(); - long basicTxCost = tx.transactionCost(constants, activationConfig.forBlock(bestBlockNumber)); + long basicTxCost = tx.transactionCost(constants, activationConfig.forBlock(bestBlockNumber), signatureCache); if (state == null && basicTxCost != 0) { - logger.trace("[tx={}, sender={}] account doesn't exist", tx.getHash(), tx.getSender()); + if (logger.isTraceEnabled()) { + logger.trace("[tx={}, sender={}] account doesn't exist", tx.getHash(), tx.getSender(signatureCache)); + } return TransactionValidationResult.withError("the sender account doesn't exist"); } diff --git a/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuotaChecker.java b/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuotaChecker.java index 581c3791587..bff9690850d 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuotaChecker.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/quota/TxQuotaChecker.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.net.handler.quota; import co.rsk.core.RskAddress; @@ -24,6 +23,7 @@ import co.rsk.util.MaxSizeHashMap; import co.rsk.util.TimeProvider; import org.ethereum.core.Block; +import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.listener.GasPriceTracker; import org.slf4j.Logger; @@ -55,10 +55,13 @@ public class TxQuotaChecker { private final TimeProvider timeProvider; - public TxQuotaChecker(TimeProvider timeProvider) { + private final SignatureCache signatureCache; + + public TxQuotaChecker(TimeProvider timeProvider, SignatureCache signatureCache) { this.accountQuotas = new MaxSizeHashMap<>(MAX_QUOTAS_SIZE, true); this.timeProvider = timeProvider; this.lastBlockGasLimit = UNKNOWN_LAST_BLOCK_GAS_LIMIT; + this.signatureCache = signatureCache; } /** @@ -118,7 +121,7 @@ private void updateReceiverQuotaIfRequired(Transaction newTx, CurrentContext cur } private boolean isFirstTxFromSender(Transaction newTx, CurrentContext currentContext) { - RskAddress senderAddress = newTx.getSender(); + RskAddress senderAddress = newTx.getSender(signatureCache); TxQuota quotaForSender = this.accountQuotas.get(senderAddress); long accountNonce = currentContext.state.getNonce(senderAddress).longValue(); @@ -176,7 +179,7 @@ private TxQuota updateQuota(Transaction newTx, boolean isTxSource, CurrentContex long maxGasPerSecond = getMaxGasPerSecond(blockGasLimit.longValue()); long maxQuota = getMaxQuota(maxGasPerSecond); - RskAddress address = isTxSource ? newTx.getSender() : newTx.getReceiveAddress(); + RskAddress address = isTxSource ? newTx.getSender(signatureCache) : newTx.getReceiveAddress(); TxQuota quotaForAddress = this.accountQuotas.get(address); if (quotaForAddress == null) { @@ -206,7 +209,7 @@ private long getMaxQuota(long maxGasPerSecond) { } private double calculateConsumedVirtualGas(Transaction newTx, @Nullable Transaction replacedTx, CurrentContext currentContext) { - long accountNonce = currentContext.state.getNonce(newTx.getSender()).longValue(); + long accountNonce = currentContext.state.getNonce(newTx.getSender(signatureCache)).longValue(); long blockGasLimit = currentContext.bestBlock.getGasLimitAsInteger().longValue(); long blockMinGasPrice = currentContext.bestBlock.getMinimumGasPrice().asBigInteger().longValue(); diff --git a/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java b/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java index eab810cc340..6ac70394496 100644 --- a/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java +++ b/rskj-core/src/main/java/co/rsk/net/handler/txvalidator/TxValidatorIntrinsicGasLimitValidator.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.net.handler.txvalidator; import co.rsk.core.Coin; @@ -35,15 +34,20 @@ public class TxValidatorIntrinsicGasLimitValidator implements TxValidatorStep { private final Constants constants; private final ActivationConfig activationConfig; + private final SignatureCache signatureCache; - public TxValidatorIntrinsicGasLimitValidator(Constants constants, ActivationConfig activationConfig) { + public TxValidatorIntrinsicGasLimitValidator( + Constants constants, + ActivationConfig activationConfig, + SignatureCache signatureCache) { this.constants = constants; this.activationConfig = activationConfig; + this.signatureCache = signatureCache; } @Override public TransactionValidationResult validate(Transaction tx, @Nullable AccountState state, BigInteger gasLimit, Coin minimumGasPrice, long bestBlockNumber, boolean isFreeTx) { - if (BigInteger.valueOf(tx.transactionCost(constants, activationConfig.forBlock(bestBlockNumber))).compareTo(tx.getGasLimitAsInteger()) <= 0) { + if (BigInteger.valueOf(tx.transactionCost(constants, activationConfig.forBlock(bestBlockNumber), signatureCache)).compareTo(tx.getGasLimitAsInteger()) <= 0) { return TransactionValidationResult.ok(); } diff --git a/rskj-core/src/main/java/co/rsk/peg/AddressBasedAuthorizer.java b/rskj-core/src/main/java/co/rsk/peg/AddressBasedAuthorizer.java index 4d6e537c14e..3ef1101b0ce 100644 --- a/rskj-core/src/main/java/co/rsk/peg/AddressBasedAuthorizer.java +++ b/rskj-core/src/main/java/co/rsk/peg/AddressBasedAuthorizer.java @@ -15,10 +15,10 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.peg; import co.rsk.core.RskAddress; +import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.crypto.ECKey; @@ -48,8 +48,8 @@ public boolean isAuthorized(RskAddress sender) { .anyMatch(address -> Arrays.equals(address, sender.getBytes())); } - public boolean isAuthorized(Transaction tx) { - return isAuthorized(tx.getSender()); + public boolean isAuthorized(Transaction tx, SignatureCache signatureCache) { + return isAuthorized(tx.getSender(signatureCache)); } public int getNumberOfAuthorizedKeys() { diff --git a/rskj-core/src/main/java/co/rsk/peg/Bridge.java b/rskj-core/src/main/java/co/rsk/peg/Bridge.java index ba7cd5003fa..26cfa216238 100644 --- a/rskj-core/src/main/java/co/rsk/peg/Bridge.java +++ b/rskj-core/src/main/java/co/rsk/peg/Bridge.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.peg; import co.rsk.bitcoinj.core.*; @@ -35,10 +34,7 @@ import org.ethereum.config.Constants; import org.ethereum.config.blockchain.upgrades.ActivationConfig; import org.ethereum.config.blockchain.upgrades.ConsensusRule; -import org.ethereum.core.Block; -import org.ethereum.core.CallTransaction; -import org.ethereum.core.Repository; -import org.ethereum.core.Transaction; +import org.ethereum.core.*; import org.ethereum.db.BlockStore; import org.ethereum.db.ReceiptStore; import org.ethereum.util.ByteUtil; @@ -228,20 +224,23 @@ public class Bridge extends PrecompiledContracts.PrecompiledContract { private final BiFunction, Integer, MerkleBranch> merkleBranchFactory; + private final SignatureCache signatureCache; + public Bridge(RskAddress contractAddress, Constants constants, ActivationConfig activationConfig, - BridgeSupportFactory bridgeSupportFactory) { - this(contractAddress, constants, activationConfig, bridgeSupportFactory, MerkleBranch::new); + BridgeSupportFactory bridgeSupportFactory, SignatureCache signatureCache) { + this(contractAddress, constants, activationConfig, bridgeSupportFactory, MerkleBranch::new, signatureCache); } @VisibleForTesting Bridge(RskAddress contractAddress, Constants constants, ActivationConfig activationConfig, - BridgeSupportFactory bridgeSupportFactory, BiFunction, Integer, MerkleBranch> merkleBranchFactory) { + BridgeSupportFactory bridgeSupportFactory, BiFunction, Integer, MerkleBranch> merkleBranchFactory, SignatureCache signatureCache) { this.bridgeSupportFactory = bridgeSupportFactory; this.contractAddress = contractAddress; this.constants = constants; this.bridgeConstants = constants.getBridgeConstants(); this.activationConfig = activationConfig; this.merkleBranchFactory = merkleBranchFactory; + this.signatureCache = signatureCache; } @Override @@ -251,7 +250,7 @@ public long getGasForData(byte[] data) { throw new NullPointerException(); } - if (BridgeUtils.isFreeBridgeTx(rskTx, constants, activations)) { + if (BridgeUtils.isFreeBridgeTx(rskTx, constants, activations, signatureCache)) { return 0; } @@ -1235,8 +1234,8 @@ public static BridgeMethods.BridgeMethodExecutor activeAndRetiringFederationOnly return (self, args) -> { Federation retiringFederation = self.bridgeSupport.getRetiringFederation(); - if (!BridgeUtils.isFromFederateMember(self.rskTx, self.bridgeSupport.getActiveFederation()) - && (retiringFederation == null || !BridgeUtils.isFromFederateMember(self.rskTx, retiringFederation))) { + if (!BridgeUtils.isFromFederateMember(self.rskTx, self.bridgeSupport.getActiveFederation(), self.signatureCache) + && (retiringFederation == null || !BridgeUtils.isFromFederateMember(self.rskTx, retiringFederation, self.signatureCache))) { String errorMessage = String.format("Sender is not part of the active or retiring federations, so he is not enabled to call the function '%s'",funcName); logger.warn(errorMessage); throw new VMException(errorMessage); diff --git a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java index 71ef75c2e3d..f4b8e20aec2 100644 --- a/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java +++ b/rskj-core/src/main/java/co/rsk/peg/BridgeSupport.java @@ -15,7 +15,6 @@ * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ - package co.rsk.peg; import co.rsk.bitcoinj.core.Address; @@ -87,6 +86,7 @@ import org.ethereum.config.blockchain.upgrades.ConsensusRule; import org.ethereum.core.Block; import org.ethereum.core.Repository; +import org.ethereum.core.SignatureCache; import org.ethereum.core.Transaction; import org.ethereum.crypto.ECKey; import org.ethereum.crypto.HashUtil; @@ -172,6 +172,8 @@ public class BridgeSupport { private final org.ethereum.core.Block rskExecutionBlock; private final ActivationConfig.ForBlock activations; + private final SignatureCache signatureCache; + protected enum TxType { PEGIN, PEGOUT, @@ -190,7 +192,8 @@ public BridgeSupport( Context btcContext, FederationSupport federationSupport, BtcBlockStoreWithCache.Factory btcBlockStoreFactory, - ActivationConfig.ForBlock activations) { + ActivationConfig.ForBlock activations, + SignatureCache signatureCache) { this.rskRepository = repository; this.provider = provider; this.rskExecutionBlock = executionBlock; @@ -202,6 +205,7 @@ public BridgeSupport( this.federationSupport = federationSupport; this.btcBlockStoreFactory = btcBlockStoreFactory; this.activations = activations; + this.signatureCache = signatureCache; } public List getSubtraces() { @@ -774,7 +778,7 @@ private void saveNewUTXOs(BtcTransaction btcTx) throws IOException { */ public void releaseBtc(Transaction rskTx) throws IOException { Coin value = rskTx.getValue().toBitcoin(); - final RskAddress senderAddress = rskTx.getSender(); + final RskAddress senderAddress = rskTx.getSender(signatureCache); //as we can't send btc from contracts we want to send them back to the senderAddressStr if (BridgeUtils.isContractTx(rskTx)) { logger.trace("Contract {} tried to release funds. Release is just allowed from standard accounts.", rskTx); @@ -864,7 +868,7 @@ private void requestRelease(Address destinationAddress, Coin value, Transaction if (activations.isActive(ConsensusRule.RSKIP185)) { refundAndEmitRejectEvent( value, - rskTx.getSender(), + rskTx.getSender(signatureCache), optionalRejectedPegoutReason.get() ); } @@ -876,7 +880,7 @@ private void requestRelease(Address destinationAddress, Coin value, Transaction } if (activations.isActive(ConsensusRule.RSKIP185)) { - eventLogger.logReleaseBtcRequestReceived(rskTx.getSender().toHexString(), destinationAddress, value); + eventLogger.logReleaseBtcRequestReceived(rskTx.getSender(signatureCache).toHexString(), destinationAddress, value); } logger.info("releaseBtc successful to {}. Tx {}. Value {}.", destinationAddress, rskTx, value); } @@ -2104,7 +2108,7 @@ public Integer voteFederationChange(Transaction tx, ABICallSpec callSpec) throws AddressBasedAuthorizer authorizer = bridgeConstants.getFederationChangeAuthorizer(); // Must be authorized to vote (checking for signature) - if (!authorizer.isAuthorized(tx)) { + if (!authorizer.isAuthorized(tx, signatureCache)) { return FEDERATION_CHANGE_GENERIC_ERROR_CODE; } @@ -2124,7 +2128,7 @@ public Integer voteFederationChange(Transaction tx, ABICallSpec callSpec) throws ABICallElection election = provider.getFederationElection(authorizer); // Register the vote. It is expected to succeed, since all previous checks succeeded - if (!election.vote(callSpec, tx.getSender())) { + if (!election.vote(callSpec, tx.getSender(signatureCache))) { logger.warn("Unexpected federation change vote failure"); return FEDERATION_CHANGE_GENERIC_ERROR_CODE; } @@ -2369,7 +2373,7 @@ private Integer addLockWhitelistAddress(Transaction tx, LockWhitelistEntry entry private boolean isLockWhitelistChangeAuthorized(Transaction tx) { AddressBasedAuthorizer authorizer = bridgeConstants.getLockWhitelistChangeAuthorizer(); - return authorizer.isAuthorized(tx); + return authorizer.isAuthorized(tx, signatureCache); } /** @@ -2421,7 +2425,7 @@ public Coin getMinimumPeginTxValue() { */ public Integer voteFeePerKbChange(Transaction tx, Coin feePerKb) { AddressBasedAuthorizer authorizer = bridgeConstants.getFeePerKbChangeAuthorizer(); - if (!authorizer.isAuthorized(tx)) { + if (!authorizer.isAuthorized(tx, signatureCache)) { return FEE_PER_KB_GENERIC_ERROR_CODE; } @@ -2435,7 +2439,7 @@ public Integer voteFeePerKbChange(Transaction tx, Coin feePerKb) { ABICallElection feePerKbElection = provider.getFeePerKbElection(authorizer); ABICallSpec feeVote = new ABICallSpec("setFeePerKb", new byte[][]{BridgeSerializationUtils.serializeCoin(feePerKb)}); - boolean successfulVote = feePerKbElection.vote(feeVote, tx.getSender()); + boolean successfulVote = feePerKbElection.vote(feeVote, tx.getSender(signatureCache)); if (!successfulVote) { return -1; } @@ -2517,8 +2521,8 @@ public Optional