179 lines
6.8 KiB
Nim
179 lines
6.8 KiB
Nim
# Nimbus
|
|
# Copyright (c) 2023-2024 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/[common, rlp, trie, trie/trie_defs],
|
|
../../nimbus/common/chain_config,
|
|
../network/state/experimental/
|
|
[state_proof_types, 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.HexaryTrie.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.isErr()
|
|
|
|
accountState.HexaryTrie.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.isErr()
|
|
|
|
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])
|