feat(trie): extend proof verification

This commit is contained in:
Nicolas Maurice 2025-02-05 13:35:19 +01:00
parent c6196fe9bc
commit 213c6eb51d
3 changed files with 215 additions and 23 deletions

View file

@ -111,31 +111,77 @@ func (t *StateTrie) Prove(key []byte, proofDb ethdb.KeyValueWriter) error {
return t.trie.Prove(key, proofDb) return t.trie.Prove(key, proofDb)
} }
// VerifyProof checks merkle proofs. The given proof must contain the value for // VerifyProof verifies a proof against a root hash and a key.
// key in a trie with the given root hash. VerifyProof returns an error if the //
// proof contains invalid trie nodes or the wrong value. // If the proof if valid it returns a nil error and
func VerifyProof(rootHash common.Hash, key []byte, proofDb ethdb.KeyValueReader) (value []byte, err error) { // - value, the value associated with the key, it is non-nil if the key exists in the trie (inclusion proof) and nil otherwise (exclusion proof)
// - longestPrefix, the longest prefix of the key that exists in the trie.
// - lastNode, the node (collapsed) at longestPrefix that either proves the inclusion or exclusion of the key.
func VerifyProof(root common.Hash, key []byte, proofDB ethdb.KeyValueReader) (value []byte, err error) {
value, _, _, err = VerifyProofWithReporting(root, key, proofDB, nil)
return value, err
}
// VerifyProof verifies a proof against a root hash and a key.
//
// If the proof if valid it returns a nil error and
// - value, the value associated with the key, it is non-nil if the key exists in the trie (inclusion proof) and nil otherwise (exclusion proof)
// - longestPrefix, the longest prefix of the key that exists in the trie.
// - lastNode, the node (collapsed) at longestPrefix that either proves the inclusion or exclusion of the key.
func VerifyProofWithLastNode(root common.Hash, key []byte, proofDB ethdb.KeyValueReader) (value, longestPrefix, lastNode []byte, err error) {
return VerifyProofWithReporting(root, key, proofDB, nil)
}
type ProofReporter struct {
ReportNode func(hash common.Hash, key, node []byte)
ReportLeaf func(parentHash common.Hash, leaf []byte)
}
func VerifyProofWithReporting(
root common.Hash,
key []byte,
proofDB ethdb.KeyValueReader,
reporter *ProofReporter,
) (value, longestPrefix, lastNode []byte, err error) {
key = keybytesToHex(key) key = keybytesToHex(key)
wantHash := rootHash wantHash := root
nodeKey := []byte{}
hasher := newHasher(false)
defer returnHasherToPool(hasher)
for i := 0; ; i++ { for i := 0; ; i++ {
buf, _ := proofDb.Get(wantHash[:]) buf, _ := proofDB.Get(wantHash[:])
if buf == nil { if buf == nil {
return nil, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash) return nil, nil, nil, fmt.Errorf("proof node %d (hash %064x) missing", i, wantHash)
} }
n, err := decodeNode(wantHash[:], buf) n, err := decodeNode(wantHash[:], buf)
if err != nil { if err != nil {
return nil, fmt.Errorf("bad proof node %d: %v", i, err) return nil, nil, nil, fmt.Errorf("bad proof node %d: %v", i, err)
} }
keyrest, cld := get(n, key, true)
if reporter != nil && reporter.ReportNode != nil {
reporter.ReportNode(wantHash, nodeKey, buf)
}
keyrest, keyvisited, cld := get(n, key, true)
nodeKey = append(nodeKey, keyvisited...)
switch cld := cld.(type) { switch cld := cld.(type) {
case nil: case nil:
// The trie doesn't contain the key. // The trie doesn't contain the key.
return nil, nil collapsed, _ := hasher.proofHash(n)
return nil, longestPrefix, nodeToBytes(collapsed), nil
case hashNode: case hashNode:
key = keyrest key = keyrest
longestPrefix = append(longestPrefix, keyvisited...)
copy(wantHash[:], cld) copy(wantHash[:], cld)
case valueNode: case valueNode:
return cld, nil collapsed, _ := hasher.proofHash(n)
if reporter != nil && reporter.ReportLeaf != nil {
reporter.ReportLeaf(wantHash, cld)
}
return cld, longestPrefix, nodeToBytes(collapsed), nil
} }
} }
} }
@ -175,7 +221,7 @@ func proofToPath(rootHash common.Hash, root node, key []byte, proofDb ethdb.KeyV
) )
key, parent = keybytesToHex(key), root key, parent = keybytesToHex(key), root
for { for {
keyrest, child = get(parent, key, false) keyrest, _, child = get(parent, key, false)
switch cld := child.(type) { switch cld := child.(type) {
case nil: case nil:
// The trie doesn't contain the key. It's possible // The trie doesn't contain the key. It's possible
@ -583,30 +629,34 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
// //
// There is an additional flag `skipResolved`. If it's set then // There is an additional flag `skipResolved`. If it's set then
// all resolved nodes won't be returned. // all resolved nodes won't be returned.
func get(tn node, key []byte, skipResolved bool) ([]byte, node) { func get(tn node, key []byte, skipResolved bool) (keyrest, keyvisited []byte, n node) {
keyrest = key
keyvisited = []byte{}
for { for {
switch n := tn.(type) { switch n := tn.(type) {
case *shortNode: case *shortNode:
if !bytes.HasPrefix(key, n.Key) { if !bytes.HasPrefix(keyrest, n.Key) {
return nil, nil return keyrest, keyvisited, nil
} }
tn = n.Val tn = n.Val
key = key[len(n.Key):] keyvisited = n.Key[:]
keyrest = keyrest[len(n.Key):]
if !skipResolved { if !skipResolved {
return key, tn return keyrest, keyvisited, tn
} }
case *fullNode: case *fullNode:
tn = n.Children[key[0]] tn = n.Children[keyrest[0]]
key = key[1:] keyvisited = []byte{keyrest[0]}
keyrest = keyrest[1:]
if !skipResolved { if !skipResolved {
return key, tn return keyrest, keyvisited, tn
} }
case hashNode: case hashNode:
return key, n return keyrest, keyvisited, n
case nil: case nil:
return key, nil return keyrest, keyvisited, nil
case valueNode: case valueNode:
return nil, n return nil, keyvisited, n
default: default:
panic(fmt.Sprintf("%T: invalid node: %v", tn, tn)) panic(fmt.Sprintf("%T: invalid node: %v", tn, tn))
} }

View file

@ -26,9 +26,12 @@ import (
"testing" "testing"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core/rawdb" "github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb/memorydb" "github.com/ethereum/go-ethereum/ethdb/memorydb"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
) )
// Prng is a pseudo random number generator seeded by strong randomness. // Prng is a pseudo random number generator seeded by strong randomness.
@ -93,6 +96,112 @@ func TestProof(t *testing.T) {
} }
} }
func TestVerifyProofWithLastNode(t *testing.T) {
testCase := []struct {
desc string
root string
key string
proof []string
value string
expectedError error
expextedValue string
expectedLongestPrefixPrefix string
expectedLastNode string
}{
{
desc: "Valid inclusion proof",
root: "0x6f39539da0b571e36e04cdee1ef9273ce168644d63822352f3a18c0504220166",
key: "0xab14d68802a763f7db875346d03fbf86f137de55814b191c069e721f47474733",
proof: []string{
"0xf90211a026b1f06f4804f169f036ab0cbe42d00ac13da34d6ff9af107d1af41d425eef8aa097d5cf7afeb561c87f064e5055cb5d6634cb7f6e93636b5d2c819b73db9f60f2a05d21cb1cb916ce9e57594a3c31058d2a936a5d7f790da95c5fab850252ff93a1a0fcf8847652abe31e2c5096e87128d93fb2de640534deffd530c82dff3d8f30fba026930d0a381d96f36abdb8857d6d8e91c0acd22b2ba78399e573798622638067a0417e2530f32e6c01a6cbdda472b853b98076436110df109ffad71e9b1a685feaa04f9c68013dc5f94ec91621bda78d233590b5d91f846698e60b42b28e0c87d935a0a06147fa3e0b0272661f8402f048b2cf7061617684bf44d4154d889c3f5119faa039164ab7071740ceeeb687f999251decfbe781ac0fd31cb63624c2f4b99bca7fa089443797be9cb0c265caeabcef452c23fbfcbac12cfe58a31f7d3ad6fb0d53f1a08357d7d9cf194e90858d2186918f396ed971062fcf939b78cd5b43e7fb44eb9ca0f0e23f2080dcd0af55e7ef37a9ccab09716a4c31e0b1b2172ac416c53df675a4a0ba840647ec3e2f7d38317c7dc0a6f8e3ae14b398c454ce9e76b0232d782f66caa081b03c3a644f130eb88395dac6ecad40b1ba14826fa4fef38f82ef2fa63e2aeea01b356b63266c0771c8167162e5472815b7836bf50dc1b5d70ee982ddc619b719a08878b527ef493b8b571b73b5747370e12e1fe09dd003546b9755e39c9f98050880",
"0xf90211a09a94b4b8ab62c73bec14fd530e704e997b09f51aa9b316cc9a3b5ad0164e2530a0dca0351f0790d336e42aca22aabdbb4be8ac126c7e95fba25b55a78179cf54eba004b4813a8cad85b721321b6738f362f79911db78db50ec2571873cc1e4b00512a0bcc74ae61638d722a57cb0507bd0f30dbef47f591df032a5422eb8b3aaa015d1a0a4f53957bf35159735d8902e208471ecfa0e31ccc6e577e9b56f81d061719de6a0e77e0548e98157dc2f18b9f694965df88abf3c78b3090990c329264f2ac84325a0672727f7669561b1ddd33117f51146d4712af029e26cf1fce30608a7e8f4291ea088827c096ac6ce1fd783b9e418bb37711e6b0b34b429e5b7a321fac508d1a8aea03030c43045ea3ac75803b189a7132d88d0956a86424e3207ee38a46f2f850566a0298e2ca265c46fd3096368b732a77977211a0c55d1246dbd990111cb26fc4c66a07afd6cb69666868b214b594819385a9c51ba41ce927b5e83e0282f258be4ec9ea090d5e48e6b82c7d3963a1a944575160cf6297e03589096defc75ee8ce3cae787a0d65839513fe22a30a61cd19481e3c0ccae670d0c5de3ce6427f862fa74b8f5b9a0e3ba073d7f8cbcc33cb8a9d7254eff98d4230960e8e02eaf189da50afc52550fa0bfb620535240603e78e23975fa04ec78737125c57ef0e1295ae4791dbd72eb98a0dc02979f215e06a5c38819bd42767a8e1856ac98d0eebddd136bc18395e01f9780",
"0xf90211a0b45de0172eb2119f86e9a53414150582d4052e90d9c6bbdd124d17e90a8118c5a05da2d2141759d4ef34f007c423b60b148d190d35d04f261cf5e657e4549782e8a0acbb3eef7fd8fd5f484aed84d2cb3416e46599f6d29efc6be6c63666f72e8e9ca02ab8b018174045d2c79ae86ef00d70c7cdd964cfe225ce36c14506b88a2f1a96a031d430d208f212e6100cfe74c4c694fa18c55bc0834f6ebec8a7d413039bfb63a05dec69eb4414309d3e7cd09c929692fe28b1a0cb116635d0b57e9f712716ebb0a01205d9623ad31d97cbd6ef3c20008284386ddf66aee79984a8c006b9d411d46ea089f83ffcdedd51ca1ac27376be77c5c1ca9aa5712aba25e0f82c8c39f2e8b6c4a08a9dcb56eb25df8cbca5f7b18e43b7f6bbf70571c9ad924a1791e5aa55df2331a0e1fca8262813be4837902a1fb24584e3f504af383dc84aa7838d0369c942217aa0adfc616da1e64c42b4cb11886ccf6642222b43bff8d266caeefc572ca3e0fb65a01f5c53e0c2a0109df11c313845b7e46910004981044de1772aabba746d2ce9a5a0dd357d528f9f34a8a3ae37358496f91675b47f6133e6bd0568d581a840c6eeb4a09ece58670426075fec57d32fbac503973288de9eeb3545175b64ebe1cfad9930a0d3c0d1c48df816d9a5dc772d7f464e8872d49dbe6fcea0944b56663514237f84a066ab6c2f3748861ad36c6d19f8b2cc0ee17fca96ea862e4d3ce3beac233a75c980",
"0xf90211a042ab718a6fbb71add03da39ed57525bddd6f8455f9e77ce7f33d603cd86b791aa080b40e9fc893fc8725853fb11b6e2665376a790ff68ef273c93f0b7025fccb28a00defb48ea51e2fdb1d6f353dc5724796c073d8d80cb38d517020200e39b23051a0236e4a72db3ea239bf7edc82e7f2e9276a0f05e6bd6309023a65eb2b8fa2559fa0bcae7ef6000f79902f615489ceb4a727b39326acdd4462664503fceb4a87d36ba06a1b1da69a20a57324962130a82d2de2ad7783f87e49c1536ae23912c9e8c866a098792dc09ed13762e2bcb1c21590395500fe3162e8b9b02433e821cdeff478b5a05bcf60cfe56c1d9d6a2d63a9e0a8e91f35ac851526159bc27ef3b989daa68b4aa03d93b7e3802a49e207f8aaa619dedd7a1d8f2be43ace5b641611c66a78dde10aa077a649d238a2c0fdcd00fa8058601d1d72437ee4fafd948c34d82475fbea902aa0fa62b7a999a0584fe8a57056c60257fc24d5c5bd59a6ac04cc0e23c2356555f4a0570a6bb6e76afeb523d7536b08a5f064675e8731daf902d12bd0c4472626f6aea07fdcf35ae85861ed95f3375ae18dc5ea4518b056a8ac157c32bb8abd2a147d53a04a413bcb2c143a4038755095c087593a197d15f31ee4c11399021d0928f7d50ea0be95324a2306fcf30b974a1283e2df2d7562c55875343ae1e29fa7a67e835fe8a0e05a31ba76f7ddd817b7010f427234b38fbd2f12a03c222dde19ecee94dcb43080",
"0xf90211a056527244566aed48654b0ca3e95225defe8b8eaee17657bace6698d7744b06cda0118221e9076b2fdf85d2371af95c10b9e9539a9aa3e3f6947acc0c7958bbdeeda0eed1a6e3b203d643cf0b3349816a78974046a45972cce621ea4a1979c29c632ca0414fd647c29c1323fe866af7f73d88c7ba93ba3d3eedea38aebd7a800c34ac75a0595cc2c4b2d314f1c12e7407732c4040bec3b269f499011c733b88f63007ae75a0836b374e20ca7ed03b5c5a447b6e15a39c0d037a82a429f8e099a06a24a372eaa085bcaf2bf96c47456365f4b28869670dee8d910c69ef7c75facc1c6e8e8983bfa0540629174895cf121ea76f7b63647bd9c46378184aeff99c81be646e0e6b80bca03048c27521797b94c190972758f2592a298af84bc14a2af2d1cfdcd00dc4b4a3a04456762d4c42229917c26b66106d1b354d12de086b65ad9c4ac0c97ae1fee28ba07b5ec4d7acfb65d85ce3c2c5c8528ea71e03997ddfe4a849cf02af2b61334276a0d49449f71c85a405d1c3ac16e903f8e2333c5e86c2b294e624fb0084317e4183a09d66aac7c383422398f421567ad2b103ce002961e973c8321e03b8e26bbb4c9da0cbccafd20b1c1162663b03bf8387639db8b1472d344dbda2f85805ea3f7061dca0402abdf44ab8e0971f58fadece8c64c1d9fabe5b2bc9ffaa30c847df16613520a03e3e95cd292c1fc017d5386e0c65923e423cddafe918bc50d6cd4ba96e8a4fe580",
"0xf90211a054ecd08900814f3b9e373cbc4ea3701007e84703bc5af9cfaa9e1711fd3f0319a09f9261744412d1946bcd933e1a6d53102186b2d99ecff30e403feea9841cf053a067f9200eef6cd28b708d6292b57e2a42b577874e5b1cbd406741e61a483e6b7aa081b4a58834d68fdb58de1b2f4b422b06f6094ceea952f8d0bf4746d743eefd70a02f59d7a6832a735d3116657cb0941143aa82d9c6d38efa250bf5827071bf63e4a09007eccc09dc7929e3574e1612a8867660accce899f70546173cbe30b109ea94a08d3f6cbecc09531cb79bdde35403a8b1192cfcdf6f0aca0a4429e5708920a254a031f06093af171b8380db7bff2e61463f6c4dc01c8d7e7181cf642bad24202357a0a60e1315be595edd2f36b0c8070638278ebfc3d1b02d9ad4828a690564f98c8ea0e4e3830e486521cc62bc05be33fb82dc976be5f77031cb9cff945c6fd9c07f6ba043a6e474ef36831d726f8260533cde7ee60409a3ebfc4660f0a0ec15f36098dda01af29c4ae492c1ef17d49e759c535e83a0f10e5be08e9218ad50a9a18a058604a096d3e05bb50ee4b1cec89a4eefea21fc8939fa5d1d698dddee320266d3245853a0ad45735c19f6fdd0d89dbbaa7aae4599b0e3fb099ae36b48410fb55231a1facca09cd3816e190ebd4bfefeb9cbff3ac539ad05ace88ca3dbae324326b61b52b2b4a0084ed25dc92cd64bf00f64818b2d4a3655c038035abe7daf5b04b9949522263780",
"0xf90191a00a7a0118e00981ab321049c9d340cd52c3a4781037540f7c48d0fdc27e899b3280a08537f2e248702a6ae2a57e9110a5740f5772c876389739ac90debd6a0692713ea00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717fa0a9a54d84976d134d6dba06a65064c7f3a964a75947d452db6f6bb4b6c47b43aaa01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f046740066461a098be0d129102f96ae636f9928911ed66103ac7c62235241aa0ef7e5b4b7e289ea07da2bce701255847cf5169ba5a7578a9700133f7ce13fa26a1d4097c20d1e0fda00631e0c3a589b999513f1d44a9b11e53d81027c1019553b4100542dbcb21031fa0c8d71dd13d2806e2865a5c2cfa447f626471bf0b66182a8fd07230434e1cad2680a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a0c69d174ad6b6e58b0bd05914352839ec60915cd066dd2bee2a48016139687f21a0513dd5514fd6bad56871711441d38de2821cc6913cb192416b0385f025650731808080",
"0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
},
expextedValue: "0xf8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
expectedLongestPrefixPrefix: "ab14d68",
expectedLastNode: "0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
},
{
desc: "Valid exclusion proof #1",
root: "0x6f39539da0b571e36e04cdee1ef9273ce168644d63822352f3a18c0504220166",
key: "0xab14d68802a763f7db875346d000000000000000000000000000000000000000",
proof: []string{
"0xf90211a026b1f06f4804f169f036ab0cbe42d00ac13da34d6ff9af107d1af41d425eef8aa097d5cf7afeb561c87f064e5055cb5d6634cb7f6e93636b5d2c819b73db9f60f2a05d21cb1cb916ce9e57594a3c31058d2a936a5d7f790da95c5fab850252ff93a1a0fcf8847652abe31e2c5096e87128d93fb2de640534deffd530c82dff3d8f30fba026930d0a381d96f36abdb8857d6d8e91c0acd22b2ba78399e573798622638067a0417e2530f32e6c01a6cbdda472b853b98076436110df109ffad71e9b1a685feaa04f9c68013dc5f94ec91621bda78d233590b5d91f846698e60b42b28e0c87d935a0a06147fa3e0b0272661f8402f048b2cf7061617684bf44d4154d889c3f5119faa039164ab7071740ceeeb687f999251decfbe781ac0fd31cb63624c2f4b99bca7fa089443797be9cb0c265caeabcef452c23fbfcbac12cfe58a31f7d3ad6fb0d53f1a08357d7d9cf194e90858d2186918f396ed971062fcf939b78cd5b43e7fb44eb9ca0f0e23f2080dcd0af55e7ef37a9ccab09716a4c31e0b1b2172ac416c53df675a4a0ba840647ec3e2f7d38317c7dc0a6f8e3ae14b398c454ce9e76b0232d782f66caa081b03c3a644f130eb88395dac6ecad40b1ba14826fa4fef38f82ef2fa63e2aeea01b356b63266c0771c8167162e5472815b7836bf50dc1b5d70ee982ddc619b719a08878b527ef493b8b571b73b5747370e12e1fe09dd003546b9755e39c9f98050880",
"0xf90211a09a94b4b8ab62c73bec14fd530e704e997b09f51aa9b316cc9a3b5ad0164e2530a0dca0351f0790d336e42aca22aabdbb4be8ac126c7e95fba25b55a78179cf54eba004b4813a8cad85b721321b6738f362f79911db78db50ec2571873cc1e4b00512a0bcc74ae61638d722a57cb0507bd0f30dbef47f591df032a5422eb8b3aaa015d1a0a4f53957bf35159735d8902e208471ecfa0e31ccc6e577e9b56f81d061719de6a0e77e0548e98157dc2f18b9f694965df88abf3c78b3090990c329264f2ac84325a0672727f7669561b1ddd33117f51146d4712af029e26cf1fce30608a7e8f4291ea088827c096ac6ce1fd783b9e418bb37711e6b0b34b429e5b7a321fac508d1a8aea03030c43045ea3ac75803b189a7132d88d0956a86424e3207ee38a46f2f850566a0298e2ca265c46fd3096368b732a77977211a0c55d1246dbd990111cb26fc4c66a07afd6cb69666868b214b594819385a9c51ba41ce927b5e83e0282f258be4ec9ea090d5e48e6b82c7d3963a1a944575160cf6297e03589096defc75ee8ce3cae787a0d65839513fe22a30a61cd19481e3c0ccae670d0c5de3ce6427f862fa74b8f5b9a0e3ba073d7f8cbcc33cb8a9d7254eff98d4230960e8e02eaf189da50afc52550fa0bfb620535240603e78e23975fa04ec78737125c57ef0e1295ae4791dbd72eb98a0dc02979f215e06a5c38819bd42767a8e1856ac98d0eebddd136bc18395e01f9780",
"0xf90211a0b45de0172eb2119f86e9a53414150582d4052e90d9c6bbdd124d17e90a8118c5a05da2d2141759d4ef34f007c423b60b148d190d35d04f261cf5e657e4549782e8a0acbb3eef7fd8fd5f484aed84d2cb3416e46599f6d29efc6be6c63666f72e8e9ca02ab8b018174045d2c79ae86ef00d70c7cdd964cfe225ce36c14506b88a2f1a96a031d430d208f212e6100cfe74c4c694fa18c55bc0834f6ebec8a7d413039bfb63a05dec69eb4414309d3e7cd09c929692fe28b1a0cb116635d0b57e9f712716ebb0a01205d9623ad31d97cbd6ef3c20008284386ddf66aee79984a8c006b9d411d46ea089f83ffcdedd51ca1ac27376be77c5c1ca9aa5712aba25e0f82c8c39f2e8b6c4a08a9dcb56eb25df8cbca5f7b18e43b7f6bbf70571c9ad924a1791e5aa55df2331a0e1fca8262813be4837902a1fb24584e3f504af383dc84aa7838d0369c942217aa0adfc616da1e64c42b4cb11886ccf6642222b43bff8d266caeefc572ca3e0fb65a01f5c53e0c2a0109df11c313845b7e46910004981044de1772aabba746d2ce9a5a0dd357d528f9f34a8a3ae37358496f91675b47f6133e6bd0568d581a840c6eeb4a09ece58670426075fec57d32fbac503973288de9eeb3545175b64ebe1cfad9930a0d3c0d1c48df816d9a5dc772d7f464e8872d49dbe6fcea0944b56663514237f84a066ab6c2f3748861ad36c6d19f8b2cc0ee17fca96ea862e4d3ce3beac233a75c980",
"0xf90211a042ab718a6fbb71add03da39ed57525bddd6f8455f9e77ce7f33d603cd86b791aa080b40e9fc893fc8725853fb11b6e2665376a790ff68ef273c93f0b7025fccb28a00defb48ea51e2fdb1d6f353dc5724796c073d8d80cb38d517020200e39b23051a0236e4a72db3ea239bf7edc82e7f2e9276a0f05e6bd6309023a65eb2b8fa2559fa0bcae7ef6000f79902f615489ceb4a727b39326acdd4462664503fceb4a87d36ba06a1b1da69a20a57324962130a82d2de2ad7783f87e49c1536ae23912c9e8c866a098792dc09ed13762e2bcb1c21590395500fe3162e8b9b02433e821cdeff478b5a05bcf60cfe56c1d9d6a2d63a9e0a8e91f35ac851526159bc27ef3b989daa68b4aa03d93b7e3802a49e207f8aaa619dedd7a1d8f2be43ace5b641611c66a78dde10aa077a649d238a2c0fdcd00fa8058601d1d72437ee4fafd948c34d82475fbea902aa0fa62b7a999a0584fe8a57056c60257fc24d5c5bd59a6ac04cc0e23c2356555f4a0570a6bb6e76afeb523d7536b08a5f064675e8731daf902d12bd0c4472626f6aea07fdcf35ae85861ed95f3375ae18dc5ea4518b056a8ac157c32bb8abd2a147d53a04a413bcb2c143a4038755095c087593a197d15f31ee4c11399021d0928f7d50ea0be95324a2306fcf30b974a1283e2df2d7562c55875343ae1e29fa7a67e835fe8a0e05a31ba76f7ddd817b7010f427234b38fbd2f12a03c222dde19ecee94dcb43080",
"0xf90211a056527244566aed48654b0ca3e95225defe8b8eaee17657bace6698d7744b06cda0118221e9076b2fdf85d2371af95c10b9e9539a9aa3e3f6947acc0c7958bbdeeda0eed1a6e3b203d643cf0b3349816a78974046a45972cce621ea4a1979c29c632ca0414fd647c29c1323fe866af7f73d88c7ba93ba3d3eedea38aebd7a800c34ac75a0595cc2c4b2d314f1c12e7407732c4040bec3b269f499011c733b88f63007ae75a0836b374e20ca7ed03b5c5a447b6e15a39c0d037a82a429f8e099a06a24a372eaa085bcaf2bf96c47456365f4b28869670dee8d910c69ef7c75facc1c6e8e8983bfa0540629174895cf121ea76f7b63647bd9c46378184aeff99c81be646e0e6b80bca03048c27521797b94c190972758f2592a298af84bc14a2af2d1cfdcd00dc4b4a3a04456762d4c42229917c26b66106d1b354d12de086b65ad9c4ac0c97ae1fee28ba07b5ec4d7acfb65d85ce3c2c5c8528ea71e03997ddfe4a849cf02af2b61334276a0d49449f71c85a405d1c3ac16e903f8e2333c5e86c2b294e624fb0084317e4183a09d66aac7c383422398f421567ad2b103ce002961e973c8321e03b8e26bbb4c9da0cbccafd20b1c1162663b03bf8387639db8b1472d344dbda2f85805ea3f7061dca0402abdf44ab8e0971f58fadece8c64c1d9fabe5b2bc9ffaa30c847df16613520a03e3e95cd292c1fc017d5386e0c65923e423cddafe918bc50d6cd4ba96e8a4fe580",
"0xf90211a054ecd08900814f3b9e373cbc4ea3701007e84703bc5af9cfaa9e1711fd3f0319a09f9261744412d1946bcd933e1a6d53102186b2d99ecff30e403feea9841cf053a067f9200eef6cd28b708d6292b57e2a42b577874e5b1cbd406741e61a483e6b7aa081b4a58834d68fdb58de1b2f4b422b06f6094ceea952f8d0bf4746d743eefd70a02f59d7a6832a735d3116657cb0941143aa82d9c6d38efa250bf5827071bf63e4a09007eccc09dc7929e3574e1612a8867660accce899f70546173cbe30b109ea94a08d3f6cbecc09531cb79bdde35403a8b1192cfcdf6f0aca0a4429e5708920a254a031f06093af171b8380db7bff2e61463f6c4dc01c8d7e7181cf642bad24202357a0a60e1315be595edd2f36b0c8070638278ebfc3d1b02d9ad4828a690564f98c8ea0e4e3830e486521cc62bc05be33fb82dc976be5f77031cb9cff945c6fd9c07f6ba043a6e474ef36831d726f8260533cde7ee60409a3ebfc4660f0a0ec15f36098dda01af29c4ae492c1ef17d49e759c535e83a0f10e5be08e9218ad50a9a18a058604a096d3e05bb50ee4b1cec89a4eefea21fc8939fa5d1d698dddee320266d3245853a0ad45735c19f6fdd0d89dbbaa7aae4599b0e3fb099ae36b48410fb55231a1facca09cd3816e190ebd4bfefeb9cbff3ac539ad05ace88ca3dbae324326b61b52b2b4a0084ed25dc92cd64bf00f64818b2d4a3655c038035abe7daf5b04b9949522263780",
"0xf90191a00a7a0118e00981ab321049c9d340cd52c3a4781037540f7c48d0fdc27e899b3280a08537f2e248702a6ae2a57e9110a5740f5772c876389739ac90debd6a0692713ea00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717fa0a9a54d84976d134d6dba06a65064c7f3a964a75947d452db6f6bb4b6c47b43aaa01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f046740066461a098be0d129102f96ae636f9928911ed66103ac7c62235241aa0ef7e5b4b7e289ea07da2bce701255847cf5169ba5a7578a9700133f7ce13fa26a1d4097c20d1e0fda00631e0c3a589b999513f1d44a9b11e53d81027c1019553b4100542dbcb21031fa0c8d71dd13d2806e2865a5c2cfa447f626471bf0b66182a8fd07230434e1cad2680a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a0c69d174ad6b6e58b0bd05914352839ec60915cd066dd2bee2a48016139687f21a0513dd5514fd6bad56871711441d38de2821cc6913cb192416b0385f025650731808080",
"0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
},
expextedValue: "0x",
expectedLongestPrefixPrefix: "ab14d68",
expectedLastNode: "0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
},
{
desc: "Valid exclusion proof #2",
root: "0x6f39539da0b571e36e04cdee1ef9273ce168644d63822352f3a18c0504220166",
key: "0xab14d6f000000000000000000000000000000000000000000000000000000000",
proof: []string{
"0xf90211a026b1f06f4804f169f036ab0cbe42d00ac13da34d6ff9af107d1af41d425eef8aa097d5cf7afeb561c87f064e5055cb5d6634cb7f6e93636b5d2c819b73db9f60f2a05d21cb1cb916ce9e57594a3c31058d2a936a5d7f790da95c5fab850252ff93a1a0fcf8847652abe31e2c5096e87128d93fb2de640534deffd530c82dff3d8f30fba026930d0a381d96f36abdb8857d6d8e91c0acd22b2ba78399e573798622638067a0417e2530f32e6c01a6cbdda472b853b98076436110df109ffad71e9b1a685feaa04f9c68013dc5f94ec91621bda78d233590b5d91f846698e60b42b28e0c87d935a0a06147fa3e0b0272661f8402f048b2cf7061617684bf44d4154d889c3f5119faa039164ab7071740ceeeb687f999251decfbe781ac0fd31cb63624c2f4b99bca7fa089443797be9cb0c265caeabcef452c23fbfcbac12cfe58a31f7d3ad6fb0d53f1a08357d7d9cf194e90858d2186918f396ed971062fcf939b78cd5b43e7fb44eb9ca0f0e23f2080dcd0af55e7ef37a9ccab09716a4c31e0b1b2172ac416c53df675a4a0ba840647ec3e2f7d38317c7dc0a6f8e3ae14b398c454ce9e76b0232d782f66caa081b03c3a644f130eb88395dac6ecad40b1ba14826fa4fef38f82ef2fa63e2aeea01b356b63266c0771c8167162e5472815b7836bf50dc1b5d70ee982ddc619b719a08878b527ef493b8b571b73b5747370e12e1fe09dd003546b9755e39c9f98050880",
"0xf90211a09a94b4b8ab62c73bec14fd530e704e997b09f51aa9b316cc9a3b5ad0164e2530a0dca0351f0790d336e42aca22aabdbb4be8ac126c7e95fba25b55a78179cf54eba004b4813a8cad85b721321b6738f362f79911db78db50ec2571873cc1e4b00512a0bcc74ae61638d722a57cb0507bd0f30dbef47f591df032a5422eb8b3aaa015d1a0a4f53957bf35159735d8902e208471ecfa0e31ccc6e577e9b56f81d061719de6a0e77e0548e98157dc2f18b9f694965df88abf3c78b3090990c329264f2ac84325a0672727f7669561b1ddd33117f51146d4712af029e26cf1fce30608a7e8f4291ea088827c096ac6ce1fd783b9e418bb37711e6b0b34b429e5b7a321fac508d1a8aea03030c43045ea3ac75803b189a7132d88d0956a86424e3207ee38a46f2f850566a0298e2ca265c46fd3096368b732a77977211a0c55d1246dbd990111cb26fc4c66a07afd6cb69666868b214b594819385a9c51ba41ce927b5e83e0282f258be4ec9ea090d5e48e6b82c7d3963a1a944575160cf6297e03589096defc75ee8ce3cae787a0d65839513fe22a30a61cd19481e3c0ccae670d0c5de3ce6427f862fa74b8f5b9a0e3ba073d7f8cbcc33cb8a9d7254eff98d4230960e8e02eaf189da50afc52550fa0bfb620535240603e78e23975fa04ec78737125c57ef0e1295ae4791dbd72eb98a0dc02979f215e06a5c38819bd42767a8e1856ac98d0eebddd136bc18395e01f9780",
"0xf90211a0b45de0172eb2119f86e9a53414150582d4052e90d9c6bbdd124d17e90a8118c5a05da2d2141759d4ef34f007c423b60b148d190d35d04f261cf5e657e4549782e8a0acbb3eef7fd8fd5f484aed84d2cb3416e46599f6d29efc6be6c63666f72e8e9ca02ab8b018174045d2c79ae86ef00d70c7cdd964cfe225ce36c14506b88a2f1a96a031d430d208f212e6100cfe74c4c694fa18c55bc0834f6ebec8a7d413039bfb63a05dec69eb4414309d3e7cd09c929692fe28b1a0cb116635d0b57e9f712716ebb0a01205d9623ad31d97cbd6ef3c20008284386ddf66aee79984a8c006b9d411d46ea089f83ffcdedd51ca1ac27376be77c5c1ca9aa5712aba25e0f82c8c39f2e8b6c4a08a9dcb56eb25df8cbca5f7b18e43b7f6bbf70571c9ad924a1791e5aa55df2331a0e1fca8262813be4837902a1fb24584e3f504af383dc84aa7838d0369c942217aa0adfc616da1e64c42b4cb11886ccf6642222b43bff8d266caeefc572ca3e0fb65a01f5c53e0c2a0109df11c313845b7e46910004981044de1772aabba746d2ce9a5a0dd357d528f9f34a8a3ae37358496f91675b47f6133e6bd0568d581a840c6eeb4a09ece58670426075fec57d32fbac503973288de9eeb3545175b64ebe1cfad9930a0d3c0d1c48df816d9a5dc772d7f464e8872d49dbe6fcea0944b56663514237f84a066ab6c2f3748861ad36c6d19f8b2cc0ee17fca96ea862e4d3ce3beac233a75c980",
"0xf90211a042ab718a6fbb71add03da39ed57525bddd6f8455f9e77ce7f33d603cd86b791aa080b40e9fc893fc8725853fb11b6e2665376a790ff68ef273c93f0b7025fccb28a00defb48ea51e2fdb1d6f353dc5724796c073d8d80cb38d517020200e39b23051a0236e4a72db3ea239bf7edc82e7f2e9276a0f05e6bd6309023a65eb2b8fa2559fa0bcae7ef6000f79902f615489ceb4a727b39326acdd4462664503fceb4a87d36ba06a1b1da69a20a57324962130a82d2de2ad7783f87e49c1536ae23912c9e8c866a098792dc09ed13762e2bcb1c21590395500fe3162e8b9b02433e821cdeff478b5a05bcf60cfe56c1d9d6a2d63a9e0a8e91f35ac851526159bc27ef3b989daa68b4aa03d93b7e3802a49e207f8aaa619dedd7a1d8f2be43ace5b641611c66a78dde10aa077a649d238a2c0fdcd00fa8058601d1d72437ee4fafd948c34d82475fbea902aa0fa62b7a999a0584fe8a57056c60257fc24d5c5bd59a6ac04cc0e23c2356555f4a0570a6bb6e76afeb523d7536b08a5f064675e8731daf902d12bd0c4472626f6aea07fdcf35ae85861ed95f3375ae18dc5ea4518b056a8ac157c32bb8abd2a147d53a04a413bcb2c143a4038755095c087593a197d15f31ee4c11399021d0928f7d50ea0be95324a2306fcf30b974a1283e2df2d7562c55875343ae1e29fa7a67e835fe8a0e05a31ba76f7ddd817b7010f427234b38fbd2f12a03c222dde19ecee94dcb43080",
"0xf90211a056527244566aed48654b0ca3e95225defe8b8eaee17657bace6698d7744b06cda0118221e9076b2fdf85d2371af95c10b9e9539a9aa3e3f6947acc0c7958bbdeeda0eed1a6e3b203d643cf0b3349816a78974046a45972cce621ea4a1979c29c632ca0414fd647c29c1323fe866af7f73d88c7ba93ba3d3eedea38aebd7a800c34ac75a0595cc2c4b2d314f1c12e7407732c4040bec3b269f499011c733b88f63007ae75a0836b374e20ca7ed03b5c5a447b6e15a39c0d037a82a429f8e099a06a24a372eaa085bcaf2bf96c47456365f4b28869670dee8d910c69ef7c75facc1c6e8e8983bfa0540629174895cf121ea76f7b63647bd9c46378184aeff99c81be646e0e6b80bca03048c27521797b94c190972758f2592a298af84bc14a2af2d1cfdcd00dc4b4a3a04456762d4c42229917c26b66106d1b354d12de086b65ad9c4ac0c97ae1fee28ba07b5ec4d7acfb65d85ce3c2c5c8528ea71e03997ddfe4a849cf02af2b61334276a0d49449f71c85a405d1c3ac16e903f8e2333c5e86c2b294e624fb0084317e4183a09d66aac7c383422398f421567ad2b103ce002961e973c8321e03b8e26bbb4c9da0cbccafd20b1c1162663b03bf8387639db8b1472d344dbda2f85805ea3f7061dca0402abdf44ab8e0971f58fadece8c64c1d9fabe5b2bc9ffaa30c847df16613520a03e3e95cd292c1fc017d5386e0c65923e423cddafe918bc50d6cd4ba96e8a4fe580",
"0xf90211a054ecd08900814f3b9e373cbc4ea3701007e84703bc5af9cfaa9e1711fd3f0319a09f9261744412d1946bcd933e1a6d53102186b2d99ecff30e403feea9841cf053a067f9200eef6cd28b708d6292b57e2a42b577874e5b1cbd406741e61a483e6b7aa081b4a58834d68fdb58de1b2f4b422b06f6094ceea952f8d0bf4746d743eefd70a02f59d7a6832a735d3116657cb0941143aa82d9c6d38efa250bf5827071bf63e4a09007eccc09dc7929e3574e1612a8867660accce899f70546173cbe30b109ea94a08d3f6cbecc09531cb79bdde35403a8b1192cfcdf6f0aca0a4429e5708920a254a031f06093af171b8380db7bff2e61463f6c4dc01c8d7e7181cf642bad24202357a0a60e1315be595edd2f36b0c8070638278ebfc3d1b02d9ad4828a690564f98c8ea0e4e3830e486521cc62bc05be33fb82dc976be5f77031cb9cff945c6fd9c07f6ba043a6e474ef36831d726f8260533cde7ee60409a3ebfc4660f0a0ec15f36098dda01af29c4ae492c1ef17d49e759c535e83a0f10e5be08e9218ad50a9a18a058604a096d3e05bb50ee4b1cec89a4eefea21fc8939fa5d1d698dddee320266d3245853a0ad45735c19f6fdd0d89dbbaa7aae4599b0e3fb099ae36b48410fb55231a1facca09cd3816e190ebd4bfefeb9cbff3ac539ad05ace88ca3dbae324326b61b52b2b4a0084ed25dc92cd64bf00f64818b2d4a3655c038035abe7daf5b04b9949522263780",
"0xf90191a00a7a0118e00981ab321049c9d340cd52c3a4781037540f7c48d0fdc27e899b3280a08537f2e248702a6ae2a57e9110a5740f5772c876389739ac90debd6a0692713ea00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717fa0a9a54d84976d134d6dba06a65064c7f3a964a75947d452db6f6bb4b6c47b43aaa01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f046740066461a098be0d129102f96ae636f9928911ed66103ac7c62235241aa0ef7e5b4b7e289ea07da2bce701255847cf5169ba5a7578a9700133f7ce13fa26a1d4097c20d1e0fda00631e0c3a589b999513f1d44a9b11e53d81027c1019553b4100542dbcb21031fa0c8d71dd13d2806e2865a5c2cfa447f626471bf0b66182a8fd07230434e1cad2680a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a0c69d174ad6b6e58b0bd05914352839ec60915cd066dd2bee2a48016139687f21a0513dd5514fd6bad56871711441d38de2821cc6913cb192416b0385f025650731808080",
"0xf8669d3802a763f7db875346d03fbf86f137de55814b191c069e721f47474733b846f8440101a065d17ccfe8328a42712f5dcd7a8827eebab3341e1a8bd6a4cb741495b83bd026a0b44fb4e949d0f78f87f79ee46428f23a2a5713ce6fc6e0beb3dda78c2ac1ea55",
},
expextedValue: "0x",
expectedLongestPrefixPrefix: "ab14d6",
expectedLastNode: "0xf90191a00a7a0118e00981ab321049c9d340cd52c3a4781037540f7c48d0fdc27e899b3280a08537f2e248702a6ae2a57e9110a5740f5772c876389739ac90debd6a0692713ea00b3a26a05b5494fb3ff6f0b3897688a5581066b20b07ebab9252d169d928717fa0a9a54d84976d134d6dba06a65064c7f3a964a75947d452db6f6bb4b6c47b43aaa01e2a1ed3d1572b872bbf09ee44d2ed737da31f01de3c0f4b4e1f046740066461a098be0d129102f96ae636f9928911ed66103ac7c62235241aa0ef7e5b4b7e289ea07da2bce701255847cf5169ba5a7578a9700133f7ce13fa26a1d4097c20d1e0fda00631e0c3a589b999513f1d44a9b11e53d81027c1019553b4100542dbcb21031fa0c8d71dd13d2806e2865a5c2cfa447f626471bf0b66182a8fd07230434e1cad2680a0e9864fdfaf3693b2602f56cd938ccd494b8634b1f91800ef02203a3609ca4c21a0c69d174ad6b6e58b0bd05914352839ec60915cd066dd2bee2a48016139687f21a0513dd5514fd6bad56871711441d38de2821cc6913cb192416b0385f025650731808080",
},
}
for _, tc := range testCase {
t.Run(tc.desc, func(t *testing.T) {
// Create a proofDB compatible with trie.VerifyProof
proofDB := memorydb.New()
err := StoreHexProofs(tc.proof, proofDB)
require.NoError(t, err)
// Verify the proof
val, longestPrefix, lastNode, err := VerifyProofWithLastNode(common.HexToHash(tc.root), hexutil.MustDecode(tc.key), proofDB)
if tc.expectedError != nil {
require.Error(t, err)
assert.Equal(t, tc.expectedError, err)
} else {
require.NoError(t, err)
assert.Equal(t, tc.expectedLongestPrefixPrefix, encodeKey(longestPrefix), "Last key prefix mismatch")
assert.Equal(t, tc.expextedValue, hexutil.Encode(val), "Value mismatch")
assert.Equal(t, tc.expectedLastNode, hexutil.Encode(lastNode), "Last node mismatch")
}
})
}
}
const hextable = "0123456789abcdef"
func encodeKey(hex []byte) string {
if hasTerm(hex) {
hex = hex[:len(hex)-1]
}
key := make([]byte, len(hex))
for i, b := range hex {
key[i] = hextable[b]
}
return string(key)
}
func TestOneElementProof(t *testing.T) { func TestOneElementProof(t *testing.T) {
trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme)) trie := NewEmpty(newTestDatabase(rawdb.NewMemoryDatabase(), rawdb.HashScheme))
updateString(trie, "k", "v") updateString(trie, "k", "v")

33
trie/proof_utils.go Normal file
View file

@ -0,0 +1,33 @@
package trie
import (
"fmt"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
)
// StoreBytesProofs stores proofs in a key value database.
func StoreBytesProofs(proofs [][]byte, db ethdb.KeyValueWriter) error {
for _, node := range proofs {
hash := crypto.Keccak256(node)
if err := db.Put(hash, node); err != nil {
return fmt.Errorf("failed to store proof node %q: %w", node, err)
}
}
return nil
}
// StoreHexProofs stores proofs in a key value database.
func StoreHexProofs(proofs []string, db ethdb.KeyValueWriter) error {
byteProofs := make([][]byte, len(proofs))
for i, node := range proofs {
byteProof, err := hexutil.Decode(node)
if err != nil {
return fmt.Errorf("failed to decode proof node %q: %w", node, err)
}
byteProofs[i] = byteProof
}
return StoreBytesProofs(byteProofs, db)
}