Add improved mpt trie proof verification (#1223)

This commit is contained in:
KonradStaniec 2022-09-15 13:04:41 +02:00 committed by GitHub
parent b43709f473
commit 2503964149
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 50 additions and 68 deletions

View File

@ -78,7 +78,7 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
let proof = await lcProxy.client.eth_getProof(address, @[], blockId(blockNumber))
let proofValid = isAccountProofValid(
let accountResult = getAccountFromProof(
executionPayload.stateRoot,
proof.address,
proof.balance,
@ -88,10 +88,10 @@ proc installEthApiHandlers*(lcProxy: LightClientRpcProxy) =
proof.accountProof
)
if proofValid:
return encodeQuantity(proof.balance)
if accountResult.isOk():
return encodeQuantity(accountResult.get.balance)
else:
raise newException(ValueError, "Data provided by data provider server is invalid")
raise newException(ValueError, accountResult.error)
lcProxy.server.rpc("eth_getStorageAt") do(address: Address, slot: HexDataStr, quantityTag: string) -> HexDataStr:
checkPreconditions(payload, quantityTag)

View File

@ -38,7 +38,7 @@ suite "Merkle proof of inclusion validation":
]
check:
isAccountProofValid(
getAccountFromProof(
stateRoot,
address,
balance,
@ -46,7 +46,7 @@ suite "Merkle proof of inclusion validation":
codeHash,
storageRoot,
rlpNodes
)
).isOk()
test "Validate storage proof":
let slotValue = UInt256.fromHex("0x25a92a5853702f199bb2d805bba05d67025214a8")

View File

@ -14,7 +14,7 @@ import
eth/common/eth_types as etypes,
eth/common/eth_types_rlp,
eth/rlp,
eth/trie/hexary,
eth/trie/[hexary, hexary_proof_verification],
web3/ethtypes
export results
@ -22,6 +22,14 @@ export results
func toMDigest(arg: FixedBytes[32]): MDigest[256] =
MDigest[256](data: distinctBase(arg))
func emptyAccount(): etypes.Account =
return etypes.Account(
nonce: uint64(0),
balance: UInt256.zero,
storageRoot: etypes.EMPTY_ROOT_HASH,
codeHash: etypes.EMPTY_CODE_HASH
)
proc isValidProof(
branch: seq[seq[byte]],
rootHash: KeccakHash,
@ -34,7 +42,7 @@ proc isValidProof(
except RlpError:
return false
proc isAccountProofValidInternal(
proc getAccountFromProof*(
stateRoot: FixedBytes[32],
accountAddress: Address,
accountBalance: UInt256,
@ -42,7 +50,7 @@ proc isAccountProofValidInternal(
accountCodeHash: CodeHash,
accountStorageRootHash: StorageHash,
mptNodes: seq[RlpEncodedBytes]
): Option[etypes.Account] =
): Result[etypes.Account, string] =
let
mptNodesBytes = mptNodes.mapIt(distinctBase(it))
keccakStateRootHash = toMDigest(stateRoot)
@ -55,56 +63,45 @@ proc isAccountProofValidInternal(
accountEncoded = rlp.encode(acc)
accountKey = toSeq(keccakHash(distinctBase(accountAddress)).data)
let validProof = isValidProof(
let proofResult = verifyMptProof(
mptNodesBytes,
keccakStateRootHash,
accountKey,
accountEncoded
)
if validProof:
return some(acc)
else:
return none(etypes.Account)
case proofResult.kind
of MissingKey:
return ok(emptyAccount())
of ValidProof:
return ok(acc)
of InvalidProof:
return err(proofResult.errorMsg)
proc isAccountProofValid*(
stateRoot: FixedBytes[32],
accountAddress: Address,
accountBalance: UInt256,
accountNonce: Quantity,
accountCodeHash: CodeHash,
accountStorageRootHash: StorageHash,
mptNodes: seq[RlpEncodedBytes]
): bool =
let maybeAccount = isAccountProofValidInternal(
stateRoot,
accountAddress,
accountBalance,
accountNonce,
accountCodeHash,
accountStorageRootHash,
mptNodes
)
return maybeAccount.isSome()
proc isStorageProofValid(
proc getStorageData(
account: etypes.Account,
storageProof: StorageProof): bool =
storageProof: StorageProof): Result[UInt256, string] =
let
storageMptNodes = storageProof.proof.mapIt(distinctBase(it))
key = toSeq(keccakHash(toBytesBE(storageProof.key)).data)
encodedValue = rlp.encode(storageProof.value)
proofResult = verifyMptProof(storageMptNodes, account.storageRoot, key, encodedValue)
case proofResult.kind
of MissingKey:
return ok(UInt256.zero)
of ValidProof:
return ok(storageProof.value)
of InvalidProof:
return err(proofResult.errorMsg)
return isValidProof(storageMptNodes, account.storageRoot, key, encodedValue)
proc getStorageData*(
stateRoot: FixedBytes[32],
requestedSlot: UInt256,
proof: ProofResponse): Result[UInt256, string] =
let maybeAccount = isAccountProofValidInternal(
let account = ?getAccountFromProof(
stateRoot,
proof.address,
proof.balance,
@ -114,35 +111,20 @@ proc getStorageData*(
proof.accountProof
)
if maybeAccount.isSome():
let account = maybeAccount.unsafeGet()
if account.storageRoot == etypes.EMPTY_ROOT_HASH:
# valid account with empty storage, in that case getStorageAt
# return 0 value
return ok(u256(0))
if account.storageRoot == etypes.EMPTY_ROOT_HASH:
# valid account with empty storage, in that case getStorageAt
# return 0 value
return ok(u256(0))
if len(proof.storageProof) != 1:
return err("no storage proof for requested slot")
if len(proof.storageProof) != 1:
return err("no storage proof for requested slot")
let sproof = proof.storageProof[0]
let sproof = proof.storageProof[0]
if len(sproof.proof) == 0:
return err("empty mpt proof for account with not empty storage")
if len(sproof.proof) == 0:
return err("empty mpt proof for account with not empty storage")
if sproof.key != requestedSlot:
return err("received proof for invalid slot")
if sproof.key != requestedSlot:
return err("received proof for invalid slot")
if sproof.value == UInt256.zero:
# TODO: zero value means that storage is empty. We need to verify proof of
# no existance. As we currenctly do not have that ability just, return zero
# value
return ok(sproof.value)
if isStorageProofValid(account, sproof):
return ok(sproof.value)
else:
return err("invalid storage proof")
else:
return err("invalid account proof")
return getStorageData(account, sproof)

2
vendor/nim-eth vendored

@ -1 +1 @@
Subproject commit 5885f638e47b8607683ef9e1e77fc21ce1aede44
Subproject commit 059d319c16f9fd2391d0f2d22050dd4b30efb8e0