Ref tests 10.4 (#3835)

10.4 revision of reference tests has notable changes

* Nonce can be up to 2^64-1, with some opcode and validity interactions specced in eip-2681
* Wei fields can be up to 2^256, tests check for rollover
* VM Tests were removed
* Legacy Tests were removed
* Reference tests make it clear that transactions with a maxPriorityFee of zero are valid. Perhaps useless, but still valid.
* Adding validation hooks in the out-of-chain test execution. These validations are caught in full-chain execution, just not in conveniently places for integration testing.
* This does not transaction tests support for the "merge" fork, as the release notes marked it as an "upcoming" feature.


Signed-off-by: Danno Ferrin <danno.ferrin@gmail.com>
Signed-off-by: Daniel Lehrner <daniel.lehrner@consensys.net>
Co-authored-by: Diego López León <dieguitoll@gmail.com>
Co-authored-by: Daniel Lehrner <daniel.lehrner@consensys.net>
pull/3865/head
Danno Ferrin 2 years ago committed by GitHub
parent 950673c798
commit d88bb5867f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      datatypes/src/main/java/org/hyperledger/besu/datatypes/Wei.java
  2. 3
      ethereum/api/build.gradle
  3. 19
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCount.java
  4. 2
      ethereum/api/src/main/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/results/Quantity.java
  5. 160
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/DebugAccountAtTest.java
  6. 39
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetProofTest.java
  7. 81
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/methods/EthGetTransactionCountTest.java
  8. 36
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetEeaTransactionCountTest.java
  9. 37
      ethereum/api/src/test/java/org/hyperledger/besu/ethereum/api/jsonrpc/internal/privacy/methods/priv/PrivGetTransactionCountTest.java
  10. 30
      ethereum/core/build.gradle
  11. 21
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/Transaction.java
  12. 15
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoder.java
  13. 10
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoder.java
  14. 26
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidator.java
  15. 4
      ethereum/core/src/main/java/org/hyperledger/besu/ethereum/transaction/TransactionInvalidReason.java
  16. 20
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/BlockDataGenerator.java
  17. 86
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionTest.java
  18. 27
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/TransactionTestCaseSpec.java
  19. 24
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionDecoderTest.java
  20. 40
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/core/encoding/TransactionEncoderTest.java
  21. 88
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/DifficultyCalculatorTests.java
  22. 33
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/MainnetTransactionValidatorTest.java
  23. 20
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTestTools.java
  24. 167
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/VMReferenceTest.java
  25. 143
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/vm/operations/CreateOperationTest.java
  26. 172
      ethereum/evmtool/src/main/java/org/hyperledger/besu/evmtool/StateTestSubCommand.java
  27. 74
      ethereum/evmtool/src/test/java/org/hyperledger/besu/evmtool/StateTestSubCommandTest.java
  28. 187
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/HighGasPrice.json
  29. 3
      ethereum/evmtool/src/test/resources/org/hyperledger/besu/evmtool/bogus-test.json
  30. 2
      ethereum/referencetests/build.gradle
  31. 19
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseEipSpec.java
  32. 8
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/GeneralStateTestCaseSpec.java
  33. 2
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/ReferenceTestWorldState.java
  34. 24
      ethereum/referencetests/src/main/java/org/hyperledger/besu/ethereum/referencetests/StateTestVersionedTransaction.java
  35. 2
      ethereum/referencetests/src/test/resources
  36. 4
      ethereum/retesteth/src/main/java/org/hyperledger/besu/ethereum/retesteth/RetestethService.java
  37. 10
      ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/AbstractRLPInput.java
  38. 3
      ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPInput.java
  39. 8
      ethereum/rlp/src/main/java/org/hyperledger/besu/ethereum/rlp/RLPOutput.java
  40. 9
      ethereum/rlp/src/test/java/org/hyperledger/besu/ethereum/rlp/BytesValueRLPInputTest.java
  41. 1
      evm/src/main/java/org/hyperledger/besu/evm/account/Account.java
  42. 2
      evm/src/main/java/org/hyperledger/besu/evm/account/MutableAccount.java
  43. 4
      evm/src/main/java/org/hyperledger/besu/evm/operation/AbstractCreateOperation.java
  44. 4
      gradle/versions.gradle

@ -29,6 +29,8 @@ public final class Wei extends BaseUInt256Value<Wei> implements Quantity {
public static final Wei ONE = of(1);
public static final Wei MAX_WEI = of(UInt256.MAX_VALUE);
Wei(final UInt256 value) {
super(value, Wei::new);
}

@ -98,7 +98,10 @@ dependencies {
testImplementation 'org.apache.logging.log4j:log4j-core'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.junit.platform:junit-platform-runner'
testImplementation 'org.mockito:mockito-core'
testImplementation 'org.mockito:mockito-junit-jupiter'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'

@ -23,28 +23,24 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.Quantity;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.eth.transactions.sorter.AbstractPendingTransactionsSorter;
import java.util.OptionalLong;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
public class EthGetTransactionCount extends AbstractBlockParameterOrBlockHashMethod {
private final Supplier<AbstractPendingTransactionsSorter> pendingTransactions;
private final boolean resultAsDecimal;
public EthGetTransactionCount(
final BlockchainQueries blockchain,
final AbstractPendingTransactionsSorter pendingTransactions) {
this(Suppliers.ofInstance(blockchain), Suppliers.ofInstance(pendingTransactions), false);
this(Suppliers.ofInstance(blockchain), Suppliers.ofInstance(pendingTransactions));
}
public EthGetTransactionCount(
final Supplier<BlockchainQueries> blockchain,
final Supplier<AbstractPendingTransactionsSorter> pendingTransactions,
final boolean resultAsDecimal) {
final Supplier<AbstractPendingTransactionsSorter> pendingTransactions) {
super(blockchain);
this.pendingTransactions = pendingTransactions;
this.resultAsDecimal = resultAsDecimal;
}
@Override
@ -61,12 +57,17 @@ public class EthGetTransactionCount extends AbstractBlockParameterOrBlockHashMet
@Override
protected Object pendingResult(final JsonRpcRequestContext request) {
final Address address = request.getRequiredParameter(0, Address.class);
final OptionalLong pendingNonce = pendingTransactions.get().getNextNonceForSender(address);
final long pendingNonce = pendingTransactions.get().getNextNonceForSender(address).orElse(0);
final long latestNonce =
getBlockchainQueries()
.getTransactionCount(
address, getBlockchainQueries().getBlockchain().getChainHead().getHash());
return Quantity.create(Math.max(pendingNonce.orElse(0), latestNonce));
if (Long.compareUnsigned(pendingNonce, latestNonce) > 0) {
return Quantity.create(pendingNonce);
}
return Quantity.create(latestNonce);
}
@Override
@ -74,6 +75,6 @@ public class EthGetTransactionCount extends AbstractBlockParameterOrBlockHashMet
final Address address = request.getRequiredParameter(0, Address.class);
final long transactionCount = getBlockchainQueries().getTransactionCount(address, blockHash);
return resultAsDecimal ? Long.toString(transactionCount) : Quantity.create(transactionCount);
return Quantity.create(transactionCount);
}
}

@ -43,7 +43,7 @@ public class Quantity {
}
public static String create(final long value) {
return uint256ToHex(UInt256.valueOf(value));
return uint256ToHex(UInt256.fromHexString(Long.toHexString(value)));
}
public static String create(final Bytes value) {

@ -14,8 +14,12 @@
*/
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.mockito.ArgumentMatchers.any;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequest;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.JsonRpcRequestContext;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.BlockTrace;
@ -24,62 +28,70 @@ import org.hyperledger.besu.ethereum.api.jsonrpc.internal.processor.TransactionT
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcError;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcErrorResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.ImmutableDebugAccountAtResult;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.response.JsonRpcSuccessResponse;
import org.hyperledger.besu.ethereum.api.jsonrpc.internal.results.DebugAccountAtResult;
import org.hyperledger.besu.ethereum.api.query.BlockWithMetadata;
import org.hyperledger.besu.ethereum.api.query.BlockchainQueries;
import org.hyperledger.besu.ethereum.api.query.TransactionWithMetadata;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.debug.TraceFrame;
import org.hyperledger.besu.evm.account.Account;
import org.hyperledger.besu.evm.worldstate.WorldUpdater;
import java.util.Collections;
import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.assertj.core.api.Assertions;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
@RunWith(MockitoJUnitRunner.class)
public class DebugAccountAtTest {
private final BlockTracer blockTracer = Mockito.mock(BlockTracer.class);
private final BlockchainQueries blockchainQueries = Mockito.mock(BlockchainQueries.class);
@SuppressWarnings("unchecked")
private final BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata =
Mockito.mock(BlockWithMetadata.class);
private final TransactionWithMetadata transactionWithMetadata =
Mockito.mock(TransactionWithMetadata.class);
private final BlockTrace blockTrace = Mockito.mock(BlockTrace.class);
private final TransactionTrace transactionTrace = Mockito.mock(TransactionTrace.class);
private final DebugAccountAt debugAccountAt =
new DebugAccountAt(blockchainQueries, () -> blockTracer);
private final Transaction transaction = Mockito.mock(Transaction.class);
import org.mockito.junit.jupiter.MockitoExtension;
@ExtendWith(MockitoExtension.class)
class DebugAccountAtTest {
@Mock private BlockTracer blockTracer;
@Mock private BlockchainQueries blockchainQueries;
@Mock private BlockWithMetadata<TransactionWithMetadata, Hash> blockWithMetadata;
@Mock private TransactionWithMetadata transactionWithMetadata;
@Mock private BlockTrace blockTrace;
@Mock private TransactionTrace transactionTrace;
@Mock private TraceFrame traceFrame;
@Mock private Transaction transaction;
@Mock private WorldUpdater worldUpdater;
@Mock private Account account;
private static DebugAccountAt debugAccountAt;
@BeforeEach
void init() {
debugAccountAt = new DebugAccountAt(blockchainQueries, () -> blockTracer);
}
@Test
public void nameShouldBeDebugAccountAt() {
void nameShouldBeDebugAccountAt() {
Assertions.assertThat(debugAccountAt.getName()).isEqualTo("debug_accountAt");
}
@Test
public void testBlockNotFoundResponse() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any())).thenReturn(Optional.empty());
void testBlockNotFoundResponse() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.empty());
final Object[] params = new Object[] {Hash.ZERO.toHexString(), 0, Address.ZERO.toHexString()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.BLOCK_NOT_FOUND);
}
@Test
public void testInvalidParamsResponseEmptyList() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any()))
.thenReturn(Optional.of(blockWithMetadata));
void testInvalidParamsResponseEmptyList() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions()).thenReturn(Collections.emptyList());
final Object[] params = new Object[] {Hash.ZERO.toHexString(), 0, Address.ZERO.toHexString()};
@ -87,15 +99,14 @@ public class DebugAccountAtTest {
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.INVALID_PARAMS);
}
@Test
public void testInvalidParamsResponseNegative() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any()))
.thenReturn(Optional.of(blockWithMetadata));
void testInvalidParamsResponseNegative() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions())
.thenReturn(Collections.singletonList(transactionWithMetadata));
@ -104,15 +115,14 @@ public class DebugAccountAtTest {
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.INVALID_PARAMS);
}
@Test
public void testInvalidParamsResponseTooHigh() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any()))
.thenReturn(Optional.of(blockWithMetadata));
void testInvalidParamsResponseTooHigh() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions())
.thenReturn(Collections.singletonList(transactionWithMetadata));
@ -121,15 +131,14 @@ public class DebugAccountAtTest {
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.INVALID_PARAMS);
}
@Test
public void testTransactionNotFoundResponse() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any()))
.thenReturn(Optional.of(blockWithMetadata));
void testTransactionNotFoundResponse() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions())
.thenReturn(Collections.singletonList(transactionWithMetadata));
@ -138,24 +147,14 @@ public class DebugAccountAtTest {
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.TRANSACTION_NOT_FOUND);
}
@Test
public void testNoAccountFoundResponse() {
Mockito.when(blockchainQueries.blockByHash(Mockito.any()))
.thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions())
.thenReturn(Collections.singletonList(transactionWithMetadata));
Mockito.when(blockTracer.trace(Mockito.any(Hash.class), Mockito.any()))
.thenReturn(Optional.of(blockTrace));
Mockito.when(blockTrace.getTransactionTraces())
.thenReturn(Collections.singletonList(transactionTrace));
Mockito.when(transactionTrace.getTransaction()).thenReturn(transaction);
Mockito.when(transactionWithMetadata.getTransaction()).thenReturn(transaction);
Mockito.when(transaction.getHash()).thenReturn(Hash.ZERO);
void testNoAccountFoundResponse() {
setupMockTransaction();
final Object[] params = new Object[] {Hash.ZERO.toHexString(), 0, Address.ZERO.toHexString()};
final JsonRpcRequestContext request =
@ -163,24 +162,59 @@ public class DebugAccountAtTest {
final JsonRpcResponse response = debugAccountAt.response(request);
Assertions.assertThat(response instanceof JsonRpcErrorResponse).isTrue();
Assertions.assertThat(response).isInstanceOf(JsonRpcErrorResponse.class);
Assertions.assertThat(((JsonRpcErrorResponse) response).getError())
.isEqualByComparingTo(JsonRpcError.NO_ACCOUNT_FOUND);
}
@Test
public void testResult() {
void shouldBeSuccessfulWhenTransactionsAndAccountArePresent() {
final String codeString =
"0x608060405234801561001057600080fd5b506004361061002b5760003560e01c8063b27b880414610030575b";
final Bytes code = Bytes.fromHexString(codeString);
final String nonce = "0x1";
final String balance = "0xffff";
final String codeHash = "0xf5f334d41776ed2828fc910d488a05c57fe7c2352aab2d16e30539d7726e1562";
ImmutableDebugAccountAtResult result =
debugAccountAt.debugAccountAtResult(code, nonce, balance, codeHash);
Assertions.assertThat(result.getBalance()).isEqualTo(balance);
Assertions.assertThat(result.getNonce()).isEqualTo(nonce);
final long nonce = MAX_NONCE - 1;
final String balanceString = "0xffff";
final Wei balance = Wei.fromHexString(balanceString);
final Hash codeHash = Hash.hash(code);
setupMockTransaction();
setupMockAccount();
Mockito.when(account.getCode()).thenReturn(code);
Mockito.when(account.getNonce()).thenReturn(nonce);
Mockito.when(account.getBalance()).thenReturn(balance);
Mockito.when(account.getCodeHash()).thenReturn(codeHash);
final Object[] params = new Object[] {Hash.ZERO.toHexString(), 0, Address.ZERO.toHexString()};
final JsonRpcRequestContext request =
new JsonRpcRequestContext(new JsonRpcRequest("2.0", "debug_accountAt", params));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) debugAccountAt.response(request);
final DebugAccountAtResult result = (DebugAccountAtResult) response.getResult();
Assertions.assertThat(result.getCode()).isEqualTo(codeString);
Assertions.assertThat(result.getCodehash()).isEqualTo(codeHash);
Assertions.assertThat(result.getNonce()).isEqualTo("0x" + Long.toHexString(nonce));
Assertions.assertThat(result.getBalance()).isEqualTo(balanceString);
Assertions.assertThat(result.getCodehash()).isEqualTo(codeHash.toHexString());
}
private void setupMockAccount() {
Mockito.when(transactionTrace.getTraceFrames())
.thenReturn(Collections.singletonList(traceFrame));
Mockito.when(traceFrame.getWorldUpdater()).thenReturn(worldUpdater);
Mockito.when(worldUpdater.get(any())).thenReturn(account);
Mockito.when(account.getAddress()).thenReturn(Address.ZERO);
}
private void setupMockTransaction() {
Mockito.when(blockchainQueries.blockByHash(any())).thenReturn(Optional.of(blockWithMetadata));
Mockito.when(blockWithMetadata.getTransactions())
.thenReturn(Collections.singletonList(transactionWithMetadata));
Mockito.when(blockTracer.trace(any(Hash.class), any())).thenReturn(Optional.of(blockTrace));
Mockito.when(blockTrace.getTransactionTraces())
.thenReturn(Collections.singletonList(transactionTrace));
Mockito.when(transactionTrace.getTransaction()).thenReturn(transaction);
Mockito.when(transactionWithMetadata.getTransaction()).thenReturn(transaction);
Mockito.when(transaction.getHash()).thenReturn(Hash.ZERO);
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyList;
import static org.mockito.ArgumentMatchers.eq;
@ -45,14 +46,17 @@ import java.util.Optional;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.assertj.core.api.Assertions;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import org.mockito.junit.jupiter.MockitoExtension;
import org.mockito.junit.jupiter.MockitoSettings;
import org.mockito.quality.Strictness;
@RunWith(MockitoJUnitRunner.class)
public class EthGetProofTest {
@ExtendWith(MockitoExtension.class)
@MockitoSettings(strictness = Strictness.LENIENT)
class EthGetProofTest {
@Mock private Blockchain blockchain;
@Mock private BlockchainQueries blockchainQueries;
@Mock private ChainHead chainHead;
@ -67,18 +71,18 @@ public class EthGetProofTest {
UInt256.fromHexString("0x0000000000000000000000000000000000000000000000000000000000000001");
private final long blockNumber = 1;
@Before
@BeforeEach
public void setUp() {
method = new EthGetProof(blockchainQueries);
}
@Test
public void returnsCorrectMethodName() {
void returnsCorrectMethodName() {
assertThat(method.getName()).isEqualTo(ETH_METHOD);
}
@Test
public void errorWhenNoAddressAccountSupplied() {
void errorWhenNoAddressAccountSupplied() {
final JsonRpcRequestContext request = requestWithParams(null, null, "latest");
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchainQueries.getBlockchain().getChainHead()).thenReturn(chainHead);
@ -90,7 +94,7 @@ public class EthGetProofTest {
}
@Test
public void errorWhenNoStorageKeysSupplied() {
void errorWhenNoStorageKeysSupplied() {
final JsonRpcRequestContext request = requestWithParams(address.toString(), null, "latest");
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchainQueries.getBlockchain().getChainHead()).thenReturn(chainHead);
@ -102,7 +106,7 @@ public class EthGetProofTest {
}
@Test
public void errorWhenNoBlockNumberSupplied() {
void errorWhenNoBlockNumberSupplied() {
final JsonRpcRequestContext request = requestWithParams(address.toString(), new String[] {});
Assertions.assertThatThrownBy(() -> method.response(request))
@ -111,7 +115,7 @@ public class EthGetProofTest {
}
@Test
public void errorWhenAccountNotFound() {
void errorWhenAccountNotFound() {
generateWorldState();
@ -132,7 +136,7 @@ public class EthGetProofTest {
}
@Test
public void errorWhenWorldStateUnavailable() {
void errorWhenWorldStateUnavailable() {
when(blockchainQueries.headBlockNumber()).thenReturn(14L);
when(blockchainQueries.getWorldState(any())).thenReturn(Optional.empty());
@ -152,7 +156,7 @@ public class EthGetProofTest {
}
@Test
public void getProof() {
void getProof() {
final GetProofResult expectedResponse = generateWorldState();
@ -164,7 +168,10 @@ public class EthGetProofTest {
final JsonRpcSuccessResponse response = (JsonRpcSuccessResponse) method.response(request);
assertThat(response.getResult()).usingRecursiveComparison().isEqualTo(expectedResponse);
final GetProofResult result = (GetProofResult) response.getResult();
assertThat(result).usingRecursiveComparison().isEqualTo(expectedResponse);
assertThat(result.getNonce()).isEqualTo("0xfffffffffffffffe");
}
private JsonRpcRequestContext requestWithParams(final Object... params) {
@ -176,7 +183,7 @@ public class EthGetProofTest {
final Wei balance = Wei.of(1);
final Hash codeHash =
Hash.fromHexString("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470");
final long nonce = 1;
final long nonce = MAX_NONCE - 1;
final Hash rootHash =
Hash.fromHexString("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b431");
final Hash storageRoot =

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.methods;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@ -34,16 +35,10 @@ import java.util.Arrays;
import java.util.Collection;
import java.util.OptionalLong;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
@RunWith(Parameterized.class)
public class EthGetTransactionCountTest {
@Parameterized.Parameter public AbstractPendingTransactionsSorter pendingTransactions;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.MethodSource;
class EthGetTransactionCountTest {
private final Blockchain blockchain = mock(Blockchain.class);
private final BlockchainQueries blockchainQueries = mock(BlockchainQueries.class);
private final ChainHead chainHead = mock(ChainHead.class);
@ -52,7 +47,6 @@ public class EthGetTransactionCountTest {
private final String pendingTransactionString = "0x00000000000000000000000000000000000000AA";
private final Object[] pendingParams = new Object[] {pendingTransactionString, "pending"};
@Parameterized.Parameters
public static Collection<Object[]> data() {
return Arrays.asList(
new Object[][] {
@ -61,13 +55,12 @@ public class EthGetTransactionCountTest {
});
}
@Before
public void setup() {
ethGetTransactionCount = new EthGetTransactionCount(blockchainQueries, pendingTransactions);
}
@ParameterizedTest
@MethodSource("data")
void shouldUsePendingTransactionsWhenToldTo(
final AbstractPendingTransactionsSorter pendingTransactions) {
setup(pendingTransactions);
@Test
public void shouldUsePendingTransactionsWhenToldTo() {
final Address address = Address.fromHexString(pendingTransactionString);
when(pendingTransactions.getNextNonceForSender(address)).thenReturn(OptionalLong.of(12));
mockGetTransactionCount(address, 7L);
@ -79,8 +72,12 @@ public class EthGetTransactionCountTest {
assertThat(response.getResult()).isEqualTo("0xc");
}
@Test
public void shouldUseLatestTransactionsWhenNoPendingTransactions() {
@ParameterizedTest
@MethodSource("data")
void shouldUseLatestTransactionsWhenNoPendingTransactions(
final AbstractPendingTransactionsSorter pendingTransactions) {
setup(pendingTransactions);
final Address address = Address.fromHexString(pendingTransactionString);
when(pendingTransactions.getNextNonceForSender(address)).thenReturn(OptionalLong.empty());
mockGetTransactionCount(address, 7L);
@ -92,8 +89,12 @@ public class EthGetTransactionCountTest {
assertThat(response.getResult()).isEqualTo("0x7");
}
@Test
public void shouldUseLatestWhenItIsBiggerThanPending() {
@ParameterizedTest
@MethodSource("data")
void shouldUseLatestWhenItIsBiggerThanPending(
final AbstractPendingTransactionsSorter pendingTransactions) {
setup(pendingTransactions);
final Address address = Address.fromHexString(pendingTransactionString);
mockGetTransactionCount(address, 8);
when(pendingTransactions.getNextNonceForSender(Address.fromHexString(pendingTransactionString)))
@ -106,6 +107,46 @@ public class EthGetTransactionCountTest {
assertThat(response.getResult()).isEqualTo("0x8");
}
@ParameterizedTest
@MethodSource("data")
void shouldReturnPendingWithHighNonce(
final AbstractPendingTransactionsSorter pendingTransactions) {
setup(pendingTransactions);
final Address address = Address.fromHexString(pendingTransactionString);
when(pendingTransactions.getNextNonceForSender(address))
.thenReturn(OptionalLong.of(MAX_NONCE - 1));
mockGetTransactionCount(address, 7L);
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("1", "eth_getTransactionCount", pendingParams));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) ethGetTransactionCount.response(request);
assertThat(response.getResult()).isEqualTo("0xfffffffffffffffe");
}
@ParameterizedTest
@MethodSource("data")
void shouldReturnLatestWithHighNonce(
final AbstractPendingTransactionsSorter pendingTransactions) {
setup(pendingTransactions);
final Address address = Address.fromHexString(pendingTransactionString);
when(pendingTransactions.getNextNonceForSender(address))
.thenReturn(OptionalLong.of(MAX_NONCE - 2));
mockGetTransactionCount(address, MAX_NONCE - 1);
final JsonRpcRequestContext request =
new JsonRpcRequestContext(
new JsonRpcRequest("1", "eth_getTransactionCount", pendingParams));
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) ethGetTransactionCount.response(request);
assertThat(response.getResult()).isEqualTo("0xfffffffffffffffe");
}
private void setup(final AbstractPendingTransactionsSorter pendingTransactions) {
ethGetTransactionCount = new EthGetTransactionCount(blockchainQueries, pendingTransactions);
}
private void mockGetTransactionCount(final Address address, final long transactionCount) {
when(blockchainQueries.getBlockchain()).thenReturn(blockchain);
when(blockchainQueries.getBlockchain().getChainHead()).thenReturn(chainHead);

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
@ -33,10 +34,15 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
import org.junit.Before;
import org.junit.Test;
import java.util.stream.Stream;
public class PrivGetEeaTransactionCountTest {
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
class PrivGetEeaTransactionCountTest {
private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
private static final String[] PRIVATE_FOR = {
"sgFkVOyFndZe/5SAZJO5UYbrl7pezHetveriBBWWnE8=",
@ -52,7 +58,7 @@ public class PrivGetEeaTransactionCountTest {
Address.fromHexString("0x1000000000000000000000000000000000000001");
private final PrivacyIdProvider privacyIdProvider = (user) -> ENCLAVE_PUBLIC_KEY;
@Before
@BeforeEach
public void setup() {
when(privacyParameters.isEnabled()).thenReturn(true);
final Object[] jsonBody = new Object[] {address.toString(), ENCLAVE_PUBLIC_KEY, PRIVATE_FOR};
@ -61,25 +67,23 @@ public class PrivGetEeaTransactionCountTest {
new JsonRpcRequest("2.0", "priv_getEeaTransactionCount", jsonBody));
}
@Test
public void validRequestProducesExpectedNonce() {
final long reportedNonce = 8L;
@ParameterizedTest(name = "{index}: {1}")
@MethodSource({"provideNonces"})
void validRequestProducesExpectedNonce(final long nonce, final String ignoredName) {
final PrivGetEeaTransactionCount method =
new PrivGetEeaTransactionCount(privacyController, privacyIdProvider);
when(privacyController.determineNonce(any(), any(), eq(ENCLAVE_PUBLIC_KEY)))
.thenReturn(reportedNonce);
when(privacyController.determineNonce(any(), any(), eq(ENCLAVE_PUBLIC_KEY))).thenReturn(nonce);
final JsonRpcResponse response = method.response(request);
assertThat(response).isInstanceOf(JsonRpcSuccessResponse.class);
final JsonRpcSuccessResponse successResponse = (JsonRpcSuccessResponse) response;
final int returnedValue = Integer.decode((String) successResponse.getResult());
assertThat(returnedValue).isEqualTo(reportedNonce);
assertThat(successResponse.getResult()).isEqualTo("0x" + Long.toHexString(nonce));
}
@Test
public void nonceProviderThrowsRuntimeExceptionProducesErrorResponse() {
void nonceProviderThrowsRuntimeExceptionProducesErrorResponse() {
final PrivGetEeaTransactionCount method =
new PrivGetEeaTransactionCount(privacyController, privacyIdProvider);
@ -95,7 +99,7 @@ public class PrivGetEeaTransactionCountTest {
}
@Test
public void nonceProviderThrowsAnExceptionProducesErrorResponse() {
void nonceProviderThrowsAnExceptionProducesErrorResponse() {
final PrivGetEeaTransactionCount method =
new PrivGetEeaTransactionCount(privacyController, privacyIdProvider);
@ -111,7 +115,7 @@ public class PrivGetEeaTransactionCountTest {
}
@Test
public void failsWithUnauthorizedErrorIfMultiTenancyValidationFails() {
void failsWithUnauthorizedErrorIfMultiTenancyValidationFails() {
final PrivGetEeaTransactionCount method =
new PrivGetEeaTransactionCount(privacyController, privacyIdProvider);
@ -125,4 +129,8 @@ public class PrivGetEeaTransactionCountTest {
assertThat(errorResponse.getError())
.isEqualTo(JsonRpcError.GET_PRIVATE_TRANSACTION_NONCE_ERROR);
}
private static Stream<Arguments> provideNonces() {
return Stream.of(Arguments.of(8, "low nonce"), Arguments.of(MAX_NONCE - 1, "high nonce"));
}
}

@ -15,6 +15,7 @@
package org.hyperledger.besu.ethereum.api.jsonrpc.internal.privacy.methods.priv;
import static org.assertj.core.api.Assertions.assertThat;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@ -32,13 +33,18 @@ import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.privacy.MultiTenancyValidationException;
import org.hyperledger.besu.ethereum.privacy.PrivacyController;
import java.util.stream.Stream;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.auth.User;
import io.vertx.ext.auth.impl.UserImpl;
import org.junit.Before;
import org.junit.Test;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
public class PrivGetTransactionCountTest {
class PrivGetTransactionCountTest {
private static final String ENCLAVE_PUBLIC_KEY = "A1aVtMxLCUHmBVHXoZzzBgPbW/wj5axDpW9X8l91SGo=";
private final PrivacyParameters privacyParameters = mock(PrivacyParameters.class);
@ -48,20 +54,21 @@ public class PrivGetTransactionCountTest {
private final Address senderAddress =
Address.fromHexString("0x627306090abab3a6e1400e9345bc60c78a8bef57");
private final long NONCE = 5;
private final User user =
new UserImpl(new JsonObject().put("privacyPublicKey", ENCLAVE_PUBLIC_KEY)) {};
private final PrivacyIdProvider privacyIdProvider = (user) -> ENCLAVE_PUBLIC_KEY;
@Before
public void before() {
@BeforeEach
public void setup() {
when(privacyParameters.isEnabled()).thenReturn(true);
when(privacyController.determineNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY))
.thenReturn(NONCE);
}
@Test
public void verifyTransactionCount() {
@ParameterizedTest(name = "{index}: {1}")
@MethodSource({"provideNonces"})
void verifyTransactionCount(final long nonce, final String ignoredName) {
when(privacyController.determineNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY))
.thenReturn(nonce);
final PrivGetTransactionCount privGetTransactionCount =
new PrivGetTransactionCount(privacyController, privacyIdProvider);
@ -73,12 +80,12 @@ public class PrivGetTransactionCountTest {
final JsonRpcSuccessResponse response =
(JsonRpcSuccessResponse) privGetTransactionCount.response(request);
assertThat(response.getResult()).isEqualTo(String.format("0x%X", NONCE));
assertThat(response.getResult()).isEqualTo("0x" + Long.toHexString(nonce));
verify(privacyController).determineNonce(senderAddress, PRIVACY_GROUP_ID, ENCLAVE_PUBLIC_KEY);
}
@Test
public void failsWithNonceErrorIfExceptionIsThrown() {
void failsWithNonceErrorIfExceptionIsThrown() {
final PrivGetTransactionCount privGetTransactionCount =
new PrivGetTransactionCount(privacyController, privacyIdProvider);
@ -98,7 +105,7 @@ public class PrivGetTransactionCountTest {
}
@Test
public void failsWithUnauthorizedErrorIfMultiTenancyValidationFails() {
void failsWithUnauthorizedErrorIfMultiTenancyValidationFails() {
final PrivGetTransactionCount privGetTransactionCount =
new PrivGetTransactionCount(privacyController, privacyIdProvider);
@ -116,4 +123,8 @@ public class PrivGetTransactionCountTest {
final JsonRpcResponse response = privGetTransactionCount.response(request);
assertThat(response).isEqualTo(expectedResponse);
}
private static Stream<Arguments> provideNonces() {
return Stream.of(Arguments.of(5, "low nonce"), Arguments.of(MAX_NONCE - 1, "high nonce"));
}
}

@ -82,6 +82,7 @@ dependencies {
testImplementation 'org.apache.tuweni:tuweni-units'
testImplementation 'org.assertj:assertj-core'
testImplementation 'org.junit.jupiter:junit-jupiter'
testImplementation 'org.junit.jupiter:junit-jupiter-params'
testImplementation 'org.mockito:mockito-core'
testRuntimeOnly 'org.junit.vintage:junit-vintage-engine'
@ -174,19 +175,6 @@ task blockchainReferenceTestsSetup {
)
}
task legacyBlockchainReferenceTestsSetup {
inputs.files fileTree('../referencetests/src/test/resources/LegacyTests/Constantinople/BlockchainTests')
outputs.files "./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/blockchain"
generateTestFiles(
fileTree('../referencetests/src/test/resources/LegacyTests/Constantinople/BlockchainTests'),
file("./src/test/resources/org/hyperledger/besu/ethereum/vm/BlockchainReferenceTest.java.template"),
"LegacyTests",
"./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/blockchain",
"LegacyBlockchainReferenceTest",
("BlockchainTests/InvalidBlocks/bcExpectSection") // exclude test for test filling tool
)
}
task generalstateReferenceTestsSetup {
inputs.files fileTree("../referencetests/src/test/resources/GeneralStateTests")
outputs.files "./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/generalstate"
@ -199,18 +187,6 @@ task generalstateReferenceTestsSetup {
)
}
task legacyGeneralstateReferenceTestsSetup {
inputs.files fileTree('../referencetests/src/test/resources/LegacyTests/Constantinople/GeneralStateTests')
outputs.files "./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/generalstate"
generateTestFiles(
fileTree('../referencetests/src/test/resources/LegacyTests/Constantinople/GeneralStateTests'),
file("./src/test/resources/org/hyperledger/besu/ethereum/vm/GeneralStateReferenceTest.java.template"),
"LegacyTests",
"./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/generalstate",
"LegacyGeneralStateReferenceTest"
)
}
task generalstateRegressionReferenceTestsSetup {
inputs.files fileTree("./src/test/resources/regressions/generalstate")
outputs.files "./build/generated/sources/referencetests/java/test/org/hyperledger/besu/ethereum/vm/generalstate"
@ -242,16 +218,12 @@ clean.dependsOn(cleanupReferenceTests)
task referenceTests(type: Test, dependsOn: [
"blockchainReferenceTestsSetup",
"generalstateReferenceTestsSetup",
"legacyBlockchainReferenceTestsSetup",
"legacyGeneralstateReferenceTestsSetup",
"generalstateRegressionReferenceTestsSetup",
"compileTestJava"
]) {
compileTestJava.mustRunAfter blockchainReferenceTestsSetup
compileTestJava.mustRunAfter generalstateReferenceTestsSetup
compileTestJava.mustRunAfter generalstateRegressionReferenceTestsSetup
compileTestJava.mustRunAfter legacyBlockchainReferenceTestsSetup
compileTestJava.mustRunAfter legacyGeneralstateReferenceTestsSetup
doFirst {
if (!file("../referencetests/src/test/resources/README.md").exists()) {
throw new GradleException("ethereum/referencetests/src/test/resources/README.md missing: please clone submodules (git submodule update --init --recursive)")

@ -178,6 +178,16 @@ public class Transaction
"Must not specify access list for transaction not supporting it");
}
if (gasPrice
.or(() -> maxFeePerGas)
.orElse(Wei.ZERO)
.getAsBigInteger()
.multiply(BigInteger.valueOf(gasLimit))
.bitLength()
> 256) {
throw new IllegalArgumentException("Upfront gas cost exceeds UInt256");
}
if (Objects.equals(transactionType, TransactionType.ACCESS_LIST)) {
checkArgument(
maybeAccessList.isPresent(), "Must specify access list for access list transaction");
@ -639,7 +649,12 @@ public class Transaction
if (gasPrice == null || gasPrice.isZero()) {
return Wei.ZERO;
}
return Wei.of(getGasLimit()).multiply(gasPrice);
var cost = BigInteger.valueOf(getGasLimit()).multiply(gasPrice.getAsBigInteger());
if (cost.bitLength() > 256) {
return Wei.MAX_WEI;
} else {
return Wei.of(cost);
}
}
/**
@ -652,7 +667,7 @@ public class Transaction
* @return the up-front gas cost for the transaction
*/
public Wei getUpfrontCost() {
return getUpfrontGasCost().add(getValue());
return getUpfrontGasCost().addExact(getValue());
}
@Override
@ -1068,6 +1083,6 @@ public class Transaction
* @return the effective gas price.
*/
public final Wei getEffectiveGasPrice(final Optional<Wei> baseFeePerGas) {
return getEffectivePriorityFeePerGas(baseFeePerGas).add(baseFeePerGas.orElse(Wei.ZERO));
return getEffectivePriorityFeePerGas(baseFeePerGas).addExact(baseFeePerGas.orElse(Wei.ZERO));
}
}

@ -37,8 +37,8 @@ import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.Optional;
import java.util.function.Supplier;
import com.google.common.base.Supplier;
import com.google.common.base.Suppliers;
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
@ -50,13 +50,12 @@ public class TransactionDecoder {
Transaction decode(RLPInput input);
}
private static final ImmutableMap<TransactionType, TransactionDecoder.Decoder>
TYPED_TRANSACTION_DECODERS =
ImmutableMap.of(
TransactionType.ACCESS_LIST,
TransactionDecoder::decodeAccessList,
TransactionType.EIP1559,
TransactionDecoder::decodeEIP1559);
private static final ImmutableMap<TransactionType, Decoder> TYPED_TRANSACTION_DECODERS =
ImmutableMap.of(
TransactionType.ACCESS_LIST,
TransactionDecoder::decodeAccessList,
TransactionType.EIP1559,
TransactionDecoder::decodeEIP1559);
private static final Supplier<SignatureAlgorithm> SIGNATURE_ALGORITHM =
Suppliers.memoize(SignatureAlgorithmFactory::getInstance);

@ -26,9 +26,9 @@ import org.hyperledger.besu.plugin.data.TransactionType;
import java.math.BigInteger;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import com.google.common.collect.ImmutableMap;
import org.apache.tuweni.bytes.Bytes;
public class TransactionEncoder {
@ -38,8 +38,8 @@ public class TransactionEncoder {
void encode(Transaction transaction, RLPOutput output);
}
private static final ImmutableMap<TransactionType, Encoder> TYPED_TRANSACTION_ENCODERS =
ImmutableMap.of(
private static final Map<TransactionType, Encoder> TYPED_TRANSACTION_ENCODERS =
Map.of(
TransactionType.ACCESS_LIST,
TransactionEncoder::encodeAccessList,
TransactionType.EIP1559,
@ -118,7 +118,7 @@ public class TransactionEncoder {
final Bytes payload,
final List<AccessListEntry> accessList,
final RLPOutput rlpOutput) {
rlpOutput.writeLongScalar(chainId.orElseThrow().longValue());
rlpOutput.writeBigIntegerScalar(chainId.orElseThrow());
rlpOutput.writeLongScalar(nonce);
rlpOutput.writeUInt256Scalar(gasPrice);
rlpOutput.writeLongScalar(gasLimit);
@ -146,7 +146,7 @@ public class TransactionEncoder {
static void encodeEIP1559(final Transaction transaction, final RLPOutput out) {
out.startList();
out.writeLongScalar(transaction.getChainId().orElseThrow().longValue());
out.writeBigIntegerScalar(transaction.getChainId().orElseThrow());
out.writeLongScalar(transaction.getNonce());
out.writeUInt256Scalar(transaction.getMaxPriorityFeePerGas().orElseThrow());
out.writeUInt256Scalar(transaction.getMaxFeePerGas().orElseThrow());

@ -14,6 +14,8 @@
*/
package org.hyperledger.besu.ethereum.mainnet;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import org.hyperledger.besu.crypto.SECPSignature;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Hash;
@ -30,6 +32,8 @@ import java.math.BigInteger;
import java.util.Optional;
import java.util.Set;
import com.google.common.primitives.Longs;
/**
* Validates a transaction based on Frontier protocol runtime requirements.
*
@ -150,19 +154,29 @@ public class MainnetTransactionValidator {
}
}
// transactionValidationParams.isAllowExceedingBalance() is used on eth_call
if (!feeMarket.satisfiesFloorTxCost(transaction)
&& !transactionValidationParams.isAllowExceedingBalance()) {
if (transaction.getNonce() == MAX_NONCE) {
return ValidationResult.invalid(
TransactionInvalidReason.INVALID_TRANSACTION_FORMAT,
"effective gas price is too low to execute");
TransactionInvalidReason.NONCE_TOO_HIGH, "Nonces must be less than 2^64-1");
}
if (transaction
.getGasPrice()
.or(transaction::getMaxFeePerGas)
.orElse(Wei.ZERO)
.getAsBigInteger()
.multiply(new BigInteger(1, Longs.toByteArray(transaction.getGasLimit())))
.bitLength()
> 256) {
return ValidationResult.invalid(
TransactionInvalidReason.UPFRONT_FEE_TOO_HIGH,
"gasLimit x price must be less than 2^256");
}
final long intrinsicGasCost =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation())
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L));
if (intrinsicGasCost > transaction.getGasLimit()) {
if (Long.compareUnsigned(intrinsicGasCost, transaction.getGasLimit()) > 0) {
return ValidationResult.invalid(
TransactionInvalidReason.INTRINSIC_GAS_EXCEEDS_GAS_LIMIT,
String.format(

@ -47,5 +47,7 @@ public enum TransactionInvalidReason {
INTERNAL_ERROR,
// Quroum Compatibility Invalid Reasons
GAS_PRICE_MUST_BE_ZERO,
ETHER_VALUE_NOT_SUPPORTED
ETHER_VALUE_NOT_SUPPORTED,
NONCE_TOO_HIGH,
UPFRONT_FEE_TOO_HIGH
}

@ -56,10 +56,10 @@ import java.util.Optional;
import java.util.OptionalLong;
import java.util.Random;
import java.util.Set;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.google.common.base.Supplier;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
@ -92,9 +92,9 @@ public class BlockDataGenerator {
}
private KeyPairGenerator createKeyPairGenerator(final long seed) {
final KeyPairGenerator keyPairGenerator;
final KeyPairGenerator localKeyPairGenerator;
try {
keyPairGenerator =
localKeyPairGenerator =
KeyPairGenerator.getInstance(
SignatureAlgorithm.ALGORITHM, signatureAlgorithm.getProvider());
} catch (final Exception e) {
@ -105,11 +105,11 @@ public class BlockDataGenerator {
try {
final SecureRandom secureRandom = SecureRandomProvider.createSecureRandom();
secureRandom.setSeed(seed);
keyPairGenerator.initialize(ecGenParameterSpec, secureRandom);
localKeyPairGenerator.initialize(ecGenParameterSpec, secureRandom);
} catch (final InvalidAlgorithmParameterException e) {
throw new RuntimeException(e);
}
return keyPairGenerator;
return localKeyPairGenerator;
}
/**
@ -192,7 +192,7 @@ public class BlockDataGenerator {
}
}
}
account.setNonce(random.nextInt(10));
account.setNonce(random.nextLong());
account.setBalance(Wei.of(positiveLong()));
accounts.add(account);
@ -382,7 +382,7 @@ public class BlockDataGenerator {
private Transaction accessListTransaction(final Bytes payload, final Address to) {
return Transaction.builder()
.type(TransactionType.ACCESS_LIST)
.nonce(positiveLong())
.nonce(random.nextLong())
.gasPrice(Wei.wrap(bytesValue(4)))
.gasLimit(positiveLong())
.to(to)
@ -395,7 +395,7 @@ public class BlockDataGenerator {
private List<AccessListEntry> accessList() {
final List<Address> accessedAddresses =
Stream.generate(this::address).limit(1 + random.nextInt(3)).collect(toUnmodifiableList());
Stream.generate(this::address).limit(1L + random.nextInt(3)).collect(toUnmodifiableList());
final List<AccessListEntry> accessedStorage = new ArrayList<>();
for (int i = 0; i < accessedAddresses.size(); ++i) {
accessedStorage.add(
@ -409,7 +409,7 @@ public class BlockDataGenerator {
private Transaction eip1559Transaction(final Bytes payload, final Address to) {
return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(positiveLong())
.nonce(random.nextLong())
.maxPriorityFeePerGas(Wei.wrap(bytesValue(4)))
.maxFeePerGas(Wei.wrap(bytesValue(4)))
.gasLimit(positiveLong())
@ -423,7 +423,7 @@ public class BlockDataGenerator {
private Transaction frontierTransaction(final Bytes payload, final Address to) {
return Transaction.builder()
.type(TransactionType.FRONTIER)
.nonce(positiveLong())
.nonce(random.nextLong())
.gasPrice(Wei.wrap(bytesValue(4)))
.gasLimit(positiveLong())
.to(to)

@ -17,10 +17,25 @@ package org.hyperledger.besu.ethereum.core;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.mainnet.MainnetTransactionValidator;
import org.hyperledger.besu.ethereum.mainnet.TransactionValidationParams;
import org.hyperledger.besu.ethereum.mainnet.ValidationResult;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestProtocolSchedules;
import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.hyperledger.besu.ethereum.transaction.TransactionInvalidReason;
import org.hyperledger.besu.evm.gascalculator.BerlinGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ByzantiumGasCalculator;
import org.hyperledger.besu.evm.gascalculator.ConstantinopleGasCalculator;
import org.hyperledger.besu.evm.gascalculator.FrontierGasCalculator;
import org.hyperledger.besu.evm.gascalculator.GasCalculator;
import org.hyperledger.besu.evm.gascalculator.HomesteadGasCalculator;
import org.hyperledger.besu.evm.gascalculator.IstanbulGasCalculator;
import org.hyperledger.besu.evm.gascalculator.LondonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.PetersburgGasCalculator;
import org.hyperledger.besu.evm.gascalculator.SpuriousDragonGasCalculator;
import org.hyperledger.besu.evm.gascalculator.TangerineWhistleGasCalculator;
import org.hyperledger.besu.testutil.JsonTestParameters;
import java.util.Collection;
@ -45,6 +60,7 @@ public class TransactionTest {
.getTransactionValidator();
}
private final String name;
private final TransactionTestCaseSpec spec;
private static final String TEST_CONFIG_FILE_DIR_PATH = "TransactionTests/";
@ -52,80 +68,112 @@ public class TransactionTest {
@Parameters(name = "Name: {0}")
public static Collection<Object[]> getTestParametersForConfig() {
return JsonTestParameters.create(TransactionTestCaseSpec.class)
// ignore tests that expect transactions with large gasLimits to properly decode
.ignore("TransactionWithGasLimitOverflow(2|63)", "TransactionWithGasLimitxPriceOverflow$")
// Nonce is tracked with type long, large valued nonces can't currently be decoded
.ignore("TransactionWithHighNonce256")
.generator((name, spec, collector) -> collector.add(name, spec, true))
.generate(TEST_CONFIG_FILE_DIR_PATH);
}
public TransactionTest(
final String name, final TransactionTestCaseSpec spec, final boolean runTest) {
this.name = name;
this.spec = spec;
assumeTrue("Test " + name + " was ignored", runTest);
}
@Test
public void frontier() {
milestone("Frontier");
milestone("Frontier", new FrontierGasCalculator(), Optional.empty());
}
@Test
public void homestead() {
milestone("Homestead");
milestone("Homestead", new HomesteadGasCalculator(), Optional.empty());
}
@Test
public void eIP150() {
milestone("EIP150");
milestone("EIP150", new TangerineWhistleGasCalculator(), Optional.empty());
}
@Test
public void eIP158() {
milestone("EIP158");
milestone("EIP158", new SpuriousDragonGasCalculator(), Optional.empty());
}
@Test
public void byzantium() {
milestone("Byzantium");
milestone("Byzantium", new ByzantiumGasCalculator(), Optional.empty());
}
@Test
public void constantinople() {
milestone("Constantinople");
milestone("Constantinople", new ConstantinopleGasCalculator(), Optional.empty());
}
@Test
public void petersburg() {
milestone("ConstantinopleFix");
milestone("ConstantinopleFix", new PetersburgGasCalculator(), Optional.empty());
}
public void milestone(final String milestone) {
@Test
public void istanbul() {
milestone("Istanbul", new IstanbulGasCalculator(), Optional.empty());
}
@Test
public void berlin() {
milestone("Berlin", new BerlinGasCalculator(), Optional.empty());
}
@Test
public void london() {
milestone("London", new LondonGasCalculator(), Optional.of(Wei.of(0)));
}
public void milestone(
final String milestone, final GasCalculator gasCalculator, final Optional<Wei> baseFee) {
final TransactionTestCaseSpec.Expectation expected = spec.expectation(milestone);
try {
final Bytes rlp = spec.getRlp();
Bytes rlp = spec.getRlp();
// non-frontier transactions need to be opaque for parsing to work
if (rlp.get(0) > 0) {
final BytesValueRLPOutput output = new BytesValueRLPOutput();
output.writeBytes(rlp);
rlp = output.encoded();
}
// Test transaction deserialization (will throw an exception if it fails).
final Transaction transaction = Transaction.readFrom(RLP.input(rlp));
if (!transactionValidator(milestone)
.validate(transaction, Optional.empty(), TransactionValidationParams.processingBlock())
.isValid()) {
throw new RuntimeException(String.format("Transaction is invalid %s", transaction));
final ValidationResult<TransactionInvalidReason> validation =
transactionValidator(milestone)
.validate(transaction, baseFee, TransactionValidationParams.processingBlock());
if (!validation.isValid()) {
throw new RuntimeException(
String.format(
"Transaction is invalid %s - %s", validation.getInvalidReason(), transaction));
}
// Test rlp encoding
final Bytes actualRlp = RLP.encode(transaction::writeTo);
assertThat(expected.isSucceeds()).isTrue();
assertThat(expected.isSucceeds())
.withFailMessage("Transaction " + name + "/" + milestone + " was supposed to be invalid")
.isTrue();
assertThat(actualRlp).isEqualTo(rlp);
assertThat(transaction.getSender()).isEqualTo(expected.getSender());
assertThat(transaction.getHash()).isEqualTo(expected.getHash());
final long intrinsicGasCost =
gasCalculator.transactionIntrinsicGasCost(
transaction.getPayload(), transaction.isContractCreation())
+ (transaction.getAccessList().map(gasCalculator::accessListGasCost).orElse(0L));
assertThat(intrinsicGasCost).isEqualTo(expected.getIntrinsicGas());
} catch (final Exception e) {
assertThat(expected.isSucceeds()).isFalse();
if (expected.isSucceeds()) {
throw e;
}
}
}
}

@ -35,14 +35,20 @@ public class TransactionTestCaseSpec {
private final Address sender;
private long intrinsicGas;
private final boolean succeeds;
Expectation(
@JsonProperty("hash") final String hash, @JsonProperty("sender") final String sender) {
this.succeeds = hash != null && sender != null;
@JsonProperty("exception") final String exception,
@JsonProperty("hash") final String hash,
@JsonProperty("intrinsicGas") final String intrinsicGas,
@JsonProperty("sender") final String sender) {
this.succeeds = exception == null;
if (succeeds) {
this.hash = Hash.fromHexString(hash);
this.sender = Address.fromHexString(sender);
this.intrinsicGas = Long.decode(intrinsicGas);
} else {
this.hash = null;
this.sender = null;
@ -60,6 +66,10 @@ public class TransactionTestCaseSpec {
public Address getSender() {
return this.sender;
}
public long getIntrinsicGas() {
return intrinsicGas;
}
}
private final HashMap<String, Expectation> expectations;
@ -70,18 +80,21 @@ public class TransactionTestCaseSpec {
@JsonCreator
public TransactionTestCaseSpec(final Map<String, Object> props) {
expectations = new HashMap<>();
for (final Map.Entry<String, Object> entry : props.entrySet()) {
if ("rlp".equals(entry.getKey())) continue;
var result = (Map<String, Object>) props.get("result");
for (final Map.Entry<String, Object> entry : result.entrySet()) {
final Map<String, Object> expectation = (Map<String, Object>) entry.getValue();
expectations.put(
entry.getKey(),
new Expectation((String) expectation.get("hash"), (String) expectation.get("sender")));
new Expectation(
(String) expectation.get("exception"),
(String) expectation.get("hash"),
(String) expectation.get("intrinsicGas"),
(String) expectation.get("sender")));
}
Bytes parsedRlp = null;
try {
parsedRlp = Bytes.fromHexString(props.get("rlp").toString());
parsedRlp = Bytes.fromHexString(props.get("txbytes").toString());
} catch (final IllegalArgumentException e) {
// Some test cases include rlp "hex strings" with invalid characters
// In this case, just set rlp to null

@ -16,6 +16,7 @@ package org.hyperledger.besu.ethereum.core.encoding;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.hyperledger.besu.evm.account.Account.MAX_NONCE;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Wei;
@ -27,9 +28,9 @@ import org.hyperledger.besu.ethereum.rlp.RLPInput;
import java.math.BigInteger;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
import org.junit.jupiter.api.Test;
public class TransactionDecoderTest {
class TransactionDecoderTest {
private static final String FRONTIER_TX_RLP =
"0xf901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
@ -37,9 +38,11 @@ public class TransactionDecoderTest {
"b8a902f8a686796f6c6f7632800285012a05f20082753094000000000000000000000000000000000000aaaa8080f838f794000000000000000000000000000000000000aaaae1a0000000000000000000000000000000000000000000000000000000000000000001a00c1d69648e348fe26155b45de45004f0e4195f6352d8f0935bc93e98a3e2a862a060064e5b9765c0ac74223b0cf49635c59ae0faf82044fd17bcc68a549ade6f95";
private static final String GOQUORUM_PRIVATE_TX_RLP =
"0xf88d0b808347b7608080b840290a80a37d198ff06abe189b638ff53ac8a8dc51a0aff07609d2aa75342783ae493b3e3c6b564c0eebe49284b05a0726fb33087b9e0231d349ea0c7b5661c8c526a07144db7045a395e608cda6ab051c86cc4fb42e319960b82087f3b26f0cbc3c2da00223ac129b22aec7a6c2ace3c3ef39c5eaaa54070fd82d8ee2140b0e70b1dca9";
private static final String NONCE_64_BIT_MAX_MINUS_2_TX_RLP =
"0xf86788fffffffffffffffe0182520894095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
@Test
public void decodeGoQuorumPrivateTransactionRlp() {
void decodeGoQuorumPrivateTransactionRlp() {
boolean goQuorumCompatibilityMode = true;
RLPInput input = RLP.input(Bytes.fromHexString(GOQUORUM_PRIVATE_TX_RLP));
@ -52,7 +55,7 @@ public class TransactionDecoderTest {
}
@Test
public void decodeFrontierNominalCase() {
void decodeFrontierNominalCase() {
final Transaction transaction =
TransactionDecoder.decodeForWire(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
assertThat(transaction).isNotNull();
@ -62,7 +65,7 @@ public class TransactionDecoderTest {
}
@Test
public void decodeEIP1559NominalCase() {
void decodeEIP1559NominalCase() {
final Transaction transaction =
TransactionDecoder.decodeForWire(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
assertThat(transaction).isNotNull();
@ -71,7 +74,7 @@ public class TransactionDecoderTest {
}
@Test
public void doesNotDecodeEIP1559WithLargeMaxFeePerGasOrLargeMaxPriorityFeePerGas() {
void doesNotDecodeEIP1559WithLargeMaxFeePerGasOrLargeMaxPriorityFeePerGas() {
final String txWithBigFees =
"0x02f84e0101a1648a5f8b2dcad5ea5ba6b720ff069c1d87c21a4a6a5b3766b39e2c2792367bb066a1ffa5ffaf5b0560d3a9fb186c2ede2ae6751bc0b4fef9107cf36389630b6196a38805800180c0010203";
final boolean goQuorumCompatibilityMode = false;
@ -81,4 +84,13 @@ public class TransactionDecoderTest {
Bytes.fromHexString(txWithBigFees), goQuorumCompatibilityMode))
.isInstanceOf(RLPException.class);
}
@Test
void shouldDecodeWithHighNonce() {
final Transaction transaction =
TransactionDecoder.decodeForWire(
RLP.input(Bytes.fromHexString(NONCE_64_BIT_MAX_MINUS_2_TX_RLP)));
assertThat(transaction).isNotNull();
assertThat(transaction.getNonce()).isEqualTo(MAX_NONCE - 1);
}
}

@ -24,48 +24,36 @@ import org.hyperledger.besu.ethereum.rlp.BytesValueRLPOutput;
import org.hyperledger.besu.ethereum.rlp.RLP;
import org.apache.tuweni.bytes.Bytes;
import org.junit.Test;
import org.junit.jupiter.api.Test;
/*
* Copyright ConsenSys AG.
*
* 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
*/
public class TransactionEncoderTest {
class TransactionEncoderTest {
private static final String FRONTIER_TX_RLP =
"0xf901fc8032830138808080b901ae60056013565b6101918061001d6000396000f35b3360008190555056006001600060e060020a6000350480630a874df61461003a57806341c0e1b514610058578063a02b161e14610066578063dbbdf0831461007757005b610045600435610149565b80600160a060020a031660005260206000f35b610060610161565b60006000f35b6100716004356100d4565b60006000f35b61008560043560243561008b565b60006000f35b600054600160a060020a031632600160a060020a031614156100ac576100b1565b6100d0565b8060018360005260205260406000208190555081600060005260206000a15b5050565b600054600160a060020a031633600160a060020a031614158015610118575033600160a060020a0316600182600052602052604060002054600160a060020a031614155b61012157610126565b610146565b600060018260005260205260406000208190555080600060005260206000a15b50565b60006001826000526020526040600020549050919050565b600054600160a060020a031633600160a060020a0316146101815761018f565b600054600160a060020a0316ff5b561ca0c5689ed1ad124753d54576dfb4b571465a41900a1dff4058d8adf16f752013d0a01221cbd70ec28c94a3b55ec771bcbc70778d6ee0b51ca7ea9514594c861b1884";
private static final String EIP1559_TX_RLP =
"0xb8a902f8a686796f6c6f7632800285012a05f20082753094000000000000000000000000000000000000aaaa8080f838f794000000000000000000000000000000000000aaaae1a0000000000000000000000000000000000000000000000000000000000000000001a00c1d69648e348fe26155b45de45004f0e4195f6352d8f0935bc93e98a3e2a862a060064e5b9765c0ac74223b0cf49635c59ae0faf82044fd17bcc68a549ade6f95";
private static final String NONCE_64_BIT_MAX_MINUS_2_TX_RLP =
"0xf86788fffffffffffffffe0182520894095e7baea6a6c7c4c2dfeb977efac326af552d8780801ba048b55bfa915ac795c431978d8a6a992b628d557da5ff759b307d495a36649353a01fffd310ac743f371de3b9f7f9cb56c0b28ad43601b4ab949f53faa07bd2c804";
@Test
public void encodeFrontierTxNominalCase() {
void encodeFrontierTxNominalCase() {
final Transaction transaction =
TransactionDecoder.decodeForWire(RLP.input(Bytes.fromHexString(FRONTIER_TX_RLP)));
final BytesValueRLPOutput output = new BytesValueRLPOutput();
TransactionEncoder.encodeForWire(transaction, output);
assertThat(FRONTIER_TX_RLP).isEqualTo(output.encoded().toHexString());
assertThat(output.encoded().toHexString()).isEqualTo(FRONTIER_TX_RLP);
}
@Test
public void encodeEIP1559TxNominalCase() {
void encodeEIP1559TxNominalCase() {
final Transaction transaction =
TransactionDecoder.decodeForWire(RLP.input(Bytes.fromHexString(EIP1559_TX_RLP)));
final BytesValueRLPOutput output = new BytesValueRLPOutput();
TransactionEncoder.encodeForWire(transaction, output);
assertThat(EIP1559_TX_RLP).isEqualTo(output.encoded().toHexString());
assertThat(output.encoded().toHexString()).isEqualTo(EIP1559_TX_RLP);
}
@Test
public void blockWithLegacyAndEIP2930TransactionsRoundTrips() {
void blockWithLegacyAndEIP2930TransactionsRoundTrips() {
final BlockDataGenerator gen = new BlockDataGenerator();
final Block block =
@ -78,4 +66,14 @@ public class TransactionEncoderTest {
RLP.input(RLP.encode(block::writeTo)), new MainnetBlockHeaderFunctions()))
.isEqualTo(block);
}
@Test
void shouldEncodeWithHighNonce() {
final Transaction transaction =
TransactionDecoder.decodeForWire(
RLP.input(Bytes.fromHexString(NONCE_64_BIT_MAX_MINUS_2_TX_RLP)));
final BytesValueRLPOutput output = new BytesValueRLPOutput();
TransactionEncoder.encodeForWire(transaction, output);
assertThat(output.encoded().toHexString()).isEqualTo(NONCE_64_BIT_MAX_MINUS_2_TX_RLP);
}
}

@ -20,6 +20,7 @@ import static org.assertj.core.api.Assertions.assertThat;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.config.JsonUtil;
import org.hyperledger.besu.config.StubGenesisConfigOptions;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.ethereum.core.BlockHeader;
@ -32,6 +33,7 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
@ -73,68 +75,83 @@ public class DifficultyCalculatorTests {
EvmConfiguration.DEFAULT)
},
new Object[] {
"/BasicTests/difficultyFrontier.json",
"/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierForkBlock.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\": {\"frontierBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
new StubGenesisConfigOptions().arrowGlacierBlock(13773000))
},
new Object[] {
"/BasicTests/difficultyHomestead.json",
"/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierTimeDiff1.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\": {\"homesteadBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
new StubGenesisConfigOptions().arrowGlacierBlock(13773000))
},
new Object[] {
"/BasicTests/difficultyByzantium.json",
"/DifficultyTests/dfArrowGlacier/difficultyArrowGlacierTimeDiff2.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\": {\"byzantiumBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
new StubGenesisConfigOptions().arrowGlacierBlock(13773000))
},
new Object[] {
"/BasicTests/difficultyConstantinople.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\": {\"constantinopleBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
"/DifficultyTests/dfByzantium/difficultyByzantium.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().byzantiumBlock(0))
},
new Object[] {
"/BasicTests/difficultyEIP2384.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\":{\"muirGlacierBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
"/DifficultyTests/dfConstantinople/difficultyConstantinople.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().constantinopleBlock(0))
},
new Object[] {
"/BasicTests/difficultyEIP2384_random.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\":{\"muirGlacierBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
"/DifficultyTests/dfEIP2384/difficultyEIP2384.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0))
},
new Object[] {
"/BasicTests/difficultyEIP2384_random_to20M.json",
MainnetProtocolSchedule.fromConfig(
GenesisConfigFile.fromConfig("{\"config\":{\"muirGlacierBlock\":0}}")
.getConfigOptions(),
EvmConfiguration.DEFAULT)
"/DifficultyTests/dfEIP2384/difficultyEIP2384_random.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0))
},
new Object[] {
"/DifficultyTests/dfEIP2384/difficultyEIP2384_random_to20M.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().muirGlacierBlock(0))
},
new Object[] {
"/DifficultyTests/dfFrontier/difficultyFrontier.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions())
},
new Object[] {
"/DifficultyTests/dfHomestead/difficultyHomestead.json",
MainnetProtocolSchedule.fromConfig(new StubGenesisConfigOptions().homesteadBlock(0))
});
}
@Test
public void testDifficultyCalculation() throws IOException {
MainnetBlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions();
final MainnetBlockHeaderFunctions blockHeaderFunctions = new MainnetBlockHeaderFunctions();
final ObjectNode testObject =
JsonUtil.objectNodeFromString(
Resources.toString(
DifficultyCalculatorTests.class.getResource(testFile), StandardCharsets.UTF_8));
if (testObject.size() == 1) {
final var topObjectIterator = testObject.fields();
while (topObjectIterator.hasNext()) {
final Map.Entry<String, JsonNode> testNameIterator = topObjectIterator.next();
final var testHolderIter = testNameIterator.getValue().fields();
while (testHolderIter.hasNext()) {
final var testList = testHolderIter.next();
if (!testList.getKey().equals("_info")) {
testDifficulty(blockHeaderFunctions, (ObjectNode) testList.getValue());
}
}
}
} else {
testDifficulty(blockHeaderFunctions, testObject);
}
}
private void testDifficulty(
final MainnetBlockHeaderFunctions blockHeaderFunctions, final ObjectNode testObject) {
final var fields = testObject.fields();
while (fields.hasNext()) {
final var entry = fields.next();
final JsonNode value = entry.getValue();
final long currentBlockNumber = extractLong(value, "currentBlockNumber");
String parentUncles = value.get("parentUncles").asText();
final BlockHeader testHeader =
BlockHeaderBuilder.create()
.parentHash(Hash.EMPTY)
@ -151,7 +168,10 @@ public class DifficultyCalculatorTests {
.blockHeaderFunctions(blockHeaderFunctions)
.timestamp(extractLong(value, "parentTimestamp"))
.difficulty(Difficulty.fromHexString(value.get("parentDifficulty").asText()))
.ommersHash(Hash.fromHexString(value.get("parentUncles").asText()))
.ommersHash(
parentUncles.equals("0x00")
? Hash.EMPTY_LIST_HASH
: Hash.fromHexStringLenient(parentUncles))
.number(currentBlockNumber)
.buildBlockHeader();
final long currentTime = extractLong(value, "currentTimestamp");

@ -282,39 +282,6 @@ public class MainnetTransactionValidatorTest {
.isEqualTo("max priority fee per gas cannot be greater than max fee per gas");
}
@Test
public void shouldRejectTransactionWhenEffectiveGasPriceIsTooLow() {
final MainnetTransactionValidator validator =
new MainnetTransactionValidator(
gasCalculator,
FeeMarket.london(0L),
false,
Optional.of(BigInteger.ONE),
Set.of(TransactionType.values()),
defaultGoQuorumCompatibilityMode);
validator.setTransactionFilter(transactionFilter(true));
final Transaction transaction =
Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(0)
.maxPriorityFeePerGas(Wei.ZERO)
.maxFeePerGas(Wei.of(4))
.gasLimit(15)
.to(Address.ZERO)
.value(Wei.of(0))
.payload(Bytes.EMPTY)
.chainId(BigInteger.ONE)
.signAndBuild(new SECP256K1().generateKeyPair());
final ValidationResult<TransactionInvalidReason> validationResult =
validator.validate(transaction, Optional.of(Wei.ONE), transactionValidationParams);
assertThat(validationResult).isEqualTo(ValidationResult.invalid(INVALID_TRANSACTION_FORMAT));
assertThat(validationResult.getErrorMessage())
.isEqualTo("effective gas price is too low to execute");
}
@Test
public void shouldPropagateCorrectStateChangeParamToTransactionFilter() {
final ArgumentCaptor<Boolean> stateChangeLocalParamCaptor =

@ -114,6 +114,16 @@ public class GeneralStateReferenceTestTools {
final WorldState initialWorldState = spec.getInitialWorldState();
final Transaction transaction = spec.getTransaction();
// Sometimes the tests ask us assemble an invalid transaction. If we have
// no valid transaction then there is no test. GeneralBlockChain tests
// will handle the case where we receive the TXs in a serialized form.
if (transaction == null) {
assertThat(spec.getExpectException())
.withFailMessage("Transaction was not assembled, but no exception was expected")
.isNotNull();
return;
}
final MutableWorldState worldState = new DefaultMutableWorldState(initialWorldState);
// Several of the GeneralStateTests check if the transaction could potentially
// consume more gas than is left for the block it's attempted to be included in.
@ -136,11 +146,13 @@ public class GeneralStateReferenceTestTools {
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock());
final Account coinbase = worldStateUpdater.getOrCreate(spec.getBlockHeader().getCoinbase());
if (coinbase != null && coinbase.isEmpty() && shouldClearEmptyAccounts(spec.getFork())) {
worldStateUpdater.deleteAccount(coinbase.getAddress());
if (!result.isInvalid()) {
final Account coinbase = worldStateUpdater.getOrCreate(spec.getBlockHeader().getCoinbase());
if (coinbase != null && coinbase.isEmpty() && shouldClearEmptyAccounts(spec.getFork())) {
worldStateUpdater.deleteAccount(coinbase.getAddress());
}
worldStateUpdater.commit();
}
worldStateUpdater.commit();
// Check the world state root hash.
final Hash expectedRootHash = spec.getExpectedRootHash();

@ -1,167 +0,0 @@
/*
* Copyright ConsenSys AG.
*
* 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.vm;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.Assume.assumeTrue;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecs;
import org.hyperledger.besu.ethereum.mainnet.MutableProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSpec;
import org.hyperledger.besu.ethereum.privacy.PrivateTransactionValidator;
import org.hyperledger.besu.ethereum.referencetests.EnvironmentInformation;
import org.hyperledger.besu.ethereum.referencetests.ReferenceTestBlockchain;
import org.hyperledger.besu.ethereum.referencetests.VMReferenceTestCaseSpec;
import org.hyperledger.besu.ethereum.worldstate.DefaultMutableWorldState;
import org.hyperledger.besu.evm.frame.MessageFrame;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.evm.tracing.OperationTracer;
import org.hyperledger.besu.testutil.JsonTestParameters;
import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Collection;
import java.util.Optional;
import java.util.OptionalInt;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
/** The VM operation testing framework entry point. */
@RunWith(Parameterized.class)
public class VMReferenceTest extends AbstractRetryingTest {
/** The path where all of the VM test configuration files live. */
private static final String[] TEST_CONFIG_FILE_DIR_PATHS = {
"LegacyTests/Constantinople/VMTests/vmArithmeticTest",
"LegacyTests/Constantinople/VMTests/vmBitwiseLogicOperation",
"LegacyTests/Constantinople/VMTests/vmBlockInfoTest",
"LegacyTests/Constantinople/VMTests/vmEnvironmentalInfo",
"LegacyTests/Constantinople/VMTests/vmIOandFlowOperations",
"LegacyTests/Constantinople/VMTests/vmLogTest",
"LegacyTests/Constantinople/VMTests/vmPushDupSwapTest",
"LegacyTests/Constantinople/VMTests/vmRandomTest",
"LegacyTests/Constantinople/VMTests/vmSha3Test",
"LegacyTests/Constantinople/VMTests/vmTests",
"LegacyTests/Constantinople/VMTests/vmSystemOperations"
};
// The ignored test cases fall into two categories:
//
// 1. Incorrect Test Cases: The VMTests have known bugs with accessing
// non-existent accounts. This corresponds to test cases involving
// the BALANCE, EXTCODESIZE, EXTCODECOPY, and SELFDESTRUCT operations.
//
// 2. Test Cases for CALL, CALLCODE, and CALLCREATE: The VMTests do not
// fully test these operations and the mocking does not add much value.
// Additionally, the GeneralStateTests provide coverage of these
// operations so the proper functionality does get tested somewhere.
private static final String[] IGNORED_TESTS = {
"push32AndSuicide", "suicide", "suicide0", "suicideNotExistingAccount", "suicideSendEtherToMe",
};
private static final Optional<BigInteger> CHAIN_ID = Optional.of(BigInteger.ONE);
private final VMReferenceTestCaseSpec spec;
@Parameters(name = "Name: {0}")
public static Collection<Object[]> getTestParametersForConfig() {
return JsonTestParameters.create(VMReferenceTestCaseSpec.class)
.ignore(IGNORED_TESTS)
.generate(TEST_CONFIG_FILE_DIR_PATHS);
}
public VMReferenceTest(
final String name, final VMReferenceTestCaseSpec spec, final boolean runTest) {
this.spec = spec;
assumeTrue("Test " + name + " was ignored", runTest);
}
@Override
protected void runTest() {
final MutableWorldState worldState = new DefaultMutableWorldState(spec.getInitialWorldState());
final EnvironmentInformation execEnv = spec.getExec();
final ProtocolSpec protocolSpec =
MainnetProtocolSpecs.frontierDefinition(
OptionalInt.empty(), OptionalInt.empty(), false, EvmConfiguration.DEFAULT)
.privacyParameters(PrivacyParameters.DEFAULT)
.privateTransactionValidatorBuilder(() -> new PrivateTransactionValidator(CHAIN_ID))
.badBlocksManager(new BadBlockManager())
.build(new MutableProtocolSchedule(CHAIN_ID));
final ReferenceTestBlockchain blockchain =
new ReferenceTestBlockchain(execEnv.getBlockHeader().getNumber());
final MessageFrame frame =
MessageFrame.builder()
.type(MessageFrame.Type.MESSAGE_CALL)
.messageFrameStack(new ArrayDeque<>())
.worldUpdater(worldState.updater())
.initialGas(spec.getExec().getGas())
.contract(execEnv.getAccountAddress())
.address(execEnv.getAccountAddress())
.originator(execEnv.getOriginAddress())
.gasPrice(execEnv.getGasPrice())
.inputData(execEnv.getData())
.sender(execEnv.getCallerAddress())
.value(execEnv.getValue())
.apparentValue(execEnv.getValue())
.code(execEnv.getCode())
.blockValues(execEnv.getBlockHeader())
.depth(execEnv.getDepth())
.completer(c -> {})
.miningBeneficiary(execEnv.getBlockHeader().getCoinbase())
.blockHashLookup(new BlockHashLookup(execEnv.getBlockHeader(), blockchain))
.maxStackSize(MessageFrame.DEFAULT_MAX_STACK_SIZE)
.build();
// This is normally set inside the containing message executing the code.
frame.setState(MessageFrame.State.CODE_EXECUTING);
protocolSpec.getEvm().runToHalt(frame, OperationTracer.NO_TRACING);
if (spec.isExceptionHaltExpected()) {
assertThat(frame.getState() == MessageFrame.State.EXCEPTIONAL_HALT)
.withFailMessage("VM should have exceptionally halted")
.isTrue();
} else {
// This is normally performed when the message processor executing the VM
// executes to completion successfully.
frame.getWorldUpdater().commit();
assertThat(frame.getState() == MessageFrame.State.EXCEPTIONAL_HALT)
.withFailMessage(
"VM should not have exceptionally halted with " + frame.getExceptionalHaltReason())
.isFalse();
assertThat(frame.getOutputData())
.withFailMessage("VM output differs")
.isEqualTo(spec.getOut());
assertThat(worldState.rootHash())
.withFailMessage("Final world state differs")
.isEqualTo(spec.getFinalWorldState().rootHash());
final long actualGas = frame.getRemainingGas();
final long expectedGas = spec.getFinalGas();
final long difference =
(expectedGas > actualGas) ? expectedGas - actualGas : actualGas - expectedGas;
assertThat(actualGas)
.withFailMessage("Final gas does not match, with difference of %s", difference)
.isEqualTo(expectedGas);
}
}
}

@ -44,6 +44,7 @@ import java.util.List;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.units.bigints.UInt256;
import org.jetbrains.annotations.NotNull;
import org.junit.Test;
public class CreateOperationTest {
@ -74,39 +75,14 @@ public class CreateOperationTest {
@Test
public void createFromMemoryMutationSafe() {
// Given: Execute a CREATE operatio with a contrat that logs in the constructor
// Given: Execute a CREATE operation with a contract that logs in the constructor
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
when(account.getMutable()).thenReturn(mutableAccount);
ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
MessageFrame messageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.CONTRACT_CREATION)
.contract(Address.ZERO)
.inputData(Bytes.EMPTY)
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.createLegacyCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.depth(1)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(mock(BlockHashLookup.class))
.blockValues(mock(ProcessableBlockHeader.class))
.gasPrice(Wei.ZERO)
.messageFrameStack(messageFrameStack)
.miningBeneficiary(Address.ZERO)
.originator(Address.ZERO)
.initialGas(100000L)
.worldUpdater(worldUpdater)
.build();
messageFrame.pushStackItem(memoryLength);
messageFrame.pushStackItem(memoryOffset);
messageFrame.pushStackItem(UInt256.ZERO);
messageFrame.expandMemory(0, 500);
messageFrame.writeMemory(
memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);
when(account.getMutable()).thenReturn(mutableAccount);
when(account.getNonce()).thenReturn(55L);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(worldUpdater.getAccount(any())).thenReturn(account);
@ -119,13 +95,13 @@ public class CreateOperationTest {
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
MessageFrame createFrame = messageFrameStack.peek();
ContractCreationProcessor ccp =
final MessageFrame createFrame = messageFrameStack.peek();
final ContractCreationProcessor ccp =
new ContractCreationProcessor(evm.getGasCalculator(), evm, false, List.of(), 0, List.of());
ccp.process(createFrame, OperationTracer.NO_TRACING);
Log log = createFrame.getLogs().get(0);
String calculatedTopic = log.getTopics().get(0).toUnprefixedHexString();
final Log log = createFrame.getLogs().get(0);
final String calculatedTopic = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopic).isEqualTo(TOPIC);
// WHEN the memory that the create operation was executed from is altered.
@ -135,7 +111,104 @@ public class CreateOperationTest {
Bytes.random(SIMPLE_CREATE.size()));
// THEN the logs still have the expected topic
String calculatedTopicAfter = log.getTopics().get(0).toUnprefixedHexString();
final String calculatedTopicAfter = log.getTopics().get(0).toUnprefixedHexString();
assertThat(calculatedTopicAfter).isEqualTo(TOPIC);
}
@Test
public void nonceTooLarge() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1, messageFrameStack);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(account.getMutable()).thenReturn(mutableAccount);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(mutableAccount.getNonce()).thenReturn(-1L);
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}
@Test
public void messageFrameStackTooDeep() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.ZERO, 1025, messageFrameStack);
when(worldUpdater.getAccount(any())).thenReturn(account);
when(account.getMutable()).thenReturn(mutableAccount);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(mutableAccount.getNonce()).thenReturn(55L);
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}
@Test
public void notEnoughValue() {
final UInt256 memoryOffset = UInt256.fromHexString("0xFF");
final UInt256 memoryLength = UInt256.valueOf(SIMPLE_CREATE.size());
final ArrayDeque<MessageFrame> messageFrameStack = new ArrayDeque<>();
final MessageFrame messageFrame =
testMemoryFrame(memoryOffset, memoryLength, UInt256.valueOf(1), 1, messageFrameStack);
for (int i = 0; i < 1025; i++) {
messageFrameStack.add(messageFrame);
}
when(worldUpdater.getAccount(any())).thenReturn(account);
when(account.getMutable()).thenReturn(mutableAccount);
when(mutableAccount.getBalance()).thenReturn(Wei.ZERO);
when(mutableAccount.getNonce()).thenReturn(55L);
final EVM evm = MainnetEVMs.london(EvmConfiguration.DEFAULT);
operation.execute(messageFrame, evm);
assertThat(messageFrame.getStackItem(0)).isEqualTo(UInt256.ZERO);
}
@NotNull
private MessageFrame testMemoryFrame(
final UInt256 memoryOffset,
final UInt256 memoryLength,
final UInt256 value,
final int depth,
final ArrayDeque<MessageFrame> messageFrameStack) {
final MessageFrame messageFrame =
MessageFrame.builder()
.type(MessageFrame.Type.CONTRACT_CREATION)
.contract(Address.ZERO)
.inputData(Bytes.EMPTY)
.sender(Address.fromHexString(SENDER))
.value(Wei.ZERO)
.apparentValue(Wei.ZERO)
.code(Code.createLegacyCode(SIMPLE_CREATE, Hash.hash(SIMPLE_CREATE)))
.depth(depth)
.completer(__ -> {})
.address(Address.fromHexString(SENDER))
.blockHashLookup(mock(BlockHashLookup.class))
.blockValues(mock(ProcessableBlockHeader.class))
.gasPrice(Wei.ZERO)
.messageFrameStack(messageFrameStack)
.miningBeneficiary(Address.ZERO)
.originator(Address.ZERO)
.initialGas(100000L)
.worldUpdater(worldUpdater)
.build();
messageFrame.pushStackItem(memoryLength);
messageFrame.pushStackItem(memoryOffset);
messageFrame.pushStackItem(value);
messageFrame.expandMemory(0, 500);
messageFrame.writeMemory(
memoryOffset.trimLeadingZeros().toInt(), SIMPLE_CREATE.size(), SIMPLE_CREATE);
return messageFrame;
}
}

@ -46,7 +46,9 @@ import org.hyperledger.besu.util.Log4j2ConfiguratorUtil;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@ -77,6 +79,8 @@ public class StateTestSubCommand implements Runnable {
private static final Logger LOG = LoggerFactory.getLogger(StateTestSubCommand.class);
public static final String COMMAND_NAME = "state-test";
private final InputStream input;
private final PrintStream output;
@SuppressWarnings({"FieldCanBeFinal", "FieldMayBeFinal"})
@Option(
@ -84,7 +88,7 @@ public class StateTestSubCommand implements Runnable {
description = "Force the state tests to run on a specific fork.")
private String fork = null;
@ParentCommand private EvmToolCommand parentCommand;
@ParentCommand private final EvmToolCommand parentCommand;
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection") // picocli does it magically
@Parameters
@ -92,10 +96,19 @@ public class StateTestSubCommand implements Runnable {
private final ObjectMapper objectMapper = new ObjectMapper();
public StateTestSubCommand() {}
public StateTestSubCommand() {
this(null, System.in, System.out);
}
public StateTestSubCommand(final EvmToolCommand parentCommand) {
this(parentCommand, System.in, System.out);
}
StateTestSubCommand(
final EvmToolCommand parentCommand, final InputStream input, final PrintStream output) {
this.parentCommand = parentCommand;
this.input = input;
this.output = output;
}
@Override
@ -110,7 +123,7 @@ public class StateTestSubCommand implements Runnable {
if (stateTestFiles.isEmpty()) {
// if no state tests were specified use standard input to get filenames
final BufferedReader in =
new BufferedReader(new InputStreamReader(System.in, StandardCharsets.UTF_8));
new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8));
while (true) {
final String fileName = in.readLine();
if (fileName == null) {
@ -124,10 +137,10 @@ public class StateTestSubCommand implements Runnable {
objectMapper.readValue(file, javaType);
executeStateTest(generalStateTests);
} catch (final JsonProcessingException jpe) {
System.out.println("File content error :" + jpe);
output.println("File content error: " + jpe);
}
} else {
System.out.println("File not found:" + fileName);
output.println("File not found: " + fileName);
}
}
} else {
@ -160,7 +173,7 @@ public class StateTestSubCommand implements Runnable {
final OperationTracer tracer = // You should have picked Mercy.
parentCommand.showJsonResults
? new StandardJsonTracer(System.out, !parentCommand.noMemory)
? new StandardJsonTracer(output, !parentCommand.noMemory)
: OperationTracer.NO_TRACING;
for (final GeneralStateTestCaseEipSpec spec : specs) {
@ -169,76 +182,95 @@ public class StateTestSubCommand implements Runnable {
final WorldState initialWorldState = spec.getInitialWorldState();
final Transaction transaction = spec.getTransaction();
final MutableWorldState worldState = new DefaultMutableWorldState(initialWorldState);
// Several of the GeneralStateTests check if the transaction could potentially
// consume more gas than is left for the block it's attempted to be included in.
// This check is performed within the `BlockImporter` rather than inside the
// `TransactionProcessor`, so these tests are skipped.
if (transaction.getGasLimit() > blockHeader.getGasLimit() - blockHeader.getGasUsed()) {
return;
}
final String forkName = fork == null ? spec.getFork() : fork;
final ProtocolSchedule protocolSchedule = referenceTestProtocolSchedules.getByName(forkName);
if (protocolSchedule == null) {
throw new UnsupportedForkException(forkName);
}
final ObjectNode summaryLine = objectMapper.createObjectNode();
if (transaction == null) {
// Check the world state root hash.
summaryLine.put("test", test);
summaryLine.put("fork", spec.getFork());
summaryLine.put("d", spec.getDataIndex());
summaryLine.put("g", spec.getGasIndex());
summaryLine.put("v", spec.getValueIndex());
summaryLine.put("pass", spec.getExpectException() != null);
summaryLine.put("validationError", "Transaction had out-of-bounds parameters");
} else {
final MutableWorldState worldState = new DefaultMutableWorldState(initialWorldState);
// Several of the GeneralStateTests check if the transaction could potentially
// consume more gas than is left for the block it's attempted to be included in.
// This check is performed within the `BlockImporter` rather than inside the
// `TransactionProcessor`, so these tests are skipped.
if (transaction.getGasLimit() > blockHeader.getGasLimit() - blockHeader.getGasUsed()) {
return;
}
final MainnetTransactionProcessor processor =
protocolSchedule.getByBlockNumber(0).getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain =
new ReferenceTestBlockchain(blockHeader.getNumber());
final Stopwatch timer = Stopwatch.createStarted();
final TransactionProcessingResult result =
processor.processTransaction(
blockchain,
worldStateUpdater,
blockHeader,
transaction,
blockHeader.getCoinbase(),
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock(),
tracer);
timer.stop();
if (shouldClearEmptyAccounts(spec.getFork())) {
final Account coinbase = worldStateUpdater.getOrCreate(spec.getBlockHeader().getCoinbase());
if (coinbase != null && coinbase.isEmpty()) {
worldStateUpdater.deleteAccount(coinbase.getAddress());
final String forkName = fork == null ? spec.getFork() : fork;
final ProtocolSchedule protocolSchedule =
referenceTestProtocolSchedules.getByName(forkName);
if (protocolSchedule == null) {
throw new UnsupportedForkException(forkName);
}
final Account sender = worldStateUpdater.getAccount(transaction.getSender());
if (sender != null && sender.isEmpty()) {
worldStateUpdater.deleteAccount(sender.getAddress());
final MainnetTransactionProcessor processor =
protocolSchedule.getByBlockNumber(0).getTransactionProcessor();
final WorldUpdater worldStateUpdater = worldState.updater();
final ReferenceTestBlockchain blockchain =
new ReferenceTestBlockchain(blockHeader.getNumber());
final Stopwatch timer = Stopwatch.createStarted();
final TransactionProcessingResult result =
processor.processTransaction(
blockchain,
worldStateUpdater,
blockHeader,
transaction,
blockHeader.getCoinbase(),
new BlockHashLookup(blockHeader, blockchain),
false,
TransactionValidationParams.processingBlock(),
tracer);
timer.stop();
if (shouldClearEmptyAccounts(spec.getFork())) {
final Account coinbase =
worldStateUpdater.getOrCreate(spec.getBlockHeader().getCoinbase());
if (coinbase != null && coinbase.isEmpty()) {
worldStateUpdater.deleteAccount(coinbase.getAddress());
}
final Account sender = worldStateUpdater.getAccount(transaction.getSender());
if (sender != null && sender.isEmpty()) {
worldStateUpdater.deleteAccount(sender.getAddress());
}
}
}
worldStateUpdater.commit();
worldStateUpdater.commit();
final ObjectNode summaryLine = objectMapper.createObjectNode();
summaryLine.put("output", result.getOutput().toUnprefixedHexString());
UInt256 gasUsed = UInt256.valueOf(transaction.getGasLimit() - result.getGasRemaining());
summaryLine.put("gasUsed", StandardJsonTracer.shortNumber(gasUsed));
summaryLine.put("time", timer.elapsed(TimeUnit.NANOSECONDS));
// Check the world state root hash.
summaryLine.put("test", test);
summaryLine.put("fork", spec.getFork());
summaryLine.put("d", spec.getDataIndex());
summaryLine.put("g", spec.getGasIndex());
summaryLine.put("v", spec.getValueIndex());
summaryLine.put("postHash", worldState.rootHash().toHexString());
final List<Log> logs = result.getLogs();
final Hash actualLogsHash = Hash.hash(RLP.encode(out -> out.writeList(logs, Log::writeTo)));
summaryLine.put("postLogsHash", actualLogsHash.toHexString());
summaryLine.put(
"pass",
worldState.rootHash().equals(spec.getExpectedRootHash())
&& actualLogsHash.equals(spec.getExpectedLogsHash()));
if (result.isInvalid()) {
summaryLine.put("validationError", result.getValidationResult().getErrorMessage());
summaryLine.put("output", result.getOutput().toUnprefixedHexString());
final UInt256 gasUsed =
UInt256.valueOf(transaction.getGasLimit() - result.getGasRemaining());
summaryLine.put("gasUsed", StandardJsonTracer.shortNumber(gasUsed));
summaryLine.put("time", timer.elapsed(TimeUnit.NANOSECONDS));
// Check the world state root hash.
summaryLine.put("test", test);
summaryLine.put("fork", spec.getFork());
summaryLine.put("d", spec.getDataIndex());
summaryLine.put("g", spec.getGasIndex());
summaryLine.put("v", spec.getValueIndex());
summaryLine.put("postHash", worldState.rootHash().toHexString());
final List<Log> logs = result.getLogs();
final Hash actualLogsHash = Hash.hash(RLP.encode(out -> out.writeList(logs, Log::writeTo)));
summaryLine.put("postLogsHash", actualLogsHash.toHexString());
summaryLine.put(
"pass",
spec.getExpectException() == null
&& worldState.rootHash().equals(spec.getExpectedRootHash())
&& actualLogsHash.equals(spec.getExpectedLogsHash()));
if (result.isInvalid()) {
summaryLine.put("validationError", result.getValidationResult().getErrorMessage());
} else if (spec.getExpectException() != null) {
summaryLine.put(
"validationError",
"Exception '" + spec.getExpectException() + "' was expected but did not occur");
}
}
System.out.println(summaryLine);
output.println(summaryLine);
}
}
}

@ -14,10 +14,17 @@
*/
package org.hyperledger.besu.evmtool;
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import org.hyperledger.besu.evmtool.exception.UnsupportedForkException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import org.junit.Test;
import picocli.CommandLine;
@ -49,4 +56,71 @@ public class StateTestSubCommandTest {
cmd.parseArgs(StateTestSubCommandTest.class.getResource("access-list.json").getPath());
stateTestSubCommand.run();
}
@Test
public void noJsonTracer() {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
var parentCommand = new EvmToolCommand();
CommandLine parentCmd = new CommandLine(parentCommand);
parentCmd.parseArgs("--json=false");
final StateTestSubCommand stateTestSubCommand =
new StateTestSubCommand(parentCommand, System.in, new PrintStream(baos));
final CommandLine cmd = new CommandLine(stateTestSubCommand);
cmd.parseArgs(StateTestSubCommandTest.class.getResource("access-list.json").getPath());
stateTestSubCommand.run();
assertThat(baos.toString(UTF_8)).doesNotContain("\"pc\"");
}
@Test
public void testsInvalidTransactions() {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayInputStream bais =
new ByteArrayInputStream(
StateTestSubCommandTest.class
.getResource("HighGasPrice.json")
.getPath()
.getBytes(UTF_8));
final StateTestSubCommand stateTestSubCommand =
new StateTestSubCommand(new EvmToolCommand(), bais, new PrintStream(baos));
stateTestSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("Transaction had out-of-bounds parameters");
}
@Test
public void shouldStreamTests() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayInputStream bais =
new ByteArrayInputStream(
StateTestSubCommandTest.class
.getResource("access-list.json")
.getPath()
.getBytes(UTF_8));
final StateTestSubCommand stateTestSubCommand =
new StateTestSubCommand(new EvmToolCommand(), bais, new PrintStream(baos));
stateTestSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("\"pass\":true");
}
@Test
public void failStreamMissingFile() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayInputStream bais =
new ByteArrayInputStream("./file-dose-not-exist.json".getBytes(UTF_8));
final StateTestSubCommand stateTestSubCommand =
new StateTestSubCommand(new EvmToolCommand(), bais, new PrintStream(baos));
stateTestSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("File not found: ./file-dose-not-exist.json");
}
@Test
public void failStreamBadFile() throws IOException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final ByteArrayInputStream bais =
new ByteArrayInputStream(
StateTestSubCommandTest.class.getResource("bogus-test.json").getPath().getBytes(UTF_8));
final StateTestSubCommand stateTestSubCommand =
new StateTestSubCommand(new EvmToolCommand(), bais, new PrintStream(baos));
stateTestSubCommand.run();
assertThat(baos.toString(UTF_8)).contains("File content error: ");
}
}

@ -0,0 +1,187 @@
{
"HighGasPrice" : {
"_info" : {
"comment" : "",
"filling-rpc-server" : "evm version 1.10.16-unstable-0f893109-20220105",
"filling-tool-version" : "retesteth-0.2.2-testinfo+commit.01c6aac2.Linux.g++",
"generatedTestHash" : "399ed11fc62af31e9c3dc126828160c15b9c2422373ef7f68a628017a9693a63",
"lllcversion" : "Version: 0.5.14-develop.2021.11.27+commit.401d5358.Linux.g++",
"solidity" : "Version: 0.8.5+commit.a4f2e591.Linux.g++",
"source" : "src/GeneralStateTestsFiller/stTransactionTest/HighGasPriceFiller.yml",
"sourceHash" : "b3c0dfeeca0bb9616669d0bdd0996e24c71febd2579d14e9ec42914e9f096a44"
},
"env" : {
"currentBaseFee" : "0x0a",
"currentCoinbase" : "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
"currentDifficulty" : "0x020000",
"currentGasLimit" : "0x05500000",
"currentNumber" : "0x01",
"currentTimestamp" : "0x03e8",
"previousHash" : "0x5e20a0453cecd065ea59c37ac63e079ee08998b6045136a8ce6635c7912ec0b6"
},
"post" : {
"Berlin" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"Byzantium" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"Constantinople" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"ConstantinopleFix" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"EIP150" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"EIP158" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"Frontier" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"Homestead" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"Istanbul" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
],
"London" : [
{
"expectException" : "TR_NoFunds",
"hash" : "0x1751725d1aad5298768fbcf64069b2c1b85aeaffcc561146067d6beedd08052a",
"indexes" : {
"data" : 0,
"gas" : 0,
"value" : 0
},
"logs" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"txbytes" : "0xf87e809f031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c82520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d001801ca039b081ab7094dff1b3ac79cbf8e381adc9f7a4e16290d7abc42dd006e5e062c5a033af00e26e5eb4431dcad601b2c8bf12d51eef2bd037d14681f22692ffab1ccd"
}
]
},
"pre" : {
"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b" : {
"balance" : "0x3b9aca00",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
},
"0xd0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0" : {
"balance" : "0x00",
"code" : "0x",
"nonce" : "0x00",
"storage" : {
}
}
},
"transaction" : {
"data" : [
"0x"
],
"gasLimit" : [
"0x5208"
],
"gasPrice" : "0x031eea408f8e1799cb883da2927b1336521d73c2c14accfebb70d5c5af006c",
"nonce" : "0x00",
"secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
"sender" : "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
"to" : "0xd0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0",
"value" : [
"0x01"
]
}
}
}

@ -43,7 +43,7 @@ task ('validateReferenceTestSubmodule') {
description = "Checks that the reference tests submodule is not accidentally changed"
doLast {
def result = new ByteArrayOutputStream()
def expectedHash = '1587f40e7f606140822307d47e88c8d09acc907a'
def expectedHash = 'a380655e5ffab1a5ea0f4d860224bdb19013f06a'
def submodulePath = java.nio.file.Path.of("${rootProject.projectDir}", "ethereum/referencetests/src/test/resources").toAbsolutePath()
try {
exec {

@ -49,6 +49,7 @@ public class GeneralStateTestCaseEipSpec {
private final int dataIndex;
private final int gasIndex;
private final int valueIndex;
private final String expectException;
GeneralStateTestCaseEipSpec(
final String fork,
@ -59,7 +60,8 @@ public class GeneralStateTestCaseEipSpec {
final BlockHeader blockHeader,
final int dataIndex,
final int gasIndex,
final int valueIndex) {
final int valueIndex,
final String expectException) {
this.fork = fork;
this.transactionSupplier = transactionSupplier;
this.initialWorldState = initialWorldState;
@ -69,6 +71,7 @@ public class GeneralStateTestCaseEipSpec {
this.dataIndex = dataIndex;
this.gasIndex = gasIndex;
this.valueIndex = valueIndex;
this.expectException = expectException;
}
public String getFork() {
@ -88,7 +91,15 @@ public class GeneralStateTestCaseEipSpec {
}
public Transaction getTransaction() {
return transactionSupplier.get();
try {
return transactionSupplier.get();
} catch (RuntimeException re) {
// some tests specify invalid transactions. We throw exceptions in
// GeneralStateTests but they are encoded in BlockchainTests, so we
// can skip them as invalid (since the point of the tests is to reject
// invalid transactions).
return null;
}
}
public BlockHeader getBlockHeader() {
@ -106,4 +117,8 @@ public class GeneralStateTestCaseEipSpec {
public int getValueIndex() {
return valueIndex;
}
public String getExpectException() {
return expectException;
}
}

@ -89,7 +89,8 @@ public class GeneralStateTestCaseSpec {
safeHeader,
p.indexes.data,
p.indexes.gas,
p.indexes.value));
p.indexes.value,
p.expectException));
}
res.put(eip, specs);
}
@ -142,16 +143,19 @@ public class GeneralStateTestCaseSpec {
private final Hash rootHash;
@Nullable private final Hash logsHash;
private final Indexes indexes;
private final String expectException;
@JsonCreator
public PostSection(
@JsonProperty("expectException") final String expectException,
@JsonProperty("hash") final String hash,
@JsonProperty("logs") final String logs,
@JsonProperty("indexes") final Indexes indexes,
@JsonProperty("logs") final String logs,
@JsonProperty("txbytes") final String txbytes) {
this.rootHash = Hash.fromHexString(hash);
this.logsHash = Optional.ofNullable(logs).map(Hash::fromHexString).orElse(null);
this.indexes = indexes;
this.expectException = expectException;
}
}
}

@ -56,7 +56,7 @@ public class ReferenceTestWorldState extends DefaultMutableWorldState {
@JsonProperty("balance") final String balance,
@JsonProperty("storage") final Map<String, String> storage,
@JsonProperty("code") final String code) {
this.nonce = Long.decode(nonce);
this.nonce = Long.parseUnsignedLong(nonce.startsWith("0x") ? nonce.substring(2) : nonce, 16);
this.balance = Wei.fromHexString(balance);
this.code = Bytes.fromHexString(code);
this.storage = parseStorage(storage);

@ -36,6 +36,7 @@ import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
/**
* Represents the "transaction" part of the JSON of a general state tests.
@ -111,7 +112,7 @@ public class StateTestVersionedTransaction {
signatureAlgorithm.createKeyPair(
signatureAlgorithm.createPrivateKey(Bytes32.fromHexString(secretKey)));
this.gasLimits = parseArray(gasLimit, Long::decode);
this.gasLimits = parseArray(gasLimit, s -> UInt256.fromHexString(s).toLong());
this.values = parseArray(value, Wei::fromHexString);
this.payloads = parseArray(data, Bytes::fromHexString);
this.maybeAccessLists = Optional.ofNullable(maybeAccessLists);
@ -120,20 +121,27 @@ public class StateTestVersionedTransaction {
private static <T> List<T> parseArray(final String[] array, final Function<String, T> parseFct) {
final List<T> res = new ArrayList<>(array.length);
for (final String str : array) {
res.add(parseFct.apply(str));
try {
res.add(parseFct.apply(str));
} catch (RuntimeException re) {
// the reference tests may be testing a boundary violation
res.add(null);
}
}
return res;
}
public Transaction get(final GeneralStateTestCaseSpec.Indexes indexes) {
Long gasLimit = gasLimits.get(indexes.gas);
Wei value = values.get(indexes.value);
Bytes data = payloads.get(indexes.data);
if (value == null || gasLimit == null) {
// this means one of the params is an out-of-bounds value. Don't generate the transaction.
return null;
}
final Transaction.Builder transactionBuilder =
Transaction.builder()
.nonce(nonce)
.gasLimit(gasLimits.get(indexes.gas))
.to(to)
.value(values.get(indexes.value))
.payload(payloads.get(indexes.data));
Transaction.builder().nonce(nonce).gasLimit(gasLimit).to(to).value(value).payload(data);
Optional.ofNullable(gasPrice).ifPresent(transactionBuilder::gasPrice);
Optional.ofNullable(maxFeePerGas).ifPresent(transactionBuilder::maxFeePerGas);

@ -1 +1 @@
Subproject commit 1587f40e7f606140822307d47e88c8d09acc907a
Subproject commit a380655e5ffab1a5ea0f4d860224bdb19013f06a

@ -80,9 +80,7 @@ public class RetestethService {
new EthGetBlockByHash(retestethContext::getBlockchainQueries, blockResult, true),
new EthGetCode(retestethContext::getBlockchainQueries, Optional.empty()),
new EthGetTransactionCount(
retestethContext::getBlockchainQueries,
retestethContext::getPendingTransactions,
false),
retestethContext::getBlockchainQueries, retestethContext::getPendingTransactions),
new DebugStorageRangeAt(
retestethContext::getBlockchainQueries, retestethContext::getBlockReplay, true),
new TestModifyTimestamp(retestethContext),

@ -288,16 +288,18 @@ abstract class AbstractRLPInput implements RLPInput {
@Override
public long readLongScalar() {
checkScalar("long scalar", 8);
long res = readGenericLongScalar();
setTo(nextItem());
return res;
}
private long readGenericLongScalar() {
long res = 0;
int shift = 0;
for (int i = 0; i < currentPayloadSize; i++) {
res |= ((long) payloadByte(currentPayloadSize - i - 1) & 0xFF) << shift;
shift += 8;
}
if (res < 0) {
error("long scalar %s is not non-negative", res);
}
setTo(nextItem());
return res;
}

@ -136,7 +136,8 @@ public interface RLPInput {
void leaveListLenient();
/**
* Reads a non-negative scalar from the input and return is as a long value.
* Reads a non-negative scalar from the input and return it as a long value which is interpreted
* as an unsigned long.
*
* @return The next scalar item of this input as a long value.
* @throws RLPException if the next item to read is a list, the input is at the end of its current

@ -14,8 +14,6 @@
*/
package org.hyperledger.besu.ethereum.rlp;
import static com.google.common.base.Preconditions.checkArgument;
import java.math.BigInteger;
import java.net.InetAddress;
import java.util.function.BiConsumer;
@ -104,10 +102,8 @@ public interface RLPOutput {
* Writes a scalar (encoded with no leading zeroes).
*
* @param v The scalar to write.
* @throws IllegalArgumentException if {@code v < 0}.
*/
default void writeLongScalar(final long v) {
checkArgument(v >= 0, "Invalid negative value %s for scalar encoding", v);
writeBytes(Bytes.minimalBytes(v));
}
@ -115,17 +111,15 @@ public interface RLPOutput {
* Writes a scalar (encoded with no leading zeroes).
*
* @param v The scalar to write.
* @throws IllegalArgumentException if {@code v} is a negative integer ({@code v.signum() < 0}).
*/
default void writeBigIntegerScalar(final BigInteger v) {
checkArgument(v.signum() >= 0, "Invalid negative integer %s for scalar encoding", v);
if (v.equals(BigInteger.ZERO)) {
writeBytes(Bytes.EMPTY);
return;
}
final byte[] bytes = v.toByteArray();
// BigInteger will not include leading zeros by contract, but it always include at least one
// BigInteger will not include leading zeros by contract, but it always includes at least one
// bit of sign (a zero here since it's positive). What that mean is that if the first 1 of the
// resulting number is exactly on a byte boundary, then the sign bit constraint will make the
// value include one extra byte, which will be zero. In other words, they can be one zero bytes

@ -161,15 +161,6 @@ public class BytesValueRLPInputTest {
assertLongScalar(1024L, h("0x820400"));
}
@Test
public void longScalar_NegativeLong() {
Bytes bytes = h("0x88FFFFFFFFFFFFFFFF");
final RLPInput in = RLP.input(bytes);
assertThatThrownBy(in::readLongScalar)
.isInstanceOf(RLPException.class)
.hasMessageStartingWith("long scalar -1 is not non-negative");
}
private void assertLongScalar(final long expected, final Bytes toTest) {
final RLPInput in = RLP.input(toTest);
assertThat(in.isDone()).isFalse();

@ -27,6 +27,7 @@ import org.hyperledger.besu.datatypes.Wei;
public interface Account extends AccountState {
long DEFAULT_NONCE = 0L;
long MAX_NONCE = -1; // per twos compliment rules -1 will be the unsigned max number
Wei DEFAULT_BALANCE = Wei.ZERO;
/**

@ -50,7 +50,7 @@ public interface MutableAccount extends Account {
*/
default Wei incrementBalance(final Wei value) {
final Wei current = getBalance();
setBalance(current.add(value));
setBalance(current.addExact(value));
return current;
}

@ -70,7 +70,9 @@ public abstract class AbstractCreateOperation extends AbstractOperation {
frame.clearReturnData();
if (value.compareTo(account.getBalance()) > 0 || frame.getMessageStackDepth() >= 1024) {
if (value.compareTo(account.getBalance()) > 0
|| frame.getMessageStackDepth() >= 1024
|| account.getNonce() == -1) {
fail(frame);
} else {
spawnChildMessage(frame, evm);

@ -159,6 +159,10 @@ dependencyManagement {
dependency 'org.junit.jupiter:junit-jupiter:5.8.2'
dependency 'org.junit.jupiter:junit-jupiter-api:5.8.2'
dependency 'org.junit.jupiter:junit-jupiter-params:5.8.2'
dependency 'org.junit.platform:junit-platform-runner:1.8.2'
dependency 'org.junit.vintage:junit-vintage-engine:5.8.2'
dependency 'org.mockito:mockito-core:4.4.0'

Loading…
Cancel
Save