diff --git a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java index 4704a790f08..63c9fded9e1 100644 --- a/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java +++ b/datatypes/src/main/java/org/hyperledger/besu/datatypes/Transaction.java @@ -236,5 +236,8 @@ default Optional getMaxFeePerBlobGas() { int getSize(); /** Returns the set code transaction payload if this transaction is a 7702 transaction. */ - Optional> getSetCodeTransactionPayloads(); + Optional> setCodeTransactionPayloads(); + + /** Returns the size of the set code transaction payload list. */ + int setCodeTransactionPayloadSize(); } diff --git a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java index 3341940f3c3..ac02e075348 100644 --- a/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java +++ b/ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/TransactionCompleteResult.java @@ -138,7 +138,7 @@ public TransactionCompleteResult(final TransactionWithMetadata tx) { this.r = Quantity.create(transaction.getR()); this.s = Quantity.create(transaction.getS()); this.versionedHashes = transaction.getVersionedHashes().orElse(null); - this.setCodeAuthorizationList = transaction.getSetCodeTransactionPayloads().orElse(null); + this.setCodeAuthorizationList = transaction.setCodeTransactionPayloads().orElse(null); } @JsonGetter(value = "accessList") diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java index 70c132b9634..00def46565f 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java @@ -681,10 +681,15 @@ public Optional getBlobsWithCommitments() { } @Override - public Optional> getSetCodeTransactionPayloads() { + public Optional> setCodeTransactionPayloads() { return maybeSetCodeTransactionPayloads; } + @Override + public int setCodeTransactionPayloadSize() { + return maybeSetCodeTransactionPayloads.map(List::size).orElse(0); + } + /** * Return the list of transaction hashes extracted from the collection of Transaction passed as * argument diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java index 005926f6b3b..39818529109 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoder.java @@ -90,7 +90,8 @@ public static Transaction decode(final RLPInput input) { return transaction; } - private static SetCodeAuthorization decodeInnerPayload(final RLPInput input) { + public static SetCodeAuthorization decodeInnerPayload(final RLPInput input) { + input.enterList(); final BigInteger chainId = input.readBigIntegerScalar(); final Address address = Address.wrap(input.readBytes()); @@ -99,19 +100,29 @@ private static SetCodeAuthorization decodeInnerPayload(final RLPInput input) { throw new IllegalArgumentException("Optional nonce must be an empty list, but isn't"); } + if (!input.nextIsList()) { + throw new IllegalArgumentException("Optional nonce must be an list, but isn't"); + } + + final long noncesSize = input.nextSize(); + input.enterList(); - while (input.nextSize() != 0) { + for (int i = 0; i < noncesSize; i++) { nonces.add(input.readLongScalar()); } input.leaveList(); + final byte yParity = (byte) input.readUnsignedByteScalar(); + final SECPSignature signature = SIGNATURE_ALGORITHM .get() .createSignature( input.readUInt256Scalar().toUnsignedBigInteger(), input.readUInt256Scalar().toUnsignedBigInteger(), - (byte) input.readUnsignedByteScalar()); + yParity); + + input.leaveList(); return new SetCodeAuthorization(chainId, address, nonces, signature); } diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java index 23a5eea8878..671a4a3ea1c 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoder.java @@ -65,7 +65,7 @@ public static void encode(final Transaction transaction, final RLPOutput out) { writeAccessList(out, transaction.getAccessList()); encodeSetCodeInner( transaction - .getSetCodeTransactionPayloads() + .setCodeTransactionPayloads() .orElseThrow( () -> new IllegalStateException( diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java index c12ac1f0930..2e10de8c9e0 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessor.java @@ -45,6 +45,7 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater; import java.util.Deque; +import java.util.HashSet; import java.util.List; import java.util.Optional; import java.util.Set; @@ -264,6 +265,7 @@ public TransactionProcessingResult processTransaction( final Wei blobGasPrice) { try { final var transactionValidator = transactionValidatorFactory.get(); + final Set
setCodeAuthorities = new HashSet<>(); LOG.trace("Starting execution of {}", transaction); ValidationResult validationResult = transactionValidator.validate( @@ -336,13 +338,17 @@ public TransactionProcessingResult processTransaction( transaction.getPayload(), transaction.isContractCreation()); final long accessListGas = gasCalculator.accessListGasCost(accessListEntries.size(), accessListStorageCount); - final long gasAvailable = transaction.getGasLimit() - intrinsicGas - accessListGas; + final long setCodeGas = + gasCalculator.setCodeListGasCost(transaction.setCodeTransactionPayloadSize()); + final long gasAvailable = + transaction.getGasLimit() - intrinsicGas - accessListGas - setCodeGas; LOG.trace( - "Gas available for execution {} = {} - {} - {} (limit - intrinsic - accessList)", + "Gas available for execution {} = {} - {} - {} - {} (limit - intrinsic - accessList - setCode)", gasAvailable, transaction.getGasLimit(), intrinsicGas, - accessListGas); + accessListGas, + setCodeGas); final WorldUpdater worldUpdater = worldState.updater(); final ImmutableMap.Builder contextVariablesBuilder = @@ -378,9 +384,10 @@ public TransactionProcessingResult processTransaction( if (transaction.getVersionedHashes().isPresent()) { commonMessageFrameBuilder.versionedHashes( Optional.of(transaction.getVersionedHashes().get().stream().toList())); - } else if (transaction.getSetCodeTransactionPayloads().isPresent()) { - addressList.addAll( + } else if (transaction.setCodeTransactionPayloads().isPresent()) { + setCodeAuthorities.addAll( setCodeTransactionProcessor.addContractToAuthority(worldUpdater, transaction)); + addressList.addAll(setCodeAuthorities); } else { commonMessageFrameBuilder.versionedHashes(Optional.empty()); } @@ -492,6 +499,7 @@ public TransactionProcessingResult processTransaction( coinbaseCalculator.price(usedGas, transactionGasPrice, blockHeader.getBaseFee()); coinbase.incrementBalance(coinbaseWeiDelta); + setCodeTransactionProcessor.removeCodeFromAuthorities(worldUpdater, setCodeAuthorities); operationTracer.traceEndTransaction( worldUpdater, diff --git a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SetCodeTransactionProcessor.java b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SetCodeTransactionProcessor.java index 38ec065f456..e32ba2d09f9 100644 --- a/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SetCodeTransactionProcessor.java +++ b/ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/SetCodeTransactionProcessor.java @@ -51,7 +51,7 @@ public Set
addContractToAuthority( final Set
authorityList = new HashSet<>(); transaction - .getSetCodeTransactionPayloads() + .setCodeTransactionPayloads() .get() .forEach( payload -> { @@ -71,11 +71,8 @@ public Set
addContractToAuthority( return; } - if (!account.getCode().isEmpty()) { - return; - } - - account.setCode(worldUpdater.getAccount(payload.address()).getCode()); + worldUpdater.addCodeToEOA( + authorityAddress, worldUpdater.getAccount(payload.address()).getCode()); authorityList.add(authorityAddress); }); }); @@ -94,4 +91,9 @@ private Optional
recoverAuthority(final SetCodeAuthorization authorizat .recoverPublicKeyFromSignature(hash, authorization.signature()) .map(Address::extract); } + + public void removeCodeFromAuthorities( + final WorldUpdater worldUpdater, final Set
authorities) { + authorities.forEach(worldUpdater::removeCodeFromEOAs); + } } diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java new file mode 100644 index 00000000000..7ee014fd788 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionDecoderTest.java @@ -0,0 +1,129 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.crypto.SECPSignature; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPInput; + +import java.math.BigInteger; + +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.Test; + +class SetCodeTransactionDecoderTest { + + @Test + void shouldDecodeInnerPayloadWithNonce() { + // "0xd80194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c105" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonces().size()).isEqualTo(1); + assertThat(authorization.nonces().getFirst()).isEqualTo(0L); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 0); + assertThat(signature.getR().toString(16)) + .isEqualTo("840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5"); + assertThat(signature.getS().toString(16)) + .isEqualTo("3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99"); + } + + @Test + void shouldDecodeInnerPayloadWithoutNonce() { + // "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonces().size()).isEqualTo(0); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 1); + assertThat(signature.getR().toString(16)) + .isEqualTo("dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148"); + assertThat(signature.getS().toString(16)) + .isEqualTo("25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031"); + } + + @Test + void shouldDecodeInnerPayloadWithMultipleNonces() { + // "d90194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c20107" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85c0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c2010201a0401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135aa0753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ONE); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonces().size()).isEqualTo(2); + assertThat(authorization.nonces().getFirst()).isEqualTo(1L); + assertThat(authorization.nonces().get(1)).isEqualTo(2L); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 1); + assertThat(signature.getR().toString(16)) + .isEqualTo("401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135a"); + assertThat(signature.getS().toString(16)) + .isEqualTo("753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc"); + } + + @Test + void shouldDecodeInnerPayloadWithoutNonceAndChainIdZero() { + // "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final BytesValueRLPInput input = + new BytesValueRLPInput( + Bytes.fromHexString( + "0xf85a0094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"), + true); + final SetCodeAuthorization authorization = SetCodeTransactionDecoder.decodeInnerPayload(input); + + assertThat(authorization.chainId()).isEqualTo(BigInteger.ZERO); + assertThat(authorization.address()) + .isEqualTo(Address.fromHexStringStrict("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56")); + assertThat(authorization.nonces().isEmpty()).isTrue(); + + final SECPSignature signature = authorization.signature(); + assertThat(signature.getRecId()).isEqualTo((byte) 1); + assertThat(signature.getR().toString(16)) + .isEqualTo("25c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2"); + assertThat(signature.getS().toString(16)) + .isEqualTo("3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df"); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java new file mode 100644 index 00000000000..fe15db45966 --- /dev/null +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/SetCodeTransactionEncoderTest.java @@ -0,0 +1,149 @@ +/* + * Copyright contributors to Hyperledger Besu. + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on + * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the + * specific language governing permissions and limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package org.hyperledger.besu.ethereum.core.encoding; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; + +import org.hyperledger.besu.crypto.SignatureAlgorithm; +import org.hyperledger.besu.crypto.SignatureAlgorithmFactory; +import org.hyperledger.besu.datatypes.Address; +import org.hyperledger.besu.datatypes.SetCodeAuthorization; +import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Supplier; + +import com.google.common.base.Suppliers; +import org.apache.tuweni.bytes.Bytes; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class SetCodeTransactionEncoderTest { + private static final Supplier SIGNATURE_ALGORITHM = + Suppliers.memoize(SignatureAlgorithmFactory::getInstance); + + BytesValueRLPOutput output; + + @BeforeEach + void setUp() { + output = new BytesValueRLPOutput(); + } + + @Test + void shouldEncodeSingleSetCodeWithNonce() { + // "0xd80194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c105" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ONE, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + List.of(0L), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5", 16), + new BigInteger( + "3b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99", 16), + (byte) 0)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85b0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c18080a0840798fa67118e034c1eb7e42fe89e28d7cd5006dc813d5729e5f75b0d1a7ec5a03b1dbace38ceb862a65bf2eac0637693b5c3493bcb2a022dd614c0a74cce0b99")); + } + + @Test + void shouldEncodeSingleSetCodeWithoutNonce() { + // "0xd70194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ONE, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + new ArrayList<>(), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148", 16), + new BigInteger( + "25b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031", 16), + (byte) 1)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85a0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0dd6b24048be1b7d7fe5bbbb73ffc37eb2ce1997ecb4ae5b6096532ef19363148a025b58a1ff8ad00bddbbfa1d5c2411961cbb6d08dcdc8ae88303db3c6cf983031")); + } + + @Test + void shouldEncodeSingleSetCodeWithMultipleNonces() { + // "d90194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c20107" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ONE, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + List.of(1L, 2L), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135a", 16), + new BigInteger( + "753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc", 16), + (byte) 1)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85c0194633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c2010201a0401b5d4ebe88306448115d1a46a30e5ad1136f2818b4ebb0733d9c4efffd135aa0753ff1dbce6db504ecb9635a64d8c4506ff887e2d2a0d2b7175baf94c849eccc")); + } + + @Test + void shouldEncodeSingleSetCodeWithoutNonceAndChainIdZero() { + // "d70094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c5" + + final SetCodeAuthorization authorization = + new SetCodeAuthorization( + BigInteger.ZERO, + Address.fromHexString("0x633688abc3cCf8B0C03088D2d1C6ae4958c2fA56"), + new ArrayList<>(), + SIGNATURE_ALGORITHM + .get() + .createSignature( + new BigInteger( + "25c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2", 16), + new BigInteger( + "3c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df", 16), + (byte) 1)); + + SetCodeTransactionEncoder.encodeSingleSetCode(authorization, output); + + assertThat(output.encoded()) + .isEqualTo( + Bytes.fromHexString( + "0xf85a8094633688abc3ccf8b0c03088d2d1c6ae4958c2fa56c001a0025c1240d7ffec0daeedb752d3357aff2e3cd58468f0c2d43ee0ee999e02ace2a03c8a25b2becd6e666f69803d1ae3322f2e137b7745c2c7f19da80f993ffde4df")); + } +} diff --git a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java index d541cbd99aa..b8e8a7765d9 100644 --- a/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java +++ b/ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionProcessorTest.java @@ -39,6 +39,7 @@ import org.hyperledger.besu.evm.worldstate.WorldUpdater; import org.hyperledger.besu.evm.worldstate.WorldView; +import java.math.BigInteger; import java.util.List; import java.util.Optional; import java.util.Set; @@ -87,7 +88,8 @@ MainnetTransactionProcessor createTransactionProcessor(final boolean warmCoinbas warmCoinbase, MAX_STACK_SIZE, FeeMarket.legacy(), - CoinbaseFeePriceCalculator.frontier()); + CoinbaseFeePriceCalculator.frontier(), + new SetCodeTransactionProcessor(BigInteger.ONE)); } @Test diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java index 2e1728b2346..0af023851ca 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/GasCalculator.java @@ -645,4 +645,14 @@ default long computeExcessBlobGas(final long parentExcessBlobGas, final int newB default long computeExcessBlobGas(final long parentExcessBlobGas, final long blobGasUsed) { return 0L; } + + /** + * Returns the upfront gas cost for EIP 7702 operation. + * + * @param authorizationListLength The length of the authorization list + * @return the gas cost + */ + default long setCodeListGasCost(final int authorizationListLength) { + return 0L; + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java index 2b888c11bd0..0c223d780b0 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/gascalculator/PragueGasCalculator.java @@ -44,7 +44,8 @@ protected PragueGasCalculator(final int maxPrecompile) { super(maxPrecompile); } - public long getSetCodeListGasCost(final int authorizationListLength) { + @Override + public long setCodeListGasCost(final int authorizationListLength) { return PER_CONTRACT_CODE_BASE_COST * authorizationListLength; } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java index 9623eaf89ed..b5829694773 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/AbstractWorldUpdater.java @@ -28,6 +28,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +import org.apache.tuweni.bytes.Bytes; + /** * An abstract implementation of a {@link WorldUpdater} that buffers update over the {@link * WorldView}* provided in the constructor in memory. @@ -188,4 +190,26 @@ protected void reset() { updatedAccounts.clear(); deletedAccounts.clear(); } + + @Override + public void addCodeToEOA(final Address address, final Bytes code) { + final MutableAccount account = getAccount(address); + + if (!account.getCode().isEmpty()) { + return; + } + + account.setCode(code); + } + + @Override + public void removeCodeFromEOAs(final Address address) { + final MutableAccount account = getAccount(address); + + if (account.getCode().isEmpty()) { + return; + } + + account.setCode(Bytes.EMPTY); + } } diff --git a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java index 9b57b6ab9e3..4fa21ea54ea 100644 --- a/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java +++ b/evm/src/main/java/org/hyperledger/besu/evm/worldstate/WorldUpdater.java @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Optional; +import org.apache.tuweni.bytes.Bytes; + /** * An object that buffers updates made over a particular {@link WorldView}. * @@ -156,4 +158,23 @@ default void clearAccountsThatAreEmpty() { default void markTransactionBoundary() { // default is to ignore } + + /** + * Add code to EOA in case of EIP-7702 transaction. + * + * @param address the address of the EOA account to add code. + * @param code the code to add to the EOA account. + */ + default void addCodeToEOA(final Address address, final Bytes code) { + // default is to ignore + } + + /** + * Remove code from EOA in case of EIP-7702 transaction. + * + * @param address the address of the EOA account to remove code. + */ + default void removeCodeFromEOAs(final Address address) { + // default is to ignore + } }