Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

mpt: simplify rangeProof handling & flatten mpt file structure #3740

Open
wants to merge 14 commits into
base: master
Choose a base branch
from
Open
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
15 changes: 9 additions & 6 deletions packages/client/src/sync/fetcher/accountfetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
setLengthLeft,
} from '@ethereumjs/util'
import debugDefault from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak.js'

import { Event } from '../../types.js'
import { short } from '../../util/index.js'
Expand Down Expand Up @@ -324,10 +323,15 @@
const keys = accounts.map((acc: any) => acc.hash)
const values = accounts.map((acc: any) => accountBodyToRLP(acc.body))
// convert the request to the right values
return verifyMerkleRangeProof(stateRoot, origin, keys[keys.length - 1], keys, values, proof, {
common: this.config.chainCommon,
useKeyHashingFunction: this.config.chainCommon?.customCrypto?.keccak256 ?? keccak256,
})
return verifyMerkleRangeProof(
stateRoot,
origin,
keys[keys.length - 1],
keys,
values,
proof,
this.config.chainCommon?.customCrypto?.keccak256,
)

Check warning on line 334 in packages/client/src/sync/fetcher/accountfetcher.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/sync/fetcher/accountfetcher.ts#L326-L334

Added lines #L326 - L334 were not covered by tests
}

private getOrigin(job: Job<JobTask, AccountData[], AccountData>): Uint8Array {
Expand Down Expand Up @@ -412,7 +416,6 @@
[],
[],
<any>rangeResult.proof,
{ useKeyHashingFunction: keccak256 },
)
// if proof is false, reject corrupt peer
if (isMissingRightRange !== false) return undefined
Expand Down
12 changes: 2 additions & 10 deletions packages/client/src/sync/fetcher/storagefetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
short,
} from '@ethereumjs/util'
import debugDefault from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak'

import { Fetcher } from './fetcher.js'
import { getInitFetcherDoneFlags } from './types.js'
Expand Down Expand Up @@ -135,10 +134,7 @@
keys,
values,
proof ?? null,
{
common: this.config.chainCommon,
useKeyHashingFunction: this.config.chainCommon?.customCrypto?.keccak256 ?? keccak256,
},
this.config.chainCommon?.customCrypto?.keccak256,

Check warning on line 137 in packages/client/src/sync/fetcher/storagefetcher.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/sync/fetcher/storagefetcher.ts#L137

Added line #L137 was not covered by tests
)
} catch (err) {
this.DEBUG && this.debug(`verifyRangeProof failure: ${(err as Error).stack}`)
Expand Down Expand Up @@ -287,7 +283,6 @@
[],
[],
<any>rangeResult.proof,
{ useKeyHashingFunction: keccak256 },
)

// if proof is false, reject corrupt peer
Expand Down Expand Up @@ -344,10 +339,7 @@
accountSlots.map((s) => s.hash),
accountSlots.map((s) => s.body),
null,
{
common: this.config.chainCommon,
useKeyHashingFunction: this.config.chainCommon?.customCrypto?.keccak256 ?? keccak256,
},
this.config.chainCommon?.customCrypto?.keccak256,

Check warning on line 342 in packages/client/src/sync/fetcher/storagefetcher.ts

View check run for this annotation

Codecov / codecov/patch

packages/client/src/sync/fetcher/storagefetcher.ts#L342

Added line #L342 was not covered by tests
)

if (proof?.length === 0)
Expand Down
7 changes: 1 addition & 6 deletions packages/client/test/net/protocol/snapprotocol.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,9 +191,7 @@ describe('[SnapProtocol]', () => {
try {
const keys = accounts.map((acc: any) => acc.hash)
const values = accounts.map((acc: any) => accountBodyToRLP(acc.body))
await verifyMerkleRangeProof(stateRoot, keys[0], keys[keys.length - 1], keys, values, proof, {
useKeyHashingFunction: keccak256,
})
await verifyMerkleRangeProof(stateRoot, keys[0], keys[keys.length - 1], keys, values, proof)
} catch (e) {
assert.fail(`AccountRange proof verification failed with message=${(e as Error).message}`)
}
Expand Down Expand Up @@ -331,9 +329,6 @@ describe('[SnapProtocol]', () => {
keys,
values,
proof,
{
useKeyHashingFunction: keccak256,
},
)
} catch (e) {
assert.fail(`StorageRange proof verification failed with message=${(e as Error).message}`)
Expand Down
File renamed without changes.
2 changes: 1 addition & 1 deletion packages/mpt/src/db/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export * from './checkpoint.js'
export * from './checkpointDB.js'
2 changes: 1 addition & 1 deletion packages/mpt/src/mpt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
import debug from 'debug'
import { keccak256 } from 'ethereum-cryptography/keccak.js'

import { CheckpointDB } from './db/index.js'
import { CheckpointDB } from './db/checkpointDB.js'
import {
BranchMPTNode,
ExtensionMPTNode,
Expand Down
1 change: 1 addition & 0 deletions packages/mpt/src/proof/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './proof.js'
export * from './range.js'
37 changes: 0 additions & 37 deletions packages/mpt/src/proof/proof.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import { bytesToHex, concatBytes, equalsBytes } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'

import { createMPTFromProof } from '../constructors.js'
import { MerklePatriciaTrie } from '../index.js'
import { bytesToNibbles } from '../util/nibbles.js'

import { verifyRangeProof } from './range.js'

import type { MPTOpts, Proof } from '../index.js'
import type { PutBatch } from '@ethereumjs/util'
Expand Down Expand Up @@ -34,39 +30,6 @@ export async function verifyMerkleProof(
}
}

// /**
// * A range proof is a proof that includes the encoded trie nodes from the root node to leaf node for one or more branches of a trie,
// * allowing an entire range of leaf nodes to be validated. This is useful in applications such as snap sync where contiguous ranges
// * of state trie data is received and validated for constructing world state, locally. Also see {@link verifyRangeProof}.
// * @param rootHash - root hash of state trie this proof is being verified against.
// * @param firstKey - first key of range being proven.
// * @param lastKey - last key of range being proven.
// * @param keys - key list of leaf data being proven.
// * @param values - value list of leaf data being proven, one-to-one correspondence with keys.
// * @param proof - proof node list, if all-elements-proof where no proof is needed, proof should be null, and both `firstKey` and `lastKey` must be null as well
// * @param opts - optional, the opts may include a custom hashing function to use with the trie for proof verification
// * @returns a flag to indicate whether there exists more trie node in the trie
// */
export function verifyMerkleRangeProof(
rootHash: Uint8Array,
firstKey: Uint8Array | null,
lastKey: Uint8Array | null,
keys: Uint8Array[],
values: Uint8Array[],
proof: Uint8Array[] | null,
opts?: MPTOpts,
): Promise<boolean> {
return verifyRangeProof(
rootHash,
firstKey && bytesToNibbles(firstKey),
lastKey && bytesToNibbles(lastKey),
keys.map((k) => k).map(bytesToNibbles),
values,
proof,
opts?.useKeyHashingFunction ?? keccak256,
)
}

/**
* Creates a proof from a trie and key that can be verified using {@link verifyMPTWithMerkleProof}. An (EIP-1186)[https://eips.ethereum.org/EIPS/eip-1186] proof contains
* the encoded trie nodes from the root node to the leaf node storing state data. The returned proof will be in the format of an array that contains Uint8Arrays of
Expand Down
27 changes: 18 additions & 9 deletions packages/mpt/src/proof/range.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { equalsBytes } from '@ethereumjs/util'
import { keccak256 } from 'ethereum-cryptography/keccak'

import { createMPTFromProof } from '../index.js'
import { MerklePatriciaTrie } from '../mpt.js'
import { BranchMPTNode, ExtensionMPTNode, LeafMPTNode } from '../node/index.js'
import { nibblesCompare, nibblesTypeToPackedBytes } from '../util/nibbles.js'
import { bytesToNibbles, nibblesCompare, nibblesTypeToPackedBytes } from '../util/nibbles.js'

import type { HashKeysFunction, MPTNode, Nibbles } from '../types.js'

Expand Down Expand Up @@ -386,9 +387,11 @@ async function hasRightElement(trie: MerklePatriciaTrie, key: Nibbles): Promise<
}

/**
* verifyRangeProof checks whether the given leaf nodes and edge proof
* can prove the given trie leaves range is matched with the specific root.
* Used internally by the verifyMerkleRangeProof wrapper function.
* Checks whether the given leaf nodes and edge proof can prove the given trie leaves range is matched with the specific root.
*
* A range proof is a proof that includes the encoded trie nodes from the root node to leaf node for one or more branches of a trie,
* allowing an entire range of leaf nodes to be validated. This is useful in applications such as snap sync where contiguous ranges
* of state trie data is received and validated for constructing world state, locally.
*
* There are four situations:
*
Expand All @@ -412,17 +415,23 @@ async function hasRightElement(trie: MerklePatriciaTrie, key: Nibbles): Promise<
* @param keys - key list of leaf data being proven.
* @param values - value list of leaf data being proven, one-to-one correspondence with keys.
* @param proof - proof node list, if all-elements-proof where no proof is needed, proof should be null, and both `firstKey` and `lastKey` must be null as well
* @param opts - optional, the opts may include a custom hashing function to use with the trie for proof verification
* @returns a flag to indicate whether there exists more trie node in the trie
*/
export async function verifyRangeProof(
export async function verifyMerkleRangeProof(
rootHash: Uint8Array,
firstKey: Nibbles | null,
lastKey: Nibbles | null,
keys: Nibbles[],
firstKeyRaw: Uint8Array | null,
lastKeyRaw: Uint8Array | null,
keysRaw: Uint8Array[],
values: Uint8Array[],
proof: Uint8Array[] | null,
useKeyHashingFunction: HashKeysFunction,
useKeyHashingFunction: HashKeysFunction = keccak256,
): Promise<boolean> {
// Convert Uint8Array keys to nibbles
const firstKey = firstKeyRaw !== null ? bytesToNibbles(firstKeyRaw) : null
const lastKey = lastKeyRaw !== null ? bytesToNibbles(lastKeyRaw) : null
const keys = keysRaw.map(bytesToNibbles)

if (keys.length !== values.length) {
throw new Error('invalid keys length or values length')
}
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export * from './accessWitness.js'
export * from './cache/index.js'
export * from './merkleStateManager.js'
export * from './proofs/index.js'
export * from './proof/index.js'
export * from './rpcStateManager.js'
export * from './simpleStateManager.js'
export * from './statefulVerkleStateManager.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/test/proofStateManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import { keccak256 } from 'ethereum-cryptography/keccak.js'
import { assert, describe, it } from 'vitest'

import { MerkleStateManager } from '../src/index.js'
import { getMerkleStateProof, verifyMerkleStateProof } from '../src/proofs/index.js'
import { getMerkleStateProof, verifyMerkleStateProof } from '../src/proof/index.js'

import { ropstenContractWithStorageData } from './testdata/ropsten_contractWithStorage.js'
import { ropstenNonexistentAccountData } from './testdata/ropsten_nonexistentAccount.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/test/rpcStateManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { createVM, runBlock, runTx } from '@ethereumjs/vm'
import { assert, describe, expect, it, vi } from 'vitest'

import { MerkleStateManager } from '../src/merkleStateManager.js'
import { getRPCStateProof } from '../src/proofs/index.js'
import { getRPCStateProof } from '../src/proof/index.js'
import { RPCBlockChain, RPCStateManager } from '../src/rpcStateManager.js'

import { block as blockData } from './testdata/providerData/blocks/block0x7a120.js'
Expand Down
2 changes: 1 addition & 1 deletion packages/statemanager/test/stateManager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {
addMerkleStateProofData,
fromMerkleStateProof,
getMerkleStateProof,
} from '../src/proofs/index.js'
} from '../src/proof/index.js'

import type { PrefixedHexString } from '@ethereumjs/util'

Expand Down
Loading