Blockchain Tests Source Code¶
Location: /src/BlockchainTestsFiller
Blockchain tests can include multiple blocks and each of those blocks can include multiple transactions. These blocks can be either valid or invalid.
Subfolders¶
InvalidBlocks | Tests containing blocks that are expected to fail on import |
ValidBlocks | Normal blockchain tests |
TransitionTests | Blockchain tests with exotic network rules switching forks at block #5 |
Note
Except for the values in the indexes section, all the field values in the tests’ source code are strings. In YAML string is the default field type. In JSON string values are enclosed by quotes.
When a value is numeric, such as a value in storage or an address’s balance, it can be specified either in hexademical (starting with 0x), or in decimal (starting with a digit). For the sake of legibility, numeric values can also have underscores. For example, you can use 1_000_000_000 for 10^9, which is a lot more readable than 1000000000.
When a numeric field exceeds 256 bits, you can specify it using the syntax 0x:bigint 0x1000….00001.
Test Structure¶
You can write tests either in JSON format or in YAML format. All tests are inside a structure with their name
Format¶
Format | JSON | YAML |
---|---|---|
Filename | name-of-testFiller.json | name-of-testFiller.yml |
Format | {
"name-of-test": {
Sections go here
}
}
|
name-of-test:
Sections go here
|
Genesis Block¶
This section contains the genesis block that starts the chain being tested.
Format¶
JSON | YAML |
---|---|
{
"name-of-test": {
<other sections>,
genesisBlockHeader: {
"bloom" : "0x0 <<lots more 0s>>"
"coinbase" : "0x8888f1f195afa192cfee860698584c030f4c9db1",
"difficulty" : "0x020000",
"extraData" : "0x42",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x00",
"mixHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"nonce" : "0x0000000000000000",
"number" : "0x00",
"parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
"receiptTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"stateRoot" : "0x14f0692d8daa55f0eb56a1cf1e2b07746d66ddfa3f8bae21fece76d1421b5d47",
"timestamp" : "0x54c98c81",
"transactionsTrie" : "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
"uncleHash" : "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347"
"baseFeePerGas" : "1000"
}
}
}
|
name-of-test:
<other sections>
genesisBlockHeader:
bloom: 0x0 <<lots more 0s>>
coinbase: 0x8888f1f195afa192cfee860698584c030f4c9db1
difficulty: 131072
extraData: 0x42
gasLimit: 3141592
gasUsed: 0
mixHash: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
nonce: 0x0102030405060708
number: 0
parentHash: 0x0000000000000000000000000000000000000000000000000000000000000000
receiptTrie: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
stateRoot: 0xf99eb1626cfa6db435c0836235942d7ccaa935f1ae247d3f1c21e495685f903a
timestamp: 0x54c98c81
transactionsTrie: 0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421
uncleHash: 0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347
baseFeePerGas: 1000
|
Fields¶
Name in Block Header Sections | Meaning |
---|---|
bloom | bloom filter to speed searches |
coinbase | beneficiary of mining fee |
extraData | data added to the block, ignored by retesteth |
difficulty (pre-merge) | difficulty of previous block |
difficulty (post-merge) | not used anymore (value zero), identifies a block as pre-merge |
gasLimit | limit of gas usage per block |
gasUsed | gas used by this block |
mixHash and nonce (pre-merge) | used by the proof of work algorithm, ignored by retesteth. |
mixHash (post-merge) | block random value, has to be 32 bytes (it is not automatically zero padded) |
number | number of ancestor blocks |
parentHash | hash of previous block |
receiptTrie | The root of the receipt trie after this block |
stateRoot | The root of the state trie after this block |
timestamp | Unix time |
transactionTrie | The root of the transaction trie after this block |
uncleHash | hash of uncle block or blocks |
baseFeePerGas | The base fee per gas required of transactions (London and later, because of EIP 1559) |
Interaction with The Merge¶
The transition from proof of work (PoW) to proof of stake (PoS) changes the meaning of some genesis fields.
- difficulty is set to zero for proof of stake genesis blocks. If you try to run a PoS test on an older fork that uses PoW, the default difficulty is 0x020000.
- mixHash is used for the random value that in production comes from the beacon chain. If this value is specified in the genesis block, that value is the “random” value until there is a blockheader with mixHash. When that happens, that mixHash value is the random value until the next block with a mixHash.
Pre¶
This section contains the initial information of the blockchain.
Format¶
JSON | YAML |
---|---|
{
"name-of-test": {
<other sections>,
"pre": {
"address 1": {
"balance": "0xba1a9ce000",
"nonce": "0",
"code": ":raw 0x600160010160005500"
"storage: {
"0": "12345",
"0x12": "0x121212"
},
"address 2": {
<address fields go here>
}
}
}
}
|
name-of-test:
<other sections>
pre:
address 1:
balance: 0xba1a9ce000,
nonce: 0,
code: :raw 0x600160010160005500
storage:
0: 12345
0x12: 0x121212
address 2:
<address fields go here>
|
Address Fields¶
balance:
Wei balance at the start of the test
code:
The code of the contract. In the expect: section this has to be raw virtual machine code.
nonce:
The nonce counter for the address. This value is used to make sure each transaction is processed only once. The first transaction the address sends has a nonce equal to this value, the second one is the nonce plus one, etc.
storage:
Values in the storage of the address
JSON YAML storage: { "1": 5, "0x20": 0x10 }
storage: 1: 5 0x20: 0x10
code:
The code of the contract. There are several possibilities:
If the account is not a contract, this value is 0x
Raw virtual machine code. This is for cases where it is impossible to provide source code, or the source code is in a language retesteth does not recognize, such as Vyper.
:raw 0x600160010160005500
Lisp Like Language (lll), for example:
{ ; Add 2+2 and store the value in storage location 0 [[0]] (ADD 2 2) }
Yul, which is documented here, for example:
:yul { // Add 2+2 and store the value in storage location 0 sstore(0, add(2,2)) }
Optionally, you can specify the hard fork for which to compile the code
:yul berlin { // Add 2+2 and store the value in storage location 0. // Because we compile using the Berlin hardfork, // there is no PUSH0, and we can use the code to check // forks prior to Shanghai. sstore(0, add(2,2)) }
Solidity, which you can learn here. Solidity code can be provided to a test in two ways:
- Put the solidity code itself in the contract definition (same place as the LLL or Yul code).
- Put a :solidity section with the contract source code. In that case, the value in code: is :solidity <name of contract>.
In either case, you can specify the hardfork to use using this syntax:
// RETESTETH_SOLC_EVM_VERSION=berlin
Blocks¶
This section contains the blocks of the blockchain that are supposed to modify the state from the one in the pre section to the one in the expect section.
Format¶
JSON | YAML |
---|---|
{
"name-of-test": {
<other sections>,
blocks: [
{ transactions: [
{ <transaction> },
{ <transaction> }
]
},
{ transactions: [
{ <transaction> },
{ <transaction> }
]
blockHeader: {
"extraData" : "0x42",
"gasLimit" : "0x2fefd8",
"gasUsed" : "0x5208",
},
uncleHeaders: [ <values here> ]
}
]
}
}
|
name-of-test:
<other sections>
blocks:
- transactions:
- <transaction>
- <transaction>
- blockHeader:
extraData: 42
gasLimit: 100_000
gasUsed: 2_000
uncleHeaders:
<values here>
transactions:
- <transaction>
- <transaction>
|
Fields¶
The fields in each block are optional. Only include those fields you need.
blockHeader:
This field contains the block header parameters. Parameters that are missing are copied from the genesis block.
Name in Block Header Sections Meaning bloom bloom filter to speed searches coinbase beneficiary of mining fee extraData data added to the block, ignored by retesteth difficulty (pre-merge) difficulty of previous block difficulty (post-merge) not used anymore (value zero), identifies a block as pre-merge gasLimit limit of gas usage per block gasUsed gas used by this block mixHash and nonce (pre-merge) used by the proof of work algorithm, ignored by retesteth. mixHash (post-merge) block random value, has to be 32 bytes (it is not automatically zero padded) number number of ancestor blocks parentHash hash of previous block receiptTrie The root of the receipt trie after this block stateRoot The root of the state trie after this block timestamp Unix time transactionTrie The root of the transaction trie after this block uncleHash hash of uncle block or blocks baseFeePerGas The base fee per gas required of transactions (London and later, because of EIP 1559) You can read more about the block header fields here.
One field inside the block header which is not standard in Ethereum is expectException. That field, which is only used in invalid block tests, identifies the exception we expect to receive for the block on different forks of Ethereum. You can read more about it in the Invalid Block Tests section of the Blockchain Tests tutorial.
Note that starting with London gasLimit cannot be changed by more than 1/1024 from the previous value because of EIP 1559. You can specify baseFeePerGas, but the block is only valid if it is the same value that was calculated from the previous block.
blocknumber and chainname:
If you are testing behavior in the presence of multiple competing chains, these fields let you specify the chain and the block’s location within it.
uncleHeaders:
A list of the uncle blocks (blocks mined at the same time). Each item in the list has two fields:
- chainname: The name of the chain from which the uncle block comes
- populateFromBlock: The block number within that chain for the block that is an uncle of the block you are specifying.
However, if you write a test with uncles, you need to run it twice, once to get the state hash values to write them in the test filler file, and again to actually run the test.
transactions:
A list of transaction objects in the block.
Transaction¶
This is the data of a transaction. Every block contains a list of transactions
Format¶
JSON | YAML |
---|---|
{
"name-of-test":
{
<other sections>
"blocks": [
{
transactions: [
{
data: "0xDA7A",
gasLimit: "0x6a506a50",
gasPrice: 1,
value: 1,
to: "add13ess01233210add13ess01233210",
secretKey: "5ec13e7 ... 5ec13e7"
nonce: '0x909ce'
},
{
data: "0xDA7A",
accessList: [
{
"address": "0xcccccccccccccccccccccccccccccccccccccccd",
"storageKeys": ["0x1000", "0x60A7"]
},
{
"address": "0xccccccccccccccccccccccccccccccccccccccce",
"storageKeys": []
}
],
gasLimit: "0x6a506a50",
maxFeePerGas: 1000,
maxPriorityFeePerGas: 10,
value: 1,
to: "add13ess01233210add13ess01233210",
secretKey: "5ec13e7 ... 5ec13e7"
nonce: '0x909ce'
},
<other transactions>
]
<other block fields>
},
<other blocks>
]
}
|
<test-name>:
<other sections>
blocks:
- transactions:
- data: 0xDA7A
gasLimit: '0x6a506a50'
maxFeePerGas: 1000
maxPriorityFeePerGas: 10
value: 1
to: "add13ess01233210add13ess01233210"
secretKey: "5ec13e7 ... 5ec13e7"
nonce: '0x909ce'
- data: 0xDA7A
accessList:
- address: 0xcccccccccccccccccccccccccccccccccccccccd
storageKeys:
- 0x1000
- address: 0xcccccccccccccccccccccccccccccccccccccccc
storageKeys: []
gasLimit: '0x6a506a50'
gasPrice: "1"
value: 1
to: "add13ess01233210add13ess01233210"
secretKey: "5ec13e7 ... 5ec13e7"
nonce: '0x909ce'
- <another transaction>
<other block fields>
- <another block>
|
Fields¶
- data: The data, either in hexadecimal or an ABI call with this format: :abi <function signature> <function parameters separated by spaces>.
- accessList: An optional EIP2930 access list. The accessList is a list of structures, each of which has to have an address and a list of storageKeys (which may be empty).
- gasLimit: Gas limit for the transaction
- gasPrice: Gas price in Wei, prior to London (changed by EIP 1559).
- maxFeePerGas: Maximum acceptable gas price in Wei. Available in London and later.
- maxPriorityFeePerGas: Tip to give the miner (per gas, in Wei). The real tip is either this value or maxFeePerGas-baseFeePerGas (the lower of the two). Available in London and later.
- value: The value the transaction transmits in Wei
- to: The destination address, typically a contract. If you want to submit a create transaction, put an empty string here (and the data segment is the constructor).
- secretKey: The secret key for the sending address. That address is derived from the secret key and therefore does not need to be specified explicitely (see here).
- nonce: The nonce value for the transaction. The first transaction for an address has the nonce value of the address itself, the second transaction has the nonce plus one, etc. Alternatively, if you replace all the nonce values with auto, the tool does this for you.
- invalid: If the transaction is invalid, meaning clients should reject it, set this value to “1”
- expectException: .. include:: ../test_filler/test_expect_exception.rst
Expect¶
This section contains the information we expect to see after the test is concluded.
Format¶
JSON | YAML |
---|---|
{
"name-of-test": {
<other sections>,
"expect": [
{
"network": ["Istanbul", <other forks, see below>],
"result": {
"address 1": {
"balance": "0xba1a9ce000",
"nonce": "0",
"storage: {
"0x0": 12345,
"10" : "0x121212"
},
"code": "0x00"
},
"address 2": {
<address fields go here>
}
},
{ <forks & results> }
]
}
}
|
name-of-test:
<other sections>
expect:
- network:
- Istanbul
- <another fork>
result:
address 1:
balance: 0xba1a9ce000,
nonce: 0,
storage:
0x0: 12345
10: 0x121212
code: 0x00
address 2:
<address fields go here>
- <forks & results>
|
The Network Specification¶
The string that identifies a fork (version) within a network: list is one of three option:
- The specific version: Istanbul
- The version or anything later: >=Frontier
- Anything up to (but not including) the version <Constantinople
Address Fields¶
It is not necessary to include all fields for every address. Only include those fields you wish to test.
balance:
Wei balance at the start of the test
code:
The code of the contract. In the expect: section this has to be raw virtual machine code.
nonce:
The nonce counter for the address. This value is used to make sure each transaction is processed only once. The first transaction the address sends has a nonce equal to this value, the second one is the nonce plus one, etc.
storage:
Values in the storage of the address
JSON YAML storage: { "1": 5, "0x20": 0x10 }
storage: 1: 5 0x20: 0x10