2020-05-24 11:23:29 +00:00
|
|
|
package mpt
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
2021-10-06 13:37:23 +00:00
|
|
|
"errors"
|
2020-05-24 11:23:29 +00:00
|
|
|
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/core/storage"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/crypto/hash"
|
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util"
|
2021-07-18 13:32:10 +00:00
|
|
|
"github.com/nspcc-dev/neo-go/pkg/util/slice"
|
2020-05-24 11:23:29 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
// GetProof returns a proof that key belongs to t.
|
2020-08-14 09:16:24 +00:00
|
|
|
// Proof consist of serialized nodes occurring on path from the root to the leaf of key.
|
2020-05-24 11:23:29 +00:00
|
|
|
func (t *Trie) GetProof(key []byte) ([][]byte, error) {
|
|
|
|
var proof [][]byte
|
2021-10-06 13:37:23 +00:00
|
|
|
if len(key) > MaxKeyLength {
|
|
|
|
return nil, errors.New("key is too big")
|
|
|
|
}
|
2020-05-24 11:23:29 +00:00
|
|
|
path := toNibbles(key)
|
|
|
|
r, err := t.getProof(t.root, path, &proof)
|
|
|
|
if err != nil {
|
|
|
|
return proof, err
|
|
|
|
}
|
|
|
|
t.root = r
|
|
|
|
return proof, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (t *Trie) getProof(curr Node, path []byte, proofs *[][]byte) (Node, error) {
|
|
|
|
switch n := curr.(type) {
|
|
|
|
case *LeafNode:
|
|
|
|
if len(path) == 0 {
|
2021-07-18 13:32:10 +00:00
|
|
|
*proofs = append(*proofs, slice.Copy(n.Bytes()))
|
2020-05-24 11:23:29 +00:00
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
case *BranchNode:
|
2021-07-18 13:32:10 +00:00
|
|
|
*proofs = append(*proofs, slice.Copy(n.Bytes()))
|
2020-05-24 11:23:29 +00:00
|
|
|
i, path := splitPath(path)
|
|
|
|
r, err := t.getProof(n.Children[i], path, proofs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
n.Children[i] = r
|
|
|
|
return n, nil
|
|
|
|
case *ExtensionNode:
|
|
|
|
if bytes.HasPrefix(path, n.key) {
|
2021-07-18 13:32:10 +00:00
|
|
|
*proofs = append(*proofs, slice.Copy(n.Bytes()))
|
2020-05-24 11:23:29 +00:00
|
|
|
r, err := t.getProof(n.next, path[len(n.key):], proofs)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
n.next = r
|
|
|
|
return n, nil
|
|
|
|
}
|
|
|
|
case *HashNode:
|
2021-08-03 14:10:46 +00:00
|
|
|
r, err := t.getFromStore(n.Hash())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2020-05-24 11:23:29 +00:00
|
|
|
}
|
2021-08-03 14:10:46 +00:00
|
|
|
return t.getProof(r, path, proofs)
|
2020-05-24 11:23:29 +00:00
|
|
|
}
|
|
|
|
return nil, ErrNotFound
|
|
|
|
}
|
|
|
|
|
|
|
|
// VerifyProof verifies that path indeed belongs to a MPT with the specified root hash.
|
|
|
|
// It also returns value for the key.
|
|
|
|
func VerifyProof(rh util.Uint256, key []byte, proofs [][]byte) ([]byte, bool) {
|
|
|
|
path := toNibbles(key)
|
2020-10-21 13:58:41 +00:00
|
|
|
tr := NewTrie(NewHashNode(rh), false, storage.NewMemCachedStore(storage.NewMemoryStore()))
|
2020-05-24 11:23:29 +00:00
|
|
|
for i := range proofs {
|
|
|
|
h := hash.DoubleSha256(proofs[i])
|
|
|
|
// no errors in Put to memory store
|
|
|
|
_ = tr.Store.Put(makeStorageKey(h[:]), proofs[i])
|
|
|
|
}
|
2021-10-07 13:56:27 +00:00
|
|
|
_, leaf, _, err := tr.getWithPath(tr.root, path, true)
|
|
|
|
if err != nil {
|
|
|
|
return nil, false
|
|
|
|
}
|
|
|
|
return slice.Copy(leaf.(*LeafNode).value), true
|
2020-05-24 11:23:29 +00:00
|
|
|
}
|