Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

[996] Add features to write multi block tests #1178

Merged
merged 1 commit into from
Sep 17, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,21 @@

package net.consensys.linea.testing;

import static net.consensys.linea.zktracer.module.constants.GlobalConstants.*;

import java.math.BigInteger;
import java.util.Optional;
import java.util.OptionalLong;

import org.hyperledger.besu.config.GenesisConfigFile;
import org.hyperledger.besu.datatypes.Wei;
import org.hyperledger.besu.ethereum.chain.BadBlockManager;
import org.hyperledger.besu.ethereum.core.BlockHeader;
import org.hyperledger.besu.ethereum.core.BlockHeaderBuilder;
import org.hyperledger.besu.ethereum.core.Difficulty;
import org.hyperledger.besu.ethereum.core.MiningParameters;
import org.hyperledger.besu.ethereum.core.PrivacyParameters;
import org.hyperledger.besu.ethereum.mainnet.MainnetBlockHeaderFunctions;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSchedule;
import org.hyperledger.besu.ethereum.mainnet.MainnetProtocolSpecFactory;
import org.hyperledger.besu.ethereum.mainnet.ProtocolSchedule;
Expand All @@ -35,6 +42,23 @@ public class ExecutionEnvironment {
static GenesisConfigFile GENESIS_CONFIG =
GenesisConfigFile.fromSource(GenesisConfigFile.class.getResource("/linea.json"));

public static BlockHeaderBuilder getLineaBlockHeaderBuilder(
Optional<BlockHeader> parentBlockHeader) {
BlockHeaderBuilder blockHeaderBuilder =
parentBlockHeader.isPresent()
? BlockHeaderBuilder.fromHeader(parentBlockHeader.get())
.number(parentBlockHeader.get().getNumber() + 1)
.timestamp(parentBlockHeader.get().getTimestamp() + 100)
.parentHash(parentBlockHeader.get().getHash())
.blockHeaderFunctions(new MainnetBlockHeaderFunctions())
: BlockHeaderBuilder.createDefault();

return blockHeaderBuilder
.baseFee(Wei.of(LINEA_BASE_FEE))
.gasLimit(LINEA_BLOCK_GAS_LIMIT)
.difficulty(Difficulty.of(LINEA_DIFFICULTY));
}

public static ProtocolSpec getProtocolSpec(BigInteger chainId) {
BadBlockManager badBlockManager = new BadBlockManager();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.testing;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import lombok.Builder;
import lombok.Singular;
import lombok.extern.slf4j.Slf4j;
import net.consensys.linea.blockcapture.snapshots.*;
import org.hyperledger.besu.ethereum.core.*;

@Builder
@Slf4j
public class MultiBlockExecutionEnvironment {
@Singular("addAccount")
private final List<ToyAccount> accounts;

private final List<BlockSnapshot> blocks;

public static class MultiBlockExecutionEnvironmentBuilder {

private List<BlockSnapshot> blocks = new ArrayList<>();

public MultiBlockExecutionEnvironmentBuilder addBlock(List<Transaction> transactions) {
BlockHeaderBuilder blockHeaderBuilder =
this.blocks.isEmpty()
? ExecutionEnvironment.getLineaBlockHeaderBuilder(Optional.empty())
: ExecutionEnvironment.getLineaBlockHeaderBuilder(
Optional.of(this.blocks.getLast().header().toBlockHeader()));
blockHeaderBuilder.coinbase(ToyExecutionEnvironmentV2.DEFAULT_COINBASE_ADDRESS);
BlockBody blockBody = new BlockBody(transactions, Collections.emptyList());
this.blocks.add(BlockSnapshot.of(blockHeaderBuilder.buildBlockHeader(), blockBody));

return this;
}
}

public void run() {
ReplayExecutionEnvironment.builder()
.build()
.replay(ToyExecutionEnvironmentV2.CHAIN_ID, this.buildConflationSnapshot());
}

private ConflationSnapshot buildConflationSnapshot() {
List<AccountSnapshot> accountSnapshots =
accounts.stream()
.map(
toyAccount ->
new AccountSnapshot(
toyAccount.getAddress().toHexString(),
toyAccount.getNonce(),
toyAccount.getBalance().toHexString(),
toyAccount.getCode().toHexString()))
.toList();

List<StorageSnapshot> storageSnapshots =
accounts.stream()
.flatMap(
account ->
account.storage.entrySet().stream()
.map(
storageEntry ->
new StorageSnapshot(
account.getAddress().toHexString(),
storageEntry.getKey().toHexString(),
storageEntry.getValue().toHexString())))
.toList();

List<BlockHashSnapshot> blockHashSnapshots =
blocks.stream()
.map(
blockSnapshot -> {
BlockHeader blockHeader = blockSnapshot.header().toBlockHeader();
return BlockHashSnapshot.of(blockHeader.getNumber(), blockHeader.getBlockHash());
})
.toList();

return new ConflationSnapshot(
this.blocks, accountSnapshots, storageSnapshots, blockHashSnapshots);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public void replay(BigInteger chainId, final Reader replayFile, String inputFile
this.checkTracer(inputFilePath);
}

public void replay(BigInteger chainId, ConflationSnapshot conflation) {
this.executeFrom(chainId, conflation);
this.checkTracer();
}

/**
* Loads the states and the conflation defined in a {@link ConflationSnapshot}, mimick the
* accounts, storage and blocks state as it was on the blockchain before the conflation played
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public class ToyAccount implements MutableAccount {
private Wei balance;
private Bytes code;
private Supplier<Hash> codeHash = Suppliers.memoize(() -> Hash.hash(code));
private final Map<UInt256, UInt256> storage = new HashMap<>();
final Map<UInt256, UInt256> storage = new HashMap<>();
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would think getter / setters here is better than making it package visible.


@Builder
public ToyAccount(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,6 @@

package net.consensys.linea.testing;

import static net.consensys.linea.zktracer.module.constants.GlobalConstants.LINEA_BASE_FEE;
import static net.consensys.linea.zktracer.module.constants.GlobalConstants.LINEA_BLOCK_GAS_LIMIT;
import static net.consensys.linea.zktracer.module.constants.GlobalConstants.LINEA_DIFFICULTY;

import java.math.BigInteger;
import java.util.*;
import java.util.function.Consumer;
Expand All @@ -45,6 +41,7 @@ public class ToyExecutionEnvironmentV2 {
public static final Address DEFAULT_COINBASE_ADDRESS =
Address.fromHexString("0xc019ba5e00000000c019ba5e00000000c019ba5e");
private static final long DEFAULT_BLOCK_NUMBER = 6678980;

private static final long DEFAULT_TIME_STAMP = 1347310;
private static final Hash DEFAULT_HASH =
Hash.fromHexStringLenient("0xdeadbeef123123666dead666dead666");
Expand Down Expand Up @@ -86,10 +83,7 @@ public GeneralStateTestCaseEipSpec buildGeneralStateTestCaseSpec(ProtocolSpec pr
ReferenceTestWorldState referenceTestWorldState =
ReferenceTestWorldState.create(accountMockMap, protocolSpec.getEvm().getEvmConfiguration());
BlockHeader blockHeader =
BlockHeaderBuilder.createDefault()
.baseFee(Wei.of(LINEA_BASE_FEE))
.gasLimit(LINEA_BLOCK_GAS_LIMIT)
.difficulty(Difficulty.of(LINEA_DIFFICULTY))
ExecutionEnvironment.getLineaBlockHeaderBuilder(Optional.empty())
.number(DEFAULT_BLOCK_NUMBER)
.coinbase(DEFAULT_COINBASE_ADDRESS)
.timestamp(DEFAULT_TIME_STAMP)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
/*
* Copyright Consensys Software Inc.
*
* 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 net.consensys.linea.testing;

import java.util.List;

import net.consensys.linea.zktracer.opcode.OpCode;
import org.apache.tuweni.bytes.Bytes;
import org.hyperledger.besu.crypto.KeyPair;
import org.hyperledger.besu.crypto.SECP256K1;
import org.hyperledger.besu.datatypes.AccessListEntry;
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.core.Transaction;
import org.junit.jupiter.api.Test;

class ExampleMultiBlockTest {

@Test
void test() {
final ToyAccount receiverAccount =
ToyAccount.builder()
.balance(Wei.fromEth(1))
.nonce(116)
.address(Address.fromHexString("0xdead000000000000000000000000000beef"))
.build();

final KeyPair senderKeyPair1 = new SECP256K1().generateKeyPair();
final Address senderAddress1 =
Address.extract(Hash.hash(senderKeyPair1.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount1 =
ToyAccount.builder().balance(Wei.fromEth(123)).nonce(5).address(senderAddress1).build();

final KeyPair senderKeyPair2 = new SECP256K1().generateKeyPair();
final Address senderAddress2 =
Address.extract(Hash.hash(senderKeyPair2.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount2 =
ToyAccount.builder().balance(Wei.fromEth(1231)).nonce(15).address(senderAddress2).build();

final KeyPair senderKeyPair3 = new SECP256K1().generateKeyPair();
final Address senderAddress3 =
Address.extract(Hash.hash(senderKeyPair3.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount3 =
ToyAccount.builder().balance(Wei.fromEth(1231)).nonce(15).address(senderAddress3).build();

final KeyPair senderKeyPair4 = new SECP256K1().generateKeyPair();
final Address senderAddress4 =
Address.extract(Hash.hash(senderKeyPair4.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount4 =
ToyAccount.builder().balance(Wei.fromEth(11)).nonce(115).address(senderAddress4).build();

final KeyPair senderKeyPair5 = new SECP256K1().generateKeyPair();
final Address senderAddress5 =
Address.extract(Hash.hash(senderKeyPair5.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount5 =
ToyAccount.builder().balance(Wei.fromEth(12)).nonce(0).address(senderAddress5).build();

final KeyPair senderKeyPair6 = new SECP256K1().generateKeyPair();
final Address senderAddress6 =
Address.extract(Hash.hash(senderKeyPair6.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount6 =
ToyAccount.builder().balance(Wei.fromEth(12)).nonce(6).address(senderAddress6).build();

final KeyPair senderKeyPair7 = new SECP256K1().generateKeyPair();
final Address senderAddress7 =
Address.extract(Hash.hash(senderKeyPair7.getPublicKey().getEncodedBytes()));
final ToyAccount senderAccount7 =
ToyAccount.builder().balance(Wei.fromEth(231)).nonce(21).address(senderAddress7).build();

final Transaction pureTransfer =
ToyTransaction.builder()
.sender(senderAccount1)
.to(receiverAccount)
.keyPair(senderKeyPair1)
.value(Wei.of(123))
.build();

final Transaction pureTransferWoValue =
ToyTransaction.builder()
.sender(senderAccount2)
.to(receiverAccount)
.keyPair(senderKeyPair2)
.value(Wei.of(0))
.build();

final List<String> listOfKeys =
List.of("0x0123", "0xdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef");
final List<AccessListEntry> accessList =
List.of(
AccessListEntry.createAccessListEntry(
Address.fromHexString("0x1234567890"), listOfKeys));

final Transaction pureTransferWithUselessAccessList =
ToyTransaction.builder()
.sender(senderAccount3)
.to(receiverAccount)
.keyPair(senderKeyPair3)
.gasLimit(100000L)
.transactionType(TransactionType.ACCESS_LIST)
.accessList(accessList)
.value(Wei.of(546))
.build();

final Transaction pureTransferWithUselessCalldata =
ToyTransaction.builder()
.sender(senderAccount4)
.to(receiverAccount)
.keyPair(senderKeyPair4)
.gasLimit(1000001L)
.value(Wei.of(546))
.payload(Bytes.minimalBytes(0xdeadbeefL))
.build();

final Transaction pureTransferWithUselessCalldataAndAccessList =
ToyTransaction.builder()
.sender(senderAccount5)
.to(receiverAccount)
.gasLimit(1000020L)
.transactionType(TransactionType.EIP1559)
.keyPair(senderKeyPair5)
.value(Wei.of(546))
.accessList(accessList)
.payload(Bytes.minimalBytes(0xdeadbeefL))
.build();

MultiBlockExecutionEnvironment.MultiBlockExecutionEnvironmentBuilder builder =
MultiBlockExecutionEnvironment.builder();
builder
.accounts(
List.of(
senderAccount1,
senderAccount2,
senderAccount3,
senderAccount4,
senderAccount5,
senderAccount6,
senderAccount7,
receiverAccount))
.addBlock(List.of(pureTransfer))
.addBlock(List.of(pureTransferWoValue, pureTransferWithUselessAccessList))
.addBlock(
List.of(pureTransferWithUselessCalldata, pureTransferWithUselessCalldataAndAccessList))
.build()
.run();
}

@Test
void test2() {
KeyPair keyPair = new SECP256K1().generateKeyPair();
Address senderAddress = Address.extract(Hash.hash(keyPair.getPublicKey().getEncodedBytes()));

ToyAccount senderAccount =
ToyAccount.builder().balance(Wei.fromEth(1)).nonce(5).address(senderAddress).build();

ToyAccount receiverAccount =
ToyAccount.builder()
.balance(Wei.ONE)
.nonce(6)
.address(Address.fromHexString("0x111111"))
.code(
BytecodeCompiler.newProgram()
.push(32, 0xbeef)
.push(32, 0xdead)
.op(OpCode.ADD)
.compile())
.build();

Transaction tx =
ToyTransaction.builder().sender(senderAccount).to(receiverAccount).keyPair(keyPair).build();

MultiBlockExecutionEnvironment.builder()
.accounts(List.of(senderAccount, receiverAccount))
.addBlock(List.of(tx))
.build()
.run();
}
}
Loading