Add integration tests on block processing (#7378)

Add a block processing integration test to verify that sequential and parallel execution have the same behaviour

Signed-off-by: Ameziane H <ameziane.hamlat@consensys.net>
Co-authored-by: Simon Dudley <simon.dudley@consensys.net>
pull/7418/head
ahamlat 4 months ago committed by GitHub
parent b727c958bf
commit 9cb8b06e4b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 23
      ethereum/core/src/test-support/java/org/hyperledger/besu/ethereum/core/ExecutionContextTestFixture.java
  2. 794
      ethereum/core/src/test/java/org/hyperledger/besu/ethereum/mainnet/AbstractBlockProcessorIntegrationTest.java
  3. 56
      ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/mainnet/contractUsedInBlockProcessingIT.sol
  4. 48
      ethereum/core/src/test/resources/org/hyperledger/besu/ethereum/mainnet/genesis-bp-it.json

@ -14,6 +14,7 @@
*/
package org.hyperledger.besu.ethereum.core;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createBonsaiInMemoryWorldStateArchive;
import static org.hyperledger.besu.ethereum.core.InMemoryKeyValueStorageProvider.createInMemoryWorldStateArchive;
import org.hyperledger.besu.config.GenesisConfigFile;
@ -31,10 +32,12 @@ import org.hyperledger.besu.ethereum.storage.keyvalue.VariablesKeyValueStorage;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.evm.internal.EvmConfiguration;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import org.hyperledger.besu.plugin.services.storage.KeyValueStorage;
import org.hyperledger.besu.services.kvstore.InMemoryKeyValueStorage;
import java.math.BigInteger;
import java.util.Optional;
import java.util.function.Function;
public class ExecutionContextTestFixture {
@ -52,7 +55,8 @@ public class ExecutionContextTestFixture {
final GenesisConfigFile genesisConfigFile,
final ProtocolSchedule protocolSchedule,
final KeyValueStorage blockchainKeyValueStorage,
final KeyValueStorage variablesKeyValueStorage) {
final KeyValueStorage variablesKeyValueStorage,
final Optional<DataStorageFormat> dataStorageFormat) {
final GenesisState genesisState = GenesisState.fromConfig(genesisConfigFile, protocolSchedule);
this.genesis = genesisState.getBlock();
this.blockchainKeyValueStorage = blockchainKeyValueStorage;
@ -67,7 +71,9 @@ public class ExecutionContextTestFixture {
false),
new NoOpMetricsSystem(),
0);
this.stateArchive = createInMemoryWorldStateArchive();
if (dataStorageFormat.isPresent() && dataStorageFormat.get().equals(DataStorageFormat.BONSAI))
this.stateArchive = createBonsaiInMemoryWorldStateArchive(blockchain);
else this.stateArchive = createInMemoryWorldStateArchive();
this.protocolSchedule = protocolSchedule;
this.protocolContext =
new ProtocolContext(blockchain, stateArchive, null, new BadBlockManager());
@ -115,6 +121,7 @@ public class ExecutionContextTestFixture {
private KeyValueStorage variablesKeyValueStorage;
private KeyValueStorage blockchainKeyValueStorage;
private ProtocolSchedule protocolSchedule;
private Optional<DataStorageFormat> dataStorageFormat = Optional.empty();
public Builder(final GenesisConfigFile genesisConfigFile) {
this.genesisConfigFile = genesisConfigFile;
@ -135,6 +142,11 @@ public class ExecutionContextTestFixture {
return this;
}
public Builder dataStorageFormat(final DataStorageFormat dataStorageFormat) {
this.dataStorageFormat = Optional.of(dataStorageFormat);
return this;
}
public ExecutionContextTestFixture build() {
if (protocolSchedule == null) {
protocolSchedule =
@ -157,8 +169,13 @@ public class ExecutionContextTestFixture {
if (variablesKeyValueStorage == null) {
variablesKeyValueStorage = new InMemoryKeyValueStorage();
}
return new ExecutionContextTestFixture(
genesisConfigFile, protocolSchedule, variablesKeyValueStorage, blockchainKeyValueStorage);
genesisConfigFile,
protocolSchedule,
variablesKeyValueStorage,
blockchainKeyValueStorage,
dataStorageFormat);
}
}
}

@ -0,0 +1,794 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
package org.hyperledger.besu.ethereum.mainnet;
import static org.assertj.core.api.Assertions.assertThat;
import static org.junit.jupiter.api.Assertions.assertTrue;
import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECPPrivateKey;
import org.hyperledger.besu.crypto.SignatureAlgorithm;
import org.hyperledger.besu.crypto.SignatureAlgorithmFactory;
import org.hyperledger.besu.datatypes.Address;
import org.hyperledger.besu.datatypes.Hash;
import org.hyperledger.besu.datatypes.TransactionType;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.BlockProcessingResult;
import org.hyperledger.besu.ethereum.chain.DefaultBlockchain;
import org.hyperledger.besu.ethereum.core.Block;
import org.hyperledger.besu.ethereum.core.BlockBody;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderTestFixture;
import org.hyperledger.besu.ethereum.core.ExecutionContextTestFixture;
import org.hyperledger.besu.ethereum.core.MutableWorldState;
import org.hyperledger.besu.ethereum.core.Transaction;
import org.hyperledger.besu.ethereum.mainnet.parallelization.MainnetParallelBlockProcessor;
import org.hyperledger.besu.ethereum.trie.diffbased.bonsai.BonsaiAccount;
import org.hyperledger.besu.ethereum.worldstate.WorldStateArchive;
import org.hyperledger.besu.metrics.noop.NoOpMetricsSystem;
import org.hyperledger.besu.plugin.services.storage.DataStorageFormat;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.stream.Stream;
import org.apache.tuweni.bytes.Bytes;
import org.apache.tuweni.bytes.Bytes32;
import org.apache.tuweni.units.bigints.UInt256;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.Arguments;
import org.junit.jupiter.params.provider.MethodSource;
import org.web3j.abi.FunctionEncoder;
import org.web3j.abi.datatypes.Function;
import org.web3j.abi.datatypes.Type;
import org.web3j.abi.datatypes.generated.Uint256;
@SuppressWarnings("rawtypes")
class AbstractBlockProcessorIntegrationTest {
private static final String ACCOUNT_GENESIS_1 = "0x627306090abab3a6e1400e9345bc60c78a8bef57";
private static final String ACCOUNT_GENESIS_2 = "0x7f2d653f56ea8de6ffa554c7a0cd4e03af79f3eb";
private static final String ACCOUNT_2 = "0x0000000000000000000000000000000000000002";
private static final String ACCOUNT_3 = "0x0000000000000000000000000000000000000003";
private static final String ACCOUNT_4 = "0x0000000000000000000000000000000000000004";
private static final String ACCOUNT_5 = "0x0000000000000000000000000000000000000005";
private static final String ACCOUNT_6 = "0x0000000000000000000000000000000000000006";
private static final String CONTRACT_ADDRESS = "0x00000000000000000000000000000000000fffff";
private static final KeyPair ACCOUNT_GENESIS_1_KEYPAIR =
generateKeyPair("c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3");
private static final KeyPair ACCOUNT_GENESIS_2_KEYPAIR =
generateKeyPair("fc5141e75bf622179f8eedada7fab3e2e6b3e3da8eb9df4f46d84df22df7430e");
private WorldStateArchive worldStateArchive;
private DefaultBlockchain blockchain;
private Address coinbase;
@BeforeEach
public void setUp() {
final ExecutionContextTestFixture contextTestFixture =
ExecutionContextTestFixture.builder(
GenesisConfigFile.fromResource(
"/org/hyperledger/besu/ethereum/mainnet/genesis-bp-it.json"))
.dataStorageFormat(DataStorageFormat.BONSAI)
.build();
final BlockHeader blockHeader = new BlockHeaderTestFixture().number(0L).buildHeader();
coinbase = blockHeader.getCoinbase();
worldStateArchive = contextTestFixture.getStateArchive();
blockchain = (DefaultBlockchain) contextTestFixture.getBlockchain();
}
private static Stream<Arguments> blockProcessorProvider() {
final ExecutionContextTestFixture contextTestFixture =
ExecutionContextTestFixture.builder(
GenesisConfigFile.fromResource(
"/org/hyperledger/besu/ethereum/mainnet/genesis-bp-it.json"))
.dataStorageFormat(DataStorageFormat.BONSAI)
.build();
final ProtocolSchedule protocolSchedule = contextTestFixture.getProtocolSchedule();
final BlockHeader blockHeader = new BlockHeaderTestFixture().number(0L).buildHeader();
final MainnetTransactionProcessor transactionProcessor =
protocolSchedule.getByBlockHeader(blockHeader).getTransactionProcessor();
final BlockProcessor sequentialBlockProcessor =
new MainnetBlockProcessor(
transactionProcessor,
protocolSchedule
.getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader())
.getTransactionReceiptFactory(),
Wei.of(2_000_000_000_000_000L),
BlockHeader::getCoinbase,
false,
protocolSchedule);
final BlockProcessor parallelBlockProcessor =
new MainnetParallelBlockProcessor(
transactionProcessor,
protocolSchedule
.getByBlockHeader(new BlockHeaderTestFixture().number(0L).buildHeader())
.getTransactionReceiptFactory(),
Wei.of(2_000_000_000_000_000L),
BlockHeader::getCoinbase,
false,
protocolSchedule,
new NoOpMetricsSystem());
return Stream.of(
Arguments.of("sequential", sequentialBlockProcessor),
Arguments.of("parallel", parallelBlockProcessor));
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testBlockProcessingWithTransfers(
final String ignoredName, final BlockProcessor blockProcessor) {
processSimpleTransfers(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessConflictedSimpleTransfersSameSender(
final String ignoredName, final BlockProcessor blockProcessor) {
processConflictedSimpleTransfersSameSender(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessConflictedSimpleTransfersSameAddressReceiverAndSender(
final String ignoredName, final BlockProcessor blockProcessor) {
processConflictedSimpleTransfersSameAddressReceiverAndSender(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessConflictedSimpleTransfersWithCoinbase(
final String ignoredName, final BlockProcessor blockProcessor) {
processConflictedSimpleTransfersWithCoinbase(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessContractSlotUpdateThenReadTx(
final String ignoredName, final BlockProcessor blockProcessor) {
processContractSlotUpdateThenReadTx(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessSlotReadThenUpdateTx(
final String ignoredName, final BlockProcessor blockProcessor) {
processSlotReadThenUpdateTx(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessAccountReadThenUpdateTx(
final String ignoredName, final BlockProcessor blockProcessor) {
processAccountReadThenUpdateTx(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessAccountUpdateThenReadTx(
final String ignoredName, final BlockProcessor blockProcessor) {
processAccountUpdateThenReadTx(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessAccountReadThenUpdateTxWithTwoAccounts(
final String ignoredName, final BlockProcessor blockProcessor) {
processAccountReadThenUpdateTxWithTwoAccounts(blockProcessor);
}
@ParameterizedTest(name = "{index}: {0}")
@MethodSource("blockProcessorProvider")
void testProcessAccountUpdateThenReadTeTxWithTwoAccounts(
final String ignoredName, final BlockProcessor blockProcessor) {
processAccountUpdateThenReadTxWithTwoAccounts(blockProcessor);
}
private void processSimpleTransfers(final BlockProcessor blockProcessor) {
// Create two non conflicted transactions
Transaction transactionTransfer1 = // ACCOUNT_GENESIS_1 -> ACCOUNT_2
createTransferTransaction(
0, 1_000_000_000_000_000_000L, 300000L, 5L, 7L, ACCOUNT_2, ACCOUNT_GENESIS_1_KEYPAIR);
Transaction transactionTransfer2 = // ACCOUNT_GENESIS_2 -> ACCOUNT_3
createTransferTransaction(
0, 2_000_000_000_000_000_000L, 300000L, 5L, 7L, ACCOUNT_3, ACCOUNT_GENESIS_2_KEYPAIR);
MutableWorldState worldState = worldStateArchive.getMutable();
BonsaiAccount senderAccount1 = (BonsaiAccount) worldState.get(transactionTransfer1.getSender());
BonsaiAccount senderAccount2 = (BonsaiAccount) worldState.get(transactionTransfer2.getSender());
Block blockWithTransactions =
createBlockWithTransactions(
"0x4ca6e755674a1df696e5365361a0c352422934ba3ad0a74c9e6b0b56e4f80b4c",
transactionTransfer1,
transactionTransfer2);
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
BonsaiAccount updatedSenderAccount1 =
(BonsaiAccount) worldState.get(transactionTransfer1.getSender());
BonsaiAccount updatedSenderAccount2 =
(BonsaiAccount) worldState.get(transactionTransfer2.getSender());
BonsaiAccount updatedAccount0x2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_2));
BonsaiAccount updatedAccount0x3 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_3));
assertTrue(blockProcessingResult.isSuccessful());
assertThat(updatedAccount0x2.getBalance()).isEqualTo(Wei.of(1_000_000_000_000_000_000L));
assertThat(updatedAccount0x3.getBalance()).isEqualTo(Wei.of(2_000_000_000_000_000_000L));
assertThat(updatedSenderAccount1.getBalance()).isLessThan(senderAccount1.getBalance());
assertThat(updatedSenderAccount2.getBalance()).isLessThan(senderAccount2.getBalance());
}
private void processConflictedSimpleTransfersSameSender(final BlockProcessor blockProcessor) {
// Create three transactions with the same sender
Transaction transferTransaction1 = // ACCOUNT_GENESIS_1 -> ACCOUNT_4
createTransferTransaction(
0, 1_000_000_000_000_000_000L, 300000L, 5L, 7L, ACCOUNT_4, ACCOUNT_GENESIS_1_KEYPAIR);
Transaction transferTransaction2 = // ACCOUNT_GENESIS_1 -> ACCOUNT_5
createTransferTransaction(
1, 2_000_000_000_000_000_000L, 300000L, 5L, 7L, ACCOUNT_5, ACCOUNT_GENESIS_1_KEYPAIR);
Transaction transferTransaction3 = // ACCOUNT_GENESIS_1 -> ACCOUNT_6
createTransferTransaction(
2, 3_000_000_000_000_000_000L, 300000L, 5L, 7L, ACCOUNT_6, ACCOUNT_GENESIS_1_KEYPAIR);
MutableWorldState worldState = worldStateArchive.getMutable();
BonsaiAccount senderAccount = (BonsaiAccount) worldState.get(transferTransaction1.getSender());
Block blockWithTransactions =
createBlockWithTransactions(
"0x7420935ee980cb06060f119ee3ee3dcd5a96989985938a3b3ca096558ad61484",
transferTransaction1,
transferTransaction2,
transferTransaction3);
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
BonsaiAccount updatedSenderAccount =
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
BonsaiAccount updatedAccount0x4 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_4));
BonsaiAccount updatedAccount0x5 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_5));
BonsaiAccount updatedAccount0x6 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_6));
assertTrue(blockProcessingResult.isSuccessful());
assertThat(updatedAccount0x4.getBalance()).isEqualTo(Wei.of(1_000_000_000_000_000_000L));
assertThat(updatedAccount0x5.getBalance()).isEqualTo(Wei.of(2_000_000_000_000_000_000L));
assertThat(updatedAccount0x6.getBalance()).isEqualTo(Wei.of(3_000_000_000_000_000_000L));
assertThat(updatedSenderAccount.getBalance()).isLessThan(senderAccount.getBalance());
}
private void processConflictedSimpleTransfersSameAddressReceiverAndSender(
final BlockProcessor blockProcessor) {
// Create conflicted transfer transactions
Transaction transferTransaction1 =
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
ACCOUNT_GENESIS_2,
ACCOUNT_GENESIS_1_KEYPAIR); // ACCOUNT_GENESIS_1 -> ACCOUNT_GENESIS_2
Transaction transferTransaction2 =
createTransferTransaction(
0,
2_000_000_000_000_000_000L,
300000L,
5L,
7L,
ACCOUNT_2,
ACCOUNT_GENESIS_2_KEYPAIR); // ACCOUNT_GENESIS_2 -> ACCOUNT_2
MutableWorldState worldState = worldStateArchive.getMutable();
BonsaiAccount transferTransaction1Sender =
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
Block blockWithTransactions =
createBlockWithTransactions(
"0x5c0158e79b66c86cf5e5256390b95add0c2e6891c24e72d71b9dbea5845fea72",
transferTransaction1,
transferTransaction2);
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
BonsaiAccount updatedSenderAccount1 =
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
BonsaiAccount updatedGenesisAccount1 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_GENESIS_1));
BonsaiAccount updatedGenesisAccount2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_GENESIS_2));
BonsaiAccount updatedAccount0x2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_2));
assertTrue(blockProcessingResult.isSuccessful());
assertThat(updatedGenesisAccount1.getBalance())
.isEqualTo(
Wei.of(
UInt256.fromHexString(
("0x00000000000000000000000000000000000000000000003627e8f7123739c1c8"))));
assertThat(updatedGenesisAccount2.getBalance())
.isEqualTo(
Wei.of(
UInt256.fromHexString(
("0x00000000000000000000000000000000000000000000003627e8f7123739c024"))));
assertThat(updatedAccount0x2.getBalance()).isEqualTo(Wei.of(2_000_000_000_000_000_000L));
assertThat(updatedSenderAccount1.getBalance())
.isLessThan(transferTransaction1Sender.getBalance());
}
private void processConflictedSimpleTransfersWithCoinbase(final BlockProcessor blockProcessor) {
// Create conflicted transactions using coinbase
Transaction transferTransaction1 =
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
ACCOUNT_2,
ACCOUNT_GENESIS_1_KEYPAIR); // ACCOUNT_GENESIS_1 -> ACCOUNT_2
Transaction transferTransaction2 =
createTransferTransaction(
0,
2_000_000_000_000_000_000L,
300000L,
5L,
7L,
coinbase.toHexString(),
ACCOUNT_GENESIS_2_KEYPAIR); // ACCOUNT_GENESIS_2 -> COINBASE
MutableWorldState worldState = worldStateArchive.getMutable();
BonsaiAccount transferTransaction1Sender =
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
Block blockWithTransactions =
createBlockWithTransactions(
"0xd9544f389692face27352d23494dd1446d9af025067bc11b29e0eb83e258676a",
transferTransaction1,
transferTransaction2);
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
BonsaiAccount updatedSenderAccount1 =
(BonsaiAccount) worldState.get(transferTransaction1.getSender());
BonsaiAccount updatedAccount0x2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_2));
BonsaiAccount updatedCoinbase = (BonsaiAccount) worldState.get(coinbase);
assertTrue(blockProcessingResult.isSuccessful());
assertThat(updatedAccount0x2.getBalance()).isEqualTo(Wei.of(1_000_000_000_000_000_000L));
assertThat(updatedCoinbase.getBalance())
.isEqualTo(
Wei.of(
UInt256.fromHexString(
("0x0000000000000000000000000000000000000000000000001bc8886498566008"))));
assertThat(updatedSenderAccount1.getBalance())
.isLessThan(transferTransaction1Sender.getBalance());
}
void processContractSlotUpdateThenReadTx(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
// create conflicted transactions on the same slot (update then read)
Transaction setSlot1Transaction =
createContractUpdateSlotTransaction(
0, contractAddress, "setSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(100));
Transaction getSlot1Transaction =
createContractUpdateSlotTransaction(
0, contractAddress, "getSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.empty());
Transaction setSlot3Transaction =
createContractUpdateSlotTransaction(
1, contractAddress, "setSlot2", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(200));
Transaction setSlot4Transaction =
createContractUpdateSlotTransaction(
2, contractAddress, "setSlot3", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(300));
Block blockWithTransactions =
createBlockWithTransactions(
"0x51d59f64426ea986b1323aa22b9881c83f67947b4f90c2c302b21d3f8c459aff",
setSlot1Transaction,
getSlot1Transaction,
setSlot3Transaction,
setSlot4Transaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
assertContractStorage(worldState, contractAddress, 0, 100);
assertContractStorage(worldState, contractAddress, 1, 200);
assertContractStorage(worldState, contractAddress, 2, 300);
}
void processSlotReadThenUpdateTx(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
Transaction getSlot1Transaction =
createContractUpdateSlotTransaction(
0, contractAddress, "getSlot1", ACCOUNT_GENESIS_1_KEYPAIR, Optional.empty());
Transaction setSlot1Transaction =
createContractUpdateSlotTransaction(
0, contractAddress, "setSlot1", ACCOUNT_GENESIS_2_KEYPAIR, Optional.of(1000));
Transaction setSlo2Transaction =
createContractUpdateSlotTransaction(
1, contractAddress, "setSlot2", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(2000));
Transaction setSlot3Transaction =
createContractUpdateSlotTransaction(
2, contractAddress, "setSlot3", ACCOUNT_GENESIS_1_KEYPAIR, Optional.of(3000));
Block blockWithTransactions =
createBlockWithTransactions(
"0xdf21d4fef211d7a905022dc87f2a68f4bf9cb273fcf9745cfa7f7c2f258c03f3",
getSlot1Transaction,
setSlot1Transaction,
setSlo2Transaction,
setSlot3Transaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
assertContractStorage(worldState, contractAddress, 0, 1000);
assertContractStorage(worldState, contractAddress, 1, 2000);
assertContractStorage(worldState, contractAddress, 2, 3000);
}
void processAccountReadThenUpdateTx(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
Transaction transactionTransfer = // ACCOUNT_GENESIS_1 -> CONTRACT_ADDRESS
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
CONTRACT_ADDRESS,
ACCOUNT_GENESIS_1_KEYPAIR);
Transaction getcontractBalanceTransaction =
createContractReadAccountTransaction(
1, contractAddress, "getBalance", ACCOUNT_GENESIS_1_KEYPAIR, ACCOUNT_2);
Transaction sendEthFromContractTransaction =
createContractUpdateAccountTransaction(
0,
contractAddress,
"transferTo",
ACCOUNT_GENESIS_2_KEYPAIR,
ACCOUNT_2,
500_000_000_000_000_000L);
Block blockWithTransactions =
createBlockWithTransactions(
"0x91966cdde619acb05a1d9fef2f8801432a30edde7131f1f194002b0a766026c7",
transactionTransfer,
getcontractBalanceTransaction,
sendEthFromContractTransaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
BonsaiAccount contractAccount = (BonsaiAccount) worldState.get(contractAddress);
BonsaiAccount updatedAccount0x2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_2));
assertThat(contractAccount.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
assertThat(updatedAccount0x2.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
}
void processAccountUpdateThenReadTx(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
Transaction transactionTransfer =
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
CONTRACT_ADDRESS,
ACCOUNT_GENESIS_1_KEYPAIR);
Transaction sendEthFromContractTransaction =
createContractUpdateAccountTransaction(
1,
contractAddress,
"transferTo",
ACCOUNT_GENESIS_1_KEYPAIR,
ACCOUNT_2,
500_000_000_000_000_000L);
Transaction getcontractBalanceTransaction =
createContractReadAccountTransaction(
0, contractAddress, "getBalance", ACCOUNT_GENESIS_2_KEYPAIR, ACCOUNT_2);
Block blockWithTransactions =
createBlockWithTransactions(
"0x375af730c0f9e04666659fc419fda74cc0cb29936607c08adf21d3b236c6b7f6",
transactionTransfer,
sendEthFromContractTransaction,
getcontractBalanceTransaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
BonsaiAccount contractAccount = (BonsaiAccount) worldState.get(contractAddress);
BonsaiAccount updatedAccount0x2 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_2));
assertThat(contractAccount.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
assertThat(updatedAccount0x2.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
}
void processAccountReadThenUpdateTxWithTwoAccounts(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
Transaction transactionTransfer = // ACCOUNT_GENESIS_1 -> CONTRACT_ADDRESS
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
CONTRACT_ADDRESS,
ACCOUNT_GENESIS_1_KEYPAIR);
Transaction getcontractBalanceTransaction =
createContractReadAccountTransaction(
1, contractAddress, "getBalance", ACCOUNT_GENESIS_1_KEYPAIR, ACCOUNT_2);
Transaction sendEthFromContractTransaction =
createContractUpdateAccountTransaction(
0,
contractAddress,
"transferTo",
ACCOUNT_GENESIS_2_KEYPAIR,
ACCOUNT_3,
500_000_000_000_000_000L);
Block blockWithTransactions =
createBlockWithTransactions(
"0x3c2366a28dadbcef39ba04cde7bc30a5dccfce1e478a5c2602f5a28ab9498e6c",
transactionTransfer,
getcontractBalanceTransaction,
sendEthFromContractTransaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
BonsaiAccount contractAccount = (BonsaiAccount) worldState.get(contractAddress);
BonsaiAccount updatedAccount0x3 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_3));
assertThat(contractAccount.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
assertThat(updatedAccount0x3.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
}
void processAccountUpdateThenReadTxWithTwoAccounts(final BlockProcessor blockProcessor) {
Address contractAddress = Address.fromHexStringStrict(CONTRACT_ADDRESS);
Transaction transactionTransfer = // ACCOUNT_GENESIS_1 -> CONTRACT_ADDRESS
createTransferTransaction(
0,
1_000_000_000_000_000_000L,
300000L,
5L,
7L,
CONTRACT_ADDRESS,
ACCOUNT_GENESIS_1_KEYPAIR);
Transaction sendEthFromContractTransaction =
createContractUpdateAccountTransaction(
0,
contractAddress,
"transferTo",
ACCOUNT_GENESIS_2_KEYPAIR,
ACCOUNT_3,
500_000_000_000_000_000L);
Transaction getcontractBalanceTransaction =
createContractReadAccountTransaction(
1, contractAddress, "getBalance", ACCOUNT_GENESIS_1_KEYPAIR, ACCOUNT_2);
Block blockWithTransactions =
createBlockWithTransactions(
"0x3c2366a28dadbcef39ba04cde7bc30a5dccfce1e478a5c2602f5a28ab9498e6c",
transactionTransfer,
sendEthFromContractTransaction,
getcontractBalanceTransaction);
MutableWorldState worldState = worldStateArchive.getMutable();
BlockProcessingResult blockProcessingResult =
blockProcessor.processBlock(blockchain, worldState, blockWithTransactions);
assertTrue(blockProcessingResult.isSuccessful());
// Verify the state
BonsaiAccount contractAccount = (BonsaiAccount) worldState.get(contractAddress);
BonsaiAccount updatedAccount0x3 =
(BonsaiAccount) worldState.get(Address.fromHexStringStrict(ACCOUNT_3));
assertThat(contractAccount.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
assertThat(updatedAccount0x3.getBalance()).isEqualTo(Wei.of(500_000_000_000_000_000L));
}
private static KeyPair generateKeyPair(final String privateKeyHex) {
final KeyPair keyPair =
SignatureAlgorithmFactory.getInstance()
.createKeyPair(
SECPPrivateKey.create(
Bytes32.fromHexString(privateKeyHex), SignatureAlgorithm.ALGORITHM));
return keyPair;
}
private Transaction createContractUpdateSlotTransaction(
final int nonce,
final Address contractAddress,
final String methodSignature,
final KeyPair keyPair,
final Optional<Integer> value) {
Bytes payload = encodeFunctionCall(methodSignature, value);
return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(nonce)
.maxPriorityFeePerGas(Wei.of(5))
.maxFeePerGas(Wei.of(7))
.gasLimit(3000000L)
.to(contractAddress)
.value(Wei.ZERO)
.payload(payload)
.chainId(BigInteger.valueOf(42))
.signAndBuild(keyPair);
}
private Transaction createContractReadAccountTransaction(
final int nonce,
final Address contractAddress,
final String methodSignature,
final KeyPair keyPair,
final String address) {
Bytes payload = encodeFunctionCall(methodSignature, address);
return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(nonce)
.maxPriorityFeePerGas(Wei.of(5))
.maxFeePerGas(Wei.of(7))
.gasLimit(3000000L)
.to(contractAddress)
.value(Wei.ZERO)
.payload(payload)
.chainId(BigInteger.valueOf(42))
.signAndBuild(keyPair);
}
private Transaction createContractUpdateAccountTransaction(
final int nonce,
final Address contractAddress,
final String methodSignature,
final KeyPair keyPair,
final String address,
final long value) {
Bytes payload = encodeFunctionCall(methodSignature, address, value);
return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(nonce)
.maxPriorityFeePerGas(Wei.of(5))
.maxFeePerGas(Wei.of(7))
.gasLimit(3000000L)
.to(contractAddress)
.value(Wei.ZERO)
.payload(payload)
.chainId(BigInteger.valueOf(42))
.signAndBuild(keyPair);
}
private Bytes encodeFunctionCall(final String methodSignature, final Optional<Integer> value) {
List<Type> inputParameters =
value.isPresent() ? Arrays.<Type>asList(new Uint256(value.get())) : List.of();
Function function = new Function(methodSignature, inputParameters, List.of());
return Bytes.fromHexString(FunctionEncoder.encode(function));
}
private Bytes encodeFunctionCall(final String methodSignature, final String address) {
List<Type> inputParameters = Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(address));
Function function = new Function(methodSignature, inputParameters, List.of());
return Bytes.fromHexString(FunctionEncoder.encode(function));
}
private Bytes encodeFunctionCall(
final String methodSignature, final String address, final long value) {
List<Type> inputParameters =
Arrays.<Type>asList(new org.web3j.abi.datatypes.Address(address), new Uint256(value));
Function function = new Function(methodSignature, inputParameters, List.of());
return Bytes.fromHexString(FunctionEncoder.encode(function));
}
private Block createBlockWithTransactions(
final String stateRoot, final Transaction... transactions) {
BlockHeader blockHeader =
new BlockHeaderTestFixture()
.number(1L)
.stateRoot(Hash.fromHexString(stateRoot))
.gasLimit(30_000_000L)
.baseFeePerGas(Wei.of(5))
.buildHeader();
BlockBody blockBody = new BlockBody(Arrays.asList(transactions), Collections.emptyList());
return new Block(blockHeader, blockBody);
}
private void assertContractStorage(
final MutableWorldState worldState,
final Address contractAddress,
final int slot,
final int expectedValue) {
BonsaiAccount contractAccount = (BonsaiAccount) worldState.get(contractAddress);
UInt256 actualValue = contractAccount.getStorageValue(UInt256.valueOf(slot));
assertThat(actualValue).isEqualTo(UInt256.valueOf(expectedValue));
}
private Transaction createTransferTransaction(
final long nonce,
final long value,
final long gasLimit,
final long maxPriorityFeePerGas,
final long maxFeePerGas,
final String hexAddress,
final KeyPair keyPair) {
return Transaction.builder()
.type(TransactionType.EIP1559)
.nonce(nonce)
.maxPriorityFeePerGas(Wei.of(maxPriorityFeePerGas))
.maxFeePerGas(Wei.of(maxFeePerGas))
.gasLimit(gasLimit)
.to(Address.fromHexStringStrict(hexAddress))
.value(Wei.of(value))
.payload(Bytes.EMPTY)
.chainId(BigInteger.valueOf(42))
.signAndBuild(keyPair);
}
}

@ -0,0 +1,56 @@
/*
* Copyright contributors to Hyperledger Besu.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
* an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
*
* SPDX-License-Identifier: Apache-2.0
*/
pragma solidity ^0.8.19;
contract SimpleStorage {
uint256 private slot1; //0x0
uint256 private slot2; //0x1
uint256 private slot3; //0x2
function setSlot1(uint256 value) public {
slot1 = value;
}
function setSlot2(uint256 value) public {
slot2 = value;
}
function setSlot3(uint256 value) public {
slot3 = value;
}
function getSlot1() public view returns (uint256) {
return slot1;
}
function getSlot2() public view returns (uint256) {
return slot2;
}
function getSlot3() public view returns (uint256) {
return slot3;
}
function getBalance(address account) public view returns (uint256) {
return account.balance;
}
function transferTo(address payable recipient, uint256 amount) public payable {
require(amount <= address(this).balance, "Insufficient balance in contract");
recipient.transfer(amount);
}
receive() external payable {}
}

@ -0,0 +1,48 @@
{
"config": {
"chainId":42,
"homesteadBlock":0,
"eip150Block":0,
"eip155Block":0,
"eip158Block":0,
"byzantiumBlock":0,
"constantinopleBlock":0,
"petersburgBlock":0,
"istanbulBlock":0,
"muirGlacierBlock":0,
"berlinBlock":0,
"londonBlock":0,
"shanghaiTime": 0,
"terminalTotalDifficulty":0,
"cancunTime":0
},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x0000001",
"extraData": "",
"gasLimit": "0x1C9C380",
"nonce": "0x0000000000000107",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00",
"alloc": {
"627306090abab3a6e1400e9345bc60c78a8bef57": {
"balance": "0x3635C9ADC5DEA00000",
"privateKey": "c87509a1c067bbde78beb793e6fa76530b6382a4c0241e5e4a9ec0a0f44dc0d3",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored"
},
"7f2d653f56ea8de6ffa554c7a0cd4e03af79f3eb": {
"balance": "0x3635C9ADC5DEA00000",
"privateKey": "fc5141e75bf622179f8eedada7fab3e2e6b3e3da8eb9df4f46d84df22df7430e",
"comment": "private key and this comment are ignored. In a real chain, the private key should NOT be stored"
},"0x00000000000000000000000000000000000fffff": {
"balance": "0",
"code": "60806040526004361061007e575f3560e01c8063713ba21b1161004d578063713ba21b14610121578063bad4ba0b14610149578063cda7be6114610171578063f8b2cb4f1461019b57610085565b8063250976e7146100895780632ccb1b30146100b35780635e8ad6c7146100cf5780636c190cd1146100f757610085565b3661008557005b5f80fd5b348015610094575f80fd5b5061009d6101d7565b6040516100aa91906102d1565b60405180910390f35b6100cd60048036038101906100c89190610372565b6101e0565b005b3480156100da575f80fd5b506100f560048036038101906100f091906103b0565b61026b565b005b348015610102575f80fd5b5061010b610274565b60405161011891906102d1565b60405180910390f35b34801561012c575f80fd5b50610147600480360381019061014291906103b0565b61027d565b005b348015610154575f80fd5b5061016f600480360381019061016a91906103b0565b610287565b005b34801561017c575f80fd5b50610185610291565b60405161019291906102d1565b60405180910390f35b3480156101a6575f80fd5b506101c160048036038101906101bc9190610416565b610299565b6040516101ce91906102d1565b60405180910390f35b5f600254905090565b47811115610223576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161021a9061049b565b60405180910390fd5b8173ffffffffffffffffffffffffffffffffffffffff166108fc8290811502906040515f60405180830381858888f19350505050158015610266573d5f803e3d5ffd5b505050565b805f8190555050565b5f600154905090565b8060018190555050565b8060028190555050565b5f8054905090565b5f8173ffffffffffffffffffffffffffffffffffffffff16319050919050565b5f819050919050565b6102cb816102b9565b82525050565b5f6020820190506102e45f8301846102c2565b92915050565b5f80fd5b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f610317826102ee565b9050919050565b6103278161030d565b8114610331575f80fd5b50565b5f813590506103428161031e565b92915050565b610351816102b9565b811461035b575f80fd5b50565b5f8135905061036c81610348565b92915050565b5f8060408385031215610388576103876102ea565b5b5f61039585828601610334565b92505060206103a68582860161035e565b9150509250929050565b5f602082840312156103c5576103c46102ea565b5b5f6103d28482850161035e565b91505092915050565b5f6103e5826102ee565b9050919050565b6103f5816103db565b81146103ff575f80fd5b50565b5f81359050610410816103ec565b92915050565b5f6020828403121561042b5761042a6102ea565b5b5f61043884828501610402565b91505092915050565b5f82825260208201905092915050565b7f496e73756666696369656e742062616c616e636520696e20636f6e74726163745f82015250565b5f610485602083610441565b915061049082610451565b602082019050919050565b5f6020820190508181035f8301526104b281610479565b905091905056fea264697066735822122023baf1931f9dfdc95c00818838ff0de5f17ac26981187c1407ae4f1d111ff57464736f6c634300081a0033",
"comment": "This bytecode corresponds to the smart contract contractUsedInBlockProcessingIT.sol, visible in the resource files",
"storage": {
"0x0": "0x2a",
"0x1": "0x54",
"0x2": "0x7e"
}
}
}
}
Loading…
Cancel
Save