core: add MaxKeyLength restrictions to MPT's operations

This commit is contained in:
Anna Shaleva 2021-10-06 16:37:23 +03:00
parent b4e24bef14
commit c8120a139d
3 changed files with 16 additions and 2 deletions

View file

@ -40,12 +40,13 @@ func prepareMPTCompat() *Trie {
// There are some differences, though: // There are some differences, though:
// 1. In our implementation delete is silent, i.e. we do not return an error is the key is missing or empty. // 1. In our implementation delete is silent, i.e. we do not return an error is the key is missing or empty.
// However, we do return error when contents of hash node are missing from the store // However, we do return error when contents of hash node are missing from the store
// (corresponds to exception in C# implementation). However, if the key is too big, an error is returned
// (corresponds to exception in C# implementation). // (corresponds to exception in C# implementation).
// 2. In our implementation put returns error if something goes wrong, while C# implementation throws // 2. In our implementation put returns error if something goes wrong, while C# implementation throws
// an exception and returns nothing. // an exception and returns nothing.
// 3. In our implementation get does not immediately return error in case of an empty key. An error is returned // 3. In our implementation get does not immediately return error in case of an empty key. An error is returned
// only if value is missing from the storage. C# implementation checks that key is not empty and throws an error // only if value is missing from the storage. C# implementation checks that key is not empty and throws an error
// otherwice. // otherwice. However, if the key is too big, an error is returned (corresponds to exception in C# implementation).
func TestCompatibility(t *testing.T) { func TestCompatibility(t *testing.T) {
mainTrie := prepareMPTCompat() mainTrie := prepareMPTCompat()
@ -59,6 +60,7 @@ func TestCompatibility(t *testing.T) {
tr.testHas(t, []byte{0xac, 0x01, 0x00}, nil) tr.testHas(t, []byte{0xac, 0x01, 0x00}, nil)
tr.testHas(t, []byte{0xac, 0x99, 0x10}, nil) tr.testHas(t, []byte{0xac, 0x99, 0x10}, nil)
tr.testHas(t, []byte{0xac, 0xf1}, nil) tr.testHas(t, []byte{0xac, 0xf1}, nil)
tr.testHas(t, make([]byte, MaxKeyLength), nil)
}) })
t.Run("TryGetResolve", func(t *testing.T) { t.Run("TryGetResolve", func(t *testing.T) {
@ -96,6 +98,7 @@ func TestCompatibility(t *testing.T) {
require.NoError(t, tr.Delete([]byte{0xac, 0x20})) require.NoError(t, tr.Delete([]byte{0xac, 0x20}))
require.Error(t, tr.Delete([]byte{0xac, 0xf1})) // error for can't resolve require.Error(t, tr.Delete([]byte{0xac, 0xf1})) // error for can't resolve
require.Error(t, tr.Delete(make([]byte, MaxKeyLength+1))) // error for too big key
// In our implementation missing keys are ignored. // In our implementation missing keys are ignored.
require.NoError(t, tr.Delete([]byte{0xac})) require.NoError(t, tr.Delete([]byte{0xac}))
@ -202,6 +205,7 @@ func TestCompatibility(t *testing.T) {
testGetProof(t, tr, nil, 0) testGetProof(t, tr, nil, 0)
testGetProof(t, tr, []byte{0xac, 0x01, 0x00}, 0) testGetProof(t, tr, []byte{0xac, 0x01, 0x00}, 0)
testGetProof(t, tr, []byte{0xac, 0xf1}, 0) testGetProof(t, tr, []byte{0xac, 0xf1}, 0)
testGetProof(t, tr, make([]byte, MaxKeyLength), 0)
}) })
t.Run("VerifyProof", func(t *testing.T) { t.Run("VerifyProof", func(t *testing.T) {

View file

@ -2,6 +2,7 @@ package mpt
import ( import (
"bytes" "bytes"
"errors"
"github.com/nspcc-dev/neo-go/pkg/core/storage" "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/crypto/hash"
@ -13,6 +14,9 @@ import (
// Proof consist of serialized nodes occurring on path from the root to the leaf of key. // Proof consist of serialized nodes occurring on path from the root to the leaf of key.
func (t *Trie) GetProof(key []byte) ([][]byte, error) { func (t *Trie) GetProof(key []byte) ([][]byte, error) {
var proof [][]byte var proof [][]byte
if len(key) > MaxKeyLength {
return nil, errors.New("key is too big")
}
path := toNibbles(key) path := toNibbles(key)
r, err := t.getProof(t.root, path, &proof) r, err := t.getProof(t.root, path, &proof)
if err != nil { if err != nil {

View file

@ -49,6 +49,9 @@ func NewTrie(root Node, enableRefCount bool, store *storage.MemCachedStore) *Tri
// Get returns value for the provided key in t. // Get returns value for the provided key in t.
func (t *Trie) Get(key []byte) ([]byte, error) { func (t *Trie) Get(key []byte) ([]byte, error) {
if len(key) > MaxKeyLength {
return nil, errors.New("key is too big")
}
path := toNibbles(key) path := toNibbles(key)
r, bs, err := t.getWithPath(t.root, path) r, bs, err := t.getWithPath(t.root, path)
if err != nil { if err != nil {
@ -235,6 +238,9 @@ func (t *Trie) putIntoNode(curr Node, path []byte, val Node) (Node, error) {
// Delete removes key from trie. // Delete removes key from trie.
// It returns no error on missing key. // It returns no error on missing key.
func (t *Trie) Delete(key []byte) error { func (t *Trie) Delete(key []byte) error {
if len(key) > MaxKeyLength {
return errors.New("key is too big")
}
path := toNibbles(key) path := toNibbles(key)
r, err := t.deleteFromNode(t.root, path) r, err := t.deleteFromNode(t.root, path)
if err != nil { if err != nil {