forked from cerc-io/plugeth
trie: support empty range proof (#21199)
This commit is contained in:
parent
e1365b2464
commit
25b16085da
@ -393,7 +393,7 @@ func hasRightElement(node node, key []byte) bool {
|
|||||||
// (unless firstProof is an existent proof).
|
// (unless firstProof is an existent proof).
|
||||||
//
|
//
|
||||||
// Expect the normal case, this function can also be used to verify the following
|
// Expect the normal case, this function can also be used to verify the following
|
||||||
// range proofs(note this function doesn't accept zero element proof):
|
// range proofs:
|
||||||
//
|
//
|
||||||
// - All elements proof. In this case the left and right proof can be nil, but the
|
// - All elements proof. In this case the left and right proof can be nil, but the
|
||||||
// range should be all the leaves in the trie.
|
// range should be all the leaves in the trie.
|
||||||
@ -401,15 +401,16 @@ func hasRightElement(node node, key []byte) bool {
|
|||||||
// - One element proof. In this case no matter the left edge proof is a non-existent
|
// - One element proof. In this case no matter the left edge proof is a non-existent
|
||||||
// proof or not, we can always verify the correctness of the proof.
|
// proof or not, we can always verify the correctness of the proof.
|
||||||
//
|
//
|
||||||
|
// - Zero element proof(left edge proof should be a non-existent proof). In this
|
||||||
|
// case if there are still some other leaves available on the right side, then
|
||||||
|
// an error will be returned.
|
||||||
|
//
|
||||||
// Except returning the error to indicate the proof is valid or not, the function will
|
// Except returning the error to indicate the proof is valid or not, the function will
|
||||||
// also return a flag to indicate whether there exists more accounts/slots in the trie.
|
// also return a flag to indicate whether there exists more accounts/slots in the trie.
|
||||||
func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, values [][]byte, firstProof ethdb.KeyValueReader, lastProof ethdb.KeyValueReader) (error, bool) {
|
func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, values [][]byte, firstProof ethdb.KeyValueReader, lastProof ethdb.KeyValueReader) (error, bool) {
|
||||||
if len(keys) != len(values) {
|
if len(keys) != len(values) {
|
||||||
return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)), false
|
return fmt.Errorf("inconsistent proof data, keys: %d, values: %d", len(keys), len(values)), false
|
||||||
}
|
}
|
||||||
if len(keys) == 0 {
|
|
||||||
return errors.New("empty proof"), false
|
|
||||||
}
|
|
||||||
// Ensure the received batch is monotonic increasing.
|
// Ensure the received batch is monotonic increasing.
|
||||||
for i := 0; i < len(keys)-1; i++ {
|
for i := 0; i < len(keys)-1; i++ {
|
||||||
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
if bytes.Compare(keys[i], keys[i+1]) >= 0 {
|
||||||
@ -431,6 +432,18 @@ func VerifyRangeProof(rootHash common.Hash, firstKey []byte, keys [][]byte, valu
|
|||||||
}
|
}
|
||||||
return nil, false // no more element.
|
return nil, false // no more element.
|
||||||
}
|
}
|
||||||
|
// Special case, there is a provided left edge proof and zero key/value
|
||||||
|
// pairs, ensure there are no more accounts / slots in the trie.
|
||||||
|
if len(keys) == 0 {
|
||||||
|
root, val, err := proofToPath(rootHash, nil, firstKey, firstProof, true)
|
||||||
|
if err != nil {
|
||||||
|
return err, false
|
||||||
|
}
|
||||||
|
if val != nil || hasRightElement(root, firstKey) {
|
||||||
|
return errors.New("more entries available"), false
|
||||||
|
}
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
// Special case, there is only one element and left edge
|
// Special case, there is only one element and left edge
|
||||||
// proof is an existent one.
|
// proof is an existent one.
|
||||||
if len(keys) == 1 && bytes.Equal(keys[0], firstKey) {
|
if len(keys) == 1 && bytes.Equal(keys[0], firstKey) {
|
||||||
|
@ -571,6 +571,39 @@ func TestHasRightElement(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestEmptyRangeProof tests the range proof with "no" element.
|
||||||
|
// The first edge proof must be a non-existent proof.
|
||||||
|
func TestEmptyRangeProof(t *testing.T) {
|
||||||
|
trie, vals := randomTrie(4096)
|
||||||
|
var entries entrySlice
|
||||||
|
for _, kv := range vals {
|
||||||
|
entries = append(entries, kv)
|
||||||
|
}
|
||||||
|
sort.Sort(entries)
|
||||||
|
|
||||||
|
var cases = []struct {
|
||||||
|
pos int
|
||||||
|
err bool
|
||||||
|
}{
|
||||||
|
{len(entries) - 1, false},
|
||||||
|
{500, true},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
firstProof := memorydb.New()
|
||||||
|
first := increseKey(common.CopyBytes(entries[c.pos].k))
|
||||||
|
if err := trie.Prove(first, 0, firstProof); err != nil {
|
||||||
|
t.Fatalf("Failed to prove the first node %v", err)
|
||||||
|
}
|
||||||
|
err, _ := VerifyRangeProof(trie.Hash(), first, nil, nil, firstProof, nil)
|
||||||
|
if c.err && err == nil {
|
||||||
|
t.Fatalf("Expected error, got nil")
|
||||||
|
}
|
||||||
|
if !c.err && err != nil {
|
||||||
|
t.Fatalf("Expected no error, got %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// mutateByte changes one byte in b.
|
// mutateByte changes one byte in b.
|
||||||
func mutateByte(b []byte) {
|
func mutateByte(b []byte) {
|
||||||
for r := mrand.Intn(len(b)); ; {
|
for r := mrand.Intn(len(b)); ; {
|
||||||
|
Loading…
Reference in New Issue
Block a user