Fix tx validation if sender has delegated it's code via 7702 transaction (#7593)

Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
pull/7599/head
daniellehrner 2 months ago committed by GitHub
parent 36454b40d5
commit 01fcc7fcfb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 54
      acceptance-tests/tests/src/test/java/org/hyperledger/besu/tests/acceptance/ethereum/CodeDelegationTransactionAcceptanceTest.java
  2. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  3. 16
      evm/src/main/java/org/hyperledger/besu/evm/worldstate/DelegatedCodeService.java

@ -175,29 +175,61 @@ public class CodeDelegationTransactionAcceptanceTest extends AcceptanceTestBase
besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testHelper.buildNewBlock();
Optional<TransactionReceipt> maybeTransactionReceipt =
final Optional<TransactionReceipt> 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(
final BigInteger otherAccountBalanceAfterFirstTx =
new BigInteger("90000000000000000000000")
.subtract(authorizerBalance)
.subtract(txCost))));
.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<TransactionReceipt> 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);
}
}

@ -32,6 +32,7 @@ import org.hyperledger.besu.ethereum.mainnet.feemarket.FeeMarket;
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 class MainnetTransactionValidator implements TransactionValidator {
}
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<TransactionInvalidReason> validateTransactionSignature(

@ -85,13 +85,19 @@ public class DelegatedCodeService {
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()));
}
}

Loading…
Cancel
Save