mirror of https://github.com/hyperledger/besu
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
parent
b727c958bf
commit
9cb8b06e4b
@ -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…
Reference in new issue