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. 56
      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())); besuNode.execute(ethTransactions.sendRawTransaction(tx.encoded().toHexString()));
testHelper.buildNewBlock(); testHelper.buildNewBlock();
Optional<TransactionReceipt> maybeTransactionReceipt = final Optional<TransactionReceipt> maybeFirstTransactionReceipt =
besuNode.execute(ethTransactions.getTransactionReceipt(txHash)); besuNode.execute(ethTransactions.getTransactionReceipt(txHash));
assertThat(maybeTransactionReceipt).isPresent(); assertThat(maybeFirstTransactionReceipt).isPresent();
final String gasPriceWithout0x = final String gasPriceWithout0x =
maybeTransactionReceipt.get().getEffectiveGasPrice().substring(2); maybeFirstTransactionReceipt.get().getEffectiveGasPrice().substring(2);
final BigInteger gasPrice = new BigInteger(gasPriceWithout0x, 16); 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 // The remaining balance of the authorizer should the gas limit multiplied by the gas price
// minus the transaction cost. // minus the transaction cost.
// The following executes this calculation in reverse. // 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 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 // the remaining balance of the authorizer and minus the transaction cost
cluster.verify( final BigInteger otherAccountBalanceAfterFirstTx =
otherAccount.balanceEquals( new BigInteger("90000000000000000000000")
Amount.wei( .subtract(authorizerBalanceAfterFirstTx)
new BigInteger("90000000000000000000000") .subtract(txCost);
.subtract(authorizerBalance)
.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.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.account.Account; import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.gascalculator.GasCalculator; import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.worldstate.DelegatedCodeService;
import java.math.BigInteger; import java.math.BigInteger;
import java.util.List; import java.util.List;
@ -305,7 +306,8 @@ public class MainnetTransactionValidator implements TransactionValidator {
} }
private static boolean canSendTransaction(final Account sender, final Hash codeHash) { 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( private ValidationResult<TransactionInvalidReason> validateTransactionSignature(

@ -85,13 +85,19 @@ public class DelegatedCodeService {
worldUpdater, account, resolveDelegatedAddress(account.getCode())); worldUpdater, account, resolveDelegatedAddress(account.getCode()));
} }
private Address resolveDelegatedAddress(final Bytes code) { /**
return Address.wrap(code.slice(DELEGATED_CODE_PREFIX.size())); * Returns if the provided code is delegated code.
} *
* @param code the code to check.
private boolean hasDelegatedCode(final Bytes code) { * @return {@code true} if the code is delegated code, {@code false} otherwise.
*/
public static boolean hasDelegatedCode(final Bytes code) {
return code != null return code != null
&& code.size() == DELEGATED_CODE_SIZE && code.size() == DELEGATED_CODE_SIZE
&& code.slice(0, DELEGATED_CODE_PREFIX.size()).equals(DELEGATED_CODE_PREFIX); && 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