Portal State Proof Verification with Tests (#1951)
* Initial implementation of account and storage proof verification for the portal state network. * Completed initial state proof verification loop test. * Completed proof verification tests. * Minor updates based on PR feedback and comments. * Add state proof verification test to test runner.
This commit is contained in:
parent
43e5f428af
commit
32e1f94d78
|
@ -0,0 +1,30 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2023 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import
|
||||||
|
stint,
|
||||||
|
eth/[common, trie],
|
||||||
|
./state_proof_verification
|
||||||
|
|
||||||
|
type
|
||||||
|
AccountState* = HexaryTrie
|
||||||
|
StorageState* = HexaryTrie
|
||||||
|
|
||||||
|
proc generateAccountProof*(
|
||||||
|
state: AccountState,
|
||||||
|
address: EthAddress): AccountProof {.raises: [RlpError].} =
|
||||||
|
let key = keccakHash(address).data
|
||||||
|
state.getBranch(key).AccountProof
|
||||||
|
|
||||||
|
proc generateStorageProof*(
|
||||||
|
state: StorageState,
|
||||||
|
slotKey: UInt256): StorageProof {.raises: [RlpError].} =
|
||||||
|
let key = keccakHash(toBytesBE(slotKey)).data
|
||||||
|
state.getBranch(key).StorageProof
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2023 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/sequtils,
|
||||||
|
stint,
|
||||||
|
eth/[common, rlp, trie/hexary_proof_verification],
|
||||||
|
stew/results
|
||||||
|
|
||||||
|
export results
|
||||||
|
|
||||||
|
type
|
||||||
|
MptProof = seq[seq[byte]]
|
||||||
|
AccountProof* = distinct MptProof
|
||||||
|
StorageProof* = distinct MptProof
|
||||||
|
|
||||||
|
proc verifyAccount*(
|
||||||
|
trustedStateRoot: KeccakHash,
|
||||||
|
address: EthAddress,
|
||||||
|
account: Account,
|
||||||
|
proof: AccountProof): Result[void, string] =
|
||||||
|
|
||||||
|
let key = toSeq(keccakHash(address).data)
|
||||||
|
let value = rlp.encode(account)
|
||||||
|
|
||||||
|
let proofResult = verifyMptProof(proof.MptProof, trustedStateRoot, key, value)
|
||||||
|
|
||||||
|
case proofResult.kind
|
||||||
|
of ValidProof:
|
||||||
|
ok()
|
||||||
|
of MissingKey:
|
||||||
|
# For an account that doesn't exist yet, which is fine.
|
||||||
|
ok()
|
||||||
|
of InvalidProof:
|
||||||
|
err(proofResult.errorMsg)
|
||||||
|
|
||||||
|
proc verifyContractStorageSlot*(
|
||||||
|
trustedStorageRoot: KeccakHash,
|
||||||
|
slotKey: UInt256,
|
||||||
|
slotValue: UInt256,
|
||||||
|
proof: StorageProof): Result[void, string] =
|
||||||
|
|
||||||
|
let key = toSeq(keccakHash(toBytesBE(slotKey)).data)
|
||||||
|
let value = rlp.encode(slotValue)
|
||||||
|
|
||||||
|
let proofResult = verifyMptProof(proof.MptProof, trustedStorageRoot, key, value)
|
||||||
|
|
||||||
|
case proofResult.kind
|
||||||
|
of ValidProof:
|
||||||
|
ok()
|
||||||
|
of MissingKey:
|
||||||
|
# This is for a slot that doesn't have anything stored at it, but that's fine.
|
||||||
|
ok()
|
||||||
|
of InvalidProof:
|
||||||
|
err(proofResult.errorMsg)
|
||||||
|
|
||||||
|
func verifyContractBytecode*(
|
||||||
|
trustedCodeHash: KeccakHash,
|
||||||
|
bytecode: openArray[byte]): Result[void, string] =
|
||||||
|
if trustedCodeHash == keccakHash(bytecode):
|
||||||
|
ok()
|
||||||
|
else:
|
||||||
|
err("hash of bytecode doesn't match the expected code hash")
|
|
@ -11,6 +11,7 @@ import
|
||||||
./test_portal_wire_protocol,
|
./test_portal_wire_protocol,
|
||||||
./test_state_distance,
|
./test_state_distance,
|
||||||
./test_state_network,
|
./test_state_network,
|
||||||
|
./test_state_proof_verification,
|
||||||
./test_accumulator,
|
./test_accumulator,
|
||||||
./test_history_network,
|
./test_history_network,
|
||||||
./test_content_db,
|
./test_content_db,
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 1,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"daoForkSupport": true,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"berlinBlock": 2000
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"nonce": "0x0",
|
||||||
|
"timestamp": "0x0",
|
||||||
|
"extraData": "0x00",
|
||||||
|
"gasLimit": "0x5f5e100",
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"coinbase": "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba",
|
||||||
|
"alloc": {
|
||||||
|
"0000000000000000000000000000000000000100": {
|
||||||
|
"code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160005500",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000101": {
|
||||||
|
"code": "0x60047fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160005500",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000102": {
|
||||||
|
"code": "0x60017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0160005500",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000103": {
|
||||||
|
"code": "0x600060000160005500",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000104": {
|
||||||
|
"code": "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff60010160005500",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
},
|
||||||
|
"cccccccccccccccccccccccccccccccccccccccc": {
|
||||||
|
"code": "0x600060006000600060006004356101000162fffffff100",
|
||||||
|
"balance": "0xba1a9ce0ba1a9ce"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"number": "0x0",
|
||||||
|
"gasUsed": "0x0",
|
||||||
|
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,46 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 1,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"londonBlock": 1337
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x20000",
|
||||||
|
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"timestamp": "0x1234",
|
||||||
|
"alloc": {
|
||||||
|
"cf49fda3be353c69b41ed96333cd24302da4556f": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0161e041aad467a890839d5b08b138c1e6373072": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"b97de4b8c857e4f6bc354f226dc3249aaee49209": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"c5065c9eeebe6df2c2284d046bfc906501846c51": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000314": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
|
||||||
|
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000315": {
|
||||||
|
"balance": "0x9999999999999999999999999999999",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
{
|
||||||
|
"config": {
|
||||||
|
"chainId": 7,
|
||||||
|
"homesteadBlock": 0,
|
||||||
|
"eip150Block": 0,
|
||||||
|
"eip150Hash": "0x5de1ee4135274003348e80b788e5afa4b18b18d320a5622218d5c493fedf5689",
|
||||||
|
"eip155Block": 0,
|
||||||
|
"eip158Block": 0,
|
||||||
|
"byzantiumBlock": 0,
|
||||||
|
"constantinopleBlock": 0,
|
||||||
|
"petersburgBlock": 0,
|
||||||
|
"istanbulBlock": 0,
|
||||||
|
"muirGlacierBlock": 0,
|
||||||
|
"berlinBlock": 0,
|
||||||
|
"londonBlock": 0,
|
||||||
|
"clique": {
|
||||||
|
"epoch": 3000,
|
||||||
|
"period": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"genesis": {
|
||||||
|
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||||
|
"difficulty": "0x30000",
|
||||||
|
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000658bdf435d810c91414ec09147daa6db624063790000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
|
||||||
|
"gasLimit": "0x2fefd8",
|
||||||
|
"nonce": "0x0000000000000000",
|
||||||
|
"timestamp": "0x1234",
|
||||||
|
"alloc": {
|
||||||
|
"cf49fda3be353c69b41ed96333cd24302da4556f": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0161e041aad467a890839d5b08b138c1e6373072": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"87da6a8c6e9eff15d703fc2773e32f6af8dbe301": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"b97de4b8c857e4f6bc354f226dc3249aaee49209": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"c5065c9eeebe6df2c2284d046bfc906501846c51": {
|
||||||
|
"balance": "0x123450000000000000000"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000314": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063a223e05d1461006a578063abd1a0cf1461008d578063abfced1d146100d4578063e05c914a14610110578063e6768b451461014c575b610000565b346100005761007761019d565b6040518082815260200191505060405180910390f35b34610000576100be600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919050506101a3565b6040518082815260200191505060405180910390f35b346100005761010e600480803573ffffffffffffffffffffffffffffffffffffffff169060200190919080359060200190919050506101ed565b005b346100005761014a600480803590602001909190803573ffffffffffffffffffffffffffffffffffffffff16906020019091905050610236565b005b346100005761017960048080359060200190919080359060200190919080359060200190919050506103c4565b60405180848152602001838152602001828152602001935050505060405180910390f35b60005481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490505b919050565b80600160008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055505b5050565b7f6031a8d62d7c95988fa262657cd92107d90ed96e08d8f867d32f26edfe85502260405180905060405180910390a17f47e2689743f14e97f7dcfa5eec10ba1dff02f83b3d1d4b9c07b206cbbda66450826040518082815260200191505060405180910390a1817fa48a6b249a5084126c3da369fbc9b16827ead8cb5cdc094b717d3f1dcd995e2960405180905060405180910390a27f7890603b316f3509577afd111710f9ebeefa15e12f72347d9dffd0d65ae3bade81604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390a18073ffffffffffffffffffffffffffffffffffffffff167f7efef9ea3f60ddc038e50cccec621f86a0195894dc0520482abf8b5c6b659e4160405180905060405180910390a28181604051808381526020018273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019250505060405180910390a05b5050565b6000600060008585859250925092505b935093509390505600a165627a7a72305820aaf842d0d0c35c45622c5263cbb54813d2974d3999c8c38551d7c613ea2bc1170029",
|
||||||
|
"storage": {
|
||||||
|
"0x0000000000000000000000000000000000000000000000000000000000000000": "0x1234",
|
||||||
|
"0x6661e9d6d8b923d5bbaab1b96e1dd51ff6ea2a93520fdc9eb75d059238b8c5e9": "0x01"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000315": {
|
||||||
|
"balance": "0x9999999999999999999999999999999",
|
||||||
|
"code": "0x60606040526000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff168063ef2769ca1461003e575b610000565b3461000057610078600480803573ffffffffffffffffffffffffffffffffffffffff1690602001909190803590602001909190505061007a565b005b8173ffffffffffffffffffffffffffffffffffffffff166108fc829081150290604051809050600060405180830381858888f1935050505015610106578173ffffffffffffffffffffffffffffffffffffffff167f30a3c50752f2552dcc2b93f5b96866280816a986c0c0408cb6778b9fa198288f826040518082815260200191505060405180910390a25b5b50505600a165627a7a72305820637991fabcc8abad4294bf2bb615db78fbec4edff1635a2647d3894e2daf6a610029"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000316": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x444355"
|
||||||
|
},
|
||||||
|
"0000000000000000000000000000000000000317": {
|
||||||
|
"balance": "0x0",
|
||||||
|
"code": "0x600160003555"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,10 +7,14 @@
|
||||||
|
|
||||||
import
|
import
|
||||||
stew/shims/net,
|
stew/shims/net,
|
||||||
eth/[keys, rlp],
|
eth/[common, keys, rlp, trie, trie/db],
|
||||||
eth/p2p/discoveryv5/[enr, node, routing_table],
|
eth/p2p/discoveryv5/[enr, node, routing_table],
|
||||||
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
eth/p2p/discoveryv5/protocol as discv5_protocol,
|
||||||
../network/history/[accumulator, history_content]
|
../network/history/[accumulator, history_content],
|
||||||
|
../network/state/experimental/state_proof_generation,
|
||||||
|
../../nimbus/db/core_db,
|
||||||
|
../../nimbus/common/[chain_config],
|
||||||
|
../database/content_db
|
||||||
|
|
||||||
proc localAddress*(port: int): Address =
|
proc localAddress*(port: int): Address =
|
||||||
Address(ip: parseIpAddress("127.0.0.1"), port: Port(port))
|
Address(ip: parseIpAddress("127.0.0.1"), port: Port(port))
|
||||||
|
@ -108,3 +112,40 @@ func buildHeadersWithProof*(
|
||||||
? buildHeaderWithProof(header, epochAccumulators))
|
? buildHeaderWithProof(header, epochAccumulators))
|
||||||
|
|
||||||
ok(headersWithProof)
|
ok(headersWithProof)
|
||||||
|
|
||||||
|
proc getGenesisAlloc*(filePath: string): GenesisAlloc =
|
||||||
|
var cn: NetworkParams
|
||||||
|
if not loadNetworkParams(filePath, cn):
|
||||||
|
quit(1)
|
||||||
|
|
||||||
|
cn.genesis.alloc
|
||||||
|
|
||||||
|
proc toState*(alloc: GenesisAlloc):
|
||||||
|
(AccountState, Table[EthAddress, StorageState]) {.raises: [RlpError].} =
|
||||||
|
var accountTrie = initHexaryTrie(newMemoryDB())
|
||||||
|
var storageStates = initTable[EthAddress, StorageState]()
|
||||||
|
|
||||||
|
for address, genAccount in alloc:
|
||||||
|
var storageRoot = EMPTY_ROOT_HASH
|
||||||
|
var codeHash = EMPTY_CODE_HASH
|
||||||
|
|
||||||
|
if genAccount.code.len() > 0:
|
||||||
|
var storageTrie = initHexaryTrie(newMemoryDB())
|
||||||
|
for slotKey, slotValue in genAccount.storage:
|
||||||
|
let key = keccakHash(toBytesBE(slotKey)).data
|
||||||
|
let value = rlp.encode(slotValue)
|
||||||
|
storageTrie.put(key, value)
|
||||||
|
storageStates[address] = storageTrie.StorageState
|
||||||
|
storageRoot = storageTrie.rootHash()
|
||||||
|
codeHash = keccakHash(genAccount.code)
|
||||||
|
|
||||||
|
let account = Account(
|
||||||
|
nonce: genAccount.nonce,
|
||||||
|
balance: genAccount.balance,
|
||||||
|
storageRoot: storageRoot,
|
||||||
|
codeHash: codeHash)
|
||||||
|
let key = keccakHash(address).data
|
||||||
|
let value = rlp.encode(account)
|
||||||
|
accountTrie.put(key, value)
|
||||||
|
|
||||||
|
(accountTrie.AccountState, storageStates)
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
# Nimbus
|
||||||
|
# Copyright (c) 2023 Status Research & Development GmbH
|
||||||
|
# Licensed and distributed under either of
|
||||||
|
# * MIT license (license terms in the root directory or at https://opensource.org/licenses/MIT).
|
||||||
|
# * Apache v2 license (license terms in the root directory or at https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
# at your option. This file may not be copied, modified, or distributed except according to those terms.
|
||||||
|
|
||||||
|
{.push raises: [].}
|
||||||
|
|
||||||
|
import
|
||||||
|
std/os,
|
||||||
|
unittest2,
|
||||||
|
stew/results,
|
||||||
|
eth/trie,
|
||||||
|
../../nimbus/db/core_db,
|
||||||
|
../../nimbus/common/[chain_config],
|
||||||
|
../network/state/experimental/[state_proof_generation, state_proof_verification],
|
||||||
|
./test_helpers
|
||||||
|
|
||||||
|
proc checkValidProofsForExistingLeafs(
|
||||||
|
genAccounts: GenesisAlloc,
|
||||||
|
accountState: AccountState,
|
||||||
|
storageStates: Table[EthAddress, StorageState]) {.raises: [KeyError, RlpError].} =
|
||||||
|
|
||||||
|
for address, account in genAccounts:
|
||||||
|
var acc = newAccount(account.nonce, account.balance)
|
||||||
|
acc.codeHash = keccakHash(account.code)
|
||||||
|
let codeResult = verifyContractBytecode(acc.codeHash, account.code)
|
||||||
|
check codeResult.isOk()
|
||||||
|
|
||||||
|
if account.code.len() > 0:
|
||||||
|
let storageState = storageStates[address]
|
||||||
|
acc.storageRoot = storageState.rootHash()
|
||||||
|
|
||||||
|
for slotKey, slotValue in account.storage :
|
||||||
|
let storageProof = storageState.generateStorageProof(slotKey)
|
||||||
|
let proofResult = verifyContractStorageSlot(acc.storageRoot, slotKey, slotValue, storageProof)
|
||||||
|
check proofResult.isOk()
|
||||||
|
|
||||||
|
let accountProof = accountState.generateAccountProof(address)
|
||||||
|
let proofResult = verifyAccount(accountState.rootHash(), address, acc, accountProof)
|
||||||
|
check proofResult.isOk()
|
||||||
|
|
||||||
|
proc checkValidProofsForMissingLeafs(
|
||||||
|
genAccounts: GenesisAlloc,
|
||||||
|
accountState: var AccountState,
|
||||||
|
storageStates: Table[EthAddress, StorageState]) {.raises: [KeyError, RlpError].} =
|
||||||
|
var remainingAccounts = genAccounts.len()
|
||||||
|
|
||||||
|
for address, account in genAccounts:
|
||||||
|
if (remainingAccounts == 1):
|
||||||
|
break # can't generate proofs from an empty state
|
||||||
|
|
||||||
|
var acc = newAccount(account.nonce, account.balance)
|
||||||
|
acc.codeHash = keccakHash(account.code)
|
||||||
|
|
||||||
|
if account.code.len() > 0:
|
||||||
|
var storageState = storageStates[address]
|
||||||
|
acc.storageRoot = storageState.rootHash()
|
||||||
|
|
||||||
|
var remainingSlots = account.storage.len()
|
||||||
|
for slotKey, slotValue in account.storage:
|
||||||
|
if (remainingSlots == 1):
|
||||||
|
break # can't generate proofs from an empty state
|
||||||
|
|
||||||
|
storageState.del(keccakHash(toBytesBE(slotKey)).data) # delete the slot from the state
|
||||||
|
dec remainingSlots
|
||||||
|
|
||||||
|
let storageProof = storageState.generateStorageProof(slotKey)
|
||||||
|
let proofResult = verifyContractStorageSlot(acc.storageRoot, slotKey, slotValue, storageProof)
|
||||||
|
check proofResult.isOk()
|
||||||
|
|
||||||
|
accountState.del(keccakHash(address).data) # delete the account from the state
|
||||||
|
dec remainingAccounts
|
||||||
|
|
||||||
|
let accountProof = accountState.generateAccountProof(address)
|
||||||
|
let proofResult = verifyAccount(accountState.rootHash(), address, acc, accountProof)
|
||||||
|
check proofResult.isOk()
|
||||||
|
|
||||||
|
proc checkInvalidProofsWithBadStateRoot(
|
||||||
|
genAccounts: GenesisAlloc,
|
||||||
|
accountState: AccountState,
|
||||||
|
storageStates: Table[EthAddress, StorageState]) {.raises: [KeyError, RlpError].} =
|
||||||
|
let badHash = toDigest("2cb1b80b285d09e0570fdbbb808e1d14e4ac53e36dcd95dbc268deec2915b3e7")
|
||||||
|
|
||||||
|
for address, account in genAccounts:
|
||||||
|
var acc = newAccount(account.nonce, account.balance)
|
||||||
|
acc.codeHash = keccakHash(account.code)
|
||||||
|
let codeResult = verifyContractBytecode(badHash, account.code)
|
||||||
|
check codeResult.isErr()
|
||||||
|
|
||||||
|
if account.code.len() > 0:
|
||||||
|
var storageState = storageStates[address]
|
||||||
|
acc.storageRoot = storageState.rootHash()
|
||||||
|
|
||||||
|
var remainingSlots = account.storage.len()
|
||||||
|
for slotKey, slotValue in account.storage:
|
||||||
|
|
||||||
|
let storageProof = storageState.generateStorageProof(slotKey)
|
||||||
|
let proofResult = verifyContractStorageSlot(badHash, slotKey, slotValue, storageProof)
|
||||||
|
check:
|
||||||
|
proofResult.isErr()
|
||||||
|
proofResult.error() == "missing expected node"
|
||||||
|
|
||||||
|
let accountProof = accountState.generateAccountProof(address)
|
||||||
|
let proofResult = verifyAccount(badHash, address, acc, accountProof)
|
||||||
|
check:
|
||||||
|
proofResult.isErr()
|
||||||
|
proofResult.error() == "missing expected node"
|
||||||
|
|
||||||
|
proc checkInvalidProofsWithBadValue(
|
||||||
|
genAccounts: GenesisAlloc,
|
||||||
|
accountState: AccountState,
|
||||||
|
storageStates: Table[EthAddress, StorageState]) {.raises: [KeyError, RlpError].} =
|
||||||
|
|
||||||
|
for address, account in genAccounts:
|
||||||
|
var acc = newAccount(account.nonce, account.balance)
|
||||||
|
acc.codeHash = keccakHash(account.code)
|
||||||
|
|
||||||
|
let codeResult = verifyContractBytecode(acc.codeHash, @[1u8, 2, 3]) # bad code value
|
||||||
|
check codeResult.isErr()
|
||||||
|
|
||||||
|
if account.code.len() > 0:
|
||||||
|
var storageState = storageStates[address]
|
||||||
|
acc.storageRoot = storageState.rootHash()
|
||||||
|
|
||||||
|
var remainingSlots = account.storage.len()
|
||||||
|
for slotKey, slotValue in account.storage:
|
||||||
|
let storageProof = storageState.generateStorageProof(slotKey)
|
||||||
|
let badSlotValue = slotValue + 1 # bad slot value
|
||||||
|
|
||||||
|
let proofResult = verifyContractStorageSlot(acc.storageRoot, slotKey, badSlotValue, storageProof)
|
||||||
|
check:
|
||||||
|
proofResult.isErr()
|
||||||
|
proofResult.error() == "proof does not contain expected value"
|
||||||
|
|
||||||
|
let accountProof = accountState.generateAccountProof(address)
|
||||||
|
inc acc.balance # bad account balance
|
||||||
|
let proofResult = verifyAccount(accountState.rootHash(), address, acc, accountProof)
|
||||||
|
check:
|
||||||
|
proofResult.isErr()
|
||||||
|
proofResult.error() == "proof does not contain expected value"
|
||||||
|
|
||||||
|
|
||||||
|
suite "State Proof Verification Tests":
|
||||||
|
|
||||||
|
let genesisFiles = ["berlin2000.json", "chainid1.json", "chainid7.json", "merge.json"]
|
||||||
|
|
||||||
|
test "Valid proofs for existing leafs":
|
||||||
|
for file in genesisFiles:
|
||||||
|
let accounts = getGenesisAlloc("fluffy" / "tests" / "custom_genesis" / file)
|
||||||
|
let state = accounts.toState()
|
||||||
|
checkValidProofsForExistingLeafs(accounts, state[0], state[1])
|
||||||
|
|
||||||
|
test "Valid proofs for missing leafs":
|
||||||
|
for file in genesisFiles:
|
||||||
|
let accounts = getGenesisAlloc("fluffy" / "tests" / "custom_genesis" / file)
|
||||||
|
var state = accounts.toState()
|
||||||
|
checkValidProofsForMissingLeafs(accounts, state[0], state[1])
|
||||||
|
|
||||||
|
test "Invalid proofs with bad state root":
|
||||||
|
for file in genesisFiles:
|
||||||
|
let accounts = getGenesisAlloc("fluffy" / "tests" / "custom_genesis" / file)
|
||||||
|
var state = accounts.toState()
|
||||||
|
checkInvalidProofsWithBadStateRoot(accounts, state[0], state[1])
|
||||||
|
|
||||||
|
test "Invalid proofs with bad value":
|
||||||
|
for file in genesisFiles:
|
||||||
|
let accounts = getGenesisAlloc("fluffy" / "tests" / "custom_genesis" / file)
|
||||||
|
var state = accounts.toState()
|
||||||
|
checkInvalidProofsWithBadValue(accounts, state[0], state[1])
|
||||||
|
|
Loading…
Reference in New Issue