github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/core/mpt/proof.go (about)

     1  package mpt
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  
     7  	"github.com/nspcc-dev/neo-go/pkg/core/storage"
     8  	"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
     9  	"github.com/nspcc-dev/neo-go/pkg/util"
    10  )
    11  
    12  // GetProof returns a proof that the key belongs to t.
    13  // The proof consists of serialized nodes occurring on the path from the root to the leaf of key.
    14  func (t *Trie) GetProof(key []byte) ([][]byte, error) {
    15  	var proof [][]byte
    16  	if len(key) > MaxKeyLength {
    17  		return nil, errors.New("key is too big")
    18  	}
    19  	path := toNibbles(key)
    20  	r, err := t.getProof(t.root, path, &proof)
    21  	if err != nil {
    22  		return proof, err
    23  	}
    24  	t.root = r
    25  	return proof, nil
    26  }
    27  
    28  func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error) {
    29  	switch n := curr.(type) {
    30  	case *LeafNode:
    31  		if len(path) == 0 {
    32  			*proofs = append(*proofs, bytes.Clone(n.Bytes()))
    33  			return n, nil
    34  		}
    35  	case *BranchNode:
    36  		*proofs = append(*proofs, bytes.Clone(n.Bytes()))
    37  		i, path := splitPath(path)
    38  		r, err := t.getProof(n.Children[i], path, proofs)
    39  		if err != nil {
    40  			return nil, err
    41  		}
    42  		n.Children[i] = r
    43  		return n, nil
    44  	case *ExtensionNode:
    45  		if bytes.HasPrefix(path, n.key) {
    46  			*proofs = append(*proofs, bytes.Clone(n.Bytes()))
    47  			r, err := t.getProof(n.next, path[len(n.key):], proofs)
    48  			if err != nil {
    49  				return nil, err
    50  			}
    51  			n.next = r
    52  			return n, nil
    53  		}
    54  	case *HashNode:
    55  		r, err := t.getFromStore(n.Hash())
    56  		if err != nil {
    57  			return nil, err
    58  		}
    59  		return t.getProof(r, path, proofs)
    60  	}
    61  	return nil, ErrNotFound
    62  }
    63  
    64  // VerifyProof verifies that path indeed belongs to a MPT with the specified root hash.
    65  // It also returns the value for the key.
    66  func VerifyProof(rh util.Uint256, key []byte, proofs [][]byte) ([]byte, bool) {
    67  	path := toNibbles(key)
    68  	tr := NewTrie(NewHashNode(rh), ModeAll, storage.NewMemCachedStore(storage.NewMemoryStore()))
    69  	for i := range proofs {
    70  		h := hash.DoubleSha256(proofs[i])
    71  		tr.Store.Put(makeStorageKey(h), proofs[i])
    72  	}
    73  	_, leaf, _, err := tr.getWithPath(tr.root, path, true)
    74  	if err != nil {
    75  		return nil, false
    76  	}
    77  	return bytes.Clone(leaf.(*LeafNode).value), true
    78  }