From 01fcc7fcfb7b0a45ce73c02c537fa34d060ad44b Mon Sep 17 00:00:00 2001 From: daniellehrner Date: Tue, 10 Sep 2024 11:37:47 +0200 Subject: [PATCH] Fix tx validation if sender has delegated it's code via 7702 transaction (#7593) Signed-off-by: Daniel Lehrner --- ...deDelegationTransactionAcceptanceTest.java | 56 +++++++++++++++---- .../mainnet/MainnetTransactionValidator.java | 4 +- .../evm/worldstate/DelegatedCodeService.java | 16 ++++-- 3 files changed, 58 insertions(+), 18 deletions(-) diff --git a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/CodeDelegationTransactionAcceptanceTest.java b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/CodeDelegationTransactionAcceptanceTest.java index 6000915b441..ee896ccd278 100644 --- a/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/CodeDelegationTransactionAcceptanceTest.java +++ b/acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/CodeDelegationTransactionAcceptanceTest.java @@ -175,29 +175,61 @@ public void shouldCheckNonceAfterNonceIncreaseOfSender() throws IOException { besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString())); testHelper.buildNewBlock(); - Optional maybeTransactionReceipt = + final Optional maybeFirstTransactionReceipt = besuNode.execute(ethTransactions.getTransactionReceipt(txHash)); - assertThat(maybeTransactionReceipt).isPresent(); + assertThat(maybeFirstTransactionReceipt).isPresent(); final String gasPriceWithout0x = - maybeTransactionReceipt.get().getEffectiveGasPrice().substring(2); + maybeFirstTransactionReceipt.get().getEffectiveGasPrice().substring(2); final BigInteger gasPrice = new BigInteger(gasPriceWithout0x, 16); - final BigInteger txCost = maybeTransactionReceipt.get().getGasUsed().multiply(gasPrice); + final BigInteger txCost = maybeFirstTransactionReceipt.get().getGasUsed().multiply(gasPrice); - final BigInteger authorizerBalance = besuNode.execute(ethTransactions.getBalance(authorizer)); + final BigInteger authorizerBalanceAfterFirstTx = + besuNode.execute(ethTransactions.getBalance(authorizer)); // The remaining balance of the authorizer should the gas limit multiplied by the gas price // minus the transaction cost. // The following executes this calculation in reverse. - assertThat(GAS_LIMIT).isEqualTo(authorizerBalance.add(txCost).divide(gasPrice).longValue()); + assertThat(GAS_LIMIT) + .isEqualTo(authorizerBalanceAfterFirstTx.add(txCost).divide(gasPrice).longValue()); // The other accounts balance should be the initial 9000 ETH balance from the authorizer minus // the remaining balance of the authorizer and minus the transaction cost - cluster.verify( - otherAccount.balanceEquals( - Amount.wei( - new BigInteger("90000000000000000000000") - .subtract(authorizerBalance) - .subtract(txCost)))); + final BigInteger otherAccountBalanceAfterFirstTx = + new BigInteger("90000000000000000000000") + .subtract(authorizerBalanceAfterFirstTx) + .subtract(txCost); + + cluster.verify(otherAccount.balanceEquals(Amount.wei(otherAccountBalanceAfterFirstTx))); + + final Transaction txSendEthToOtherAccount = + Transaction.builder() + .type(TransactionType.EIP1559) + .chainId(BigInteger.valueOf(20211)) + .nonce(2) + .maxPriorityFeePerGas(Wei.of(10)) + .maxFeePerGas(Wei.of(100)) + .gasLimit(21000) + .to(Address.fromHexStringStrict(otherAccount.getAddress())) + .value(Wei.ONE) + .payload(Bytes.EMPTY) + .signAndBuild( + secp256k1.createKeyPair( + secp256k1.createPrivateKey(AUTHORIZER_PRIVATE_KEY.toUnsignedBigInteger()))); + + final String txSendEthToOtherAccountHash = + besuNode.execute( + ethTransactions.sendRawTransaction(txSendEthToOtherAccount.encoded().toHexString())); + testHelper.buildNewBlock(); + + final Optional maybeSecondTransactionReceipt = + besuNode.execute(ethTransactions.getTransactionReceipt(txSendEthToOtherAccountHash)); + assertThat(maybeSecondTransactionReceipt).isPresent(); + + // the balance of the other account should be the previous balance plus the value of the 1 Wei + final BigInteger otherAccountBalanceAfterSecondTx = + besuNode.execute(ethTransactions.getBalance(otherAccount)); + assertThat(otherAccountBalanceAfterFirstTx.add(BigInteger.ONE)) + .isEqualTo(otherAccountBalanceAfterSecondTx); } } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java index 4f4c554f3ff..b5fbf7340b2 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java @@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason; import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.gascalculator.GasCalculator; +import org.hyperledger.besu.evm.worldstate.DelegatedCodeService; import java.math.BigInteger; import java.util.List; @@ -305,7 +306,8 @@ public ValidationResult validateForSender( } private static boolean canSendTransaction(final Account sender, final Hash codeHash) { - return codeHash.equals(Hash.EMPTY) || sender.hasDelegatedCode(); + return codeHash.equals(Hash.EMPTY) + || DelegatedCodeService.hasDelegatedCode(sender.getUnprocessedCode()); } private ValidationResult validateTransactionSignature( diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java index 1c89fad8bcf..885324b10b6 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java @@ -85,13 +85,19 @@ public MutableAccount processMutableAccount( worldUpdater, account, resolveDelegatedAddress(account.getCode())); } - private Address resolveDelegatedAddress(final Bytes code) { - return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size())); - } - - private boolean hasDelegatedCode(final Bytes code) { + /** + * Returns if the provided code is delegated code. + * + * @param code the code to check. + * @return {@code true} if the code is delegated code, {@code false} otherwise. + */ + public static boolean hasDelegatedCode(final Bytes code) { return code != null && code.size() == DELEGATED_CODE_SIZE && code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX); } + + private Address resolveDelegatedAddress(final Bytes code) { + return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size())); + } }