| 
									
										
										
										
											2016-04-14 18:18:24 +02:00
										 |  |  | // Copyright 2015 The go-ethereum Authors | 
					
						
							|  |  |  | // This file is part of the go-ethereum library. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is free software: you can redistribute it and/or modify | 
					
						
							|  |  |  | // it under the terms of the GNU Lesser General Public License as published by | 
					
						
							|  |  |  | // the Free Software Foundation, either version 3 of the License, or | 
					
						
							|  |  |  | // (at your option) any later version. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The go-ethereum library is distributed in the hope that it will be useful, | 
					
						
							|  |  |  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | 
					
						
							|  |  |  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 
					
						
							|  |  |  | // GNU Lesser General Public License for more details. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // You should have received a copy of the GNU Lesser General Public License | 
					
						
							|  |  |  | // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | package trie | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"bytes" | 
					
						
							|  |  |  | 	crand "crypto/rand" | 
					
						
							|  |  |  | 	mrand "math/rand" | 
					
						
							|  |  |  | 	"testing" | 
					
						
							|  |  |  | 	"time" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/common" | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 	"github.com/ethereum/go-ethereum/crypto" | 
					
						
							|  |  |  | 	"github.com/ethereum/go-ethereum/ethdb" | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func init() { | 
					
						
							|  |  |  | 	mrand.Seed(time.Now().Unix()) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestProof(t *testing.T) { | 
					
						
							|  |  |  | 	trie, vals := randomTrie(500) | 
					
						
							|  |  |  | 	root := trie.Hash() | 
					
						
							|  |  |  | 	for _, kv := range vals { | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		proofs, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		if trie.Prove(kv.k, 0, proofs) != nil { | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 			t.Fatalf("missing key %x while constructing proof", kv.k) | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		val, err, _ := VerifyProof(root, kv.k, proofs) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		if err != nil { | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 			t.Fatalf("VerifyProof error for key %x: %v\nraw proof: %v", kv.k, err, proofs) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		if !bytes.Equal(val, kv.v) { | 
					
						
							|  |  |  | 			t.Fatalf("VerifyProof returned wrong value for key %x: got %x, want %x", kv.k, val, kv.v) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestOneElementProof(t *testing.T) { | 
					
						
							|  |  |  | 	trie := new(Trie) | 
					
						
							|  |  |  | 	updateString(trie, "k", "v") | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 	proofs, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 	trie.Prove([]byte("k"), 0, proofs) | 
					
						
							|  |  |  | 	if len(proofs.Keys()) != 1 { | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		t.Error("proof should have one element") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 	val, err, _ := VerifyProof(trie.Hash(), []byte("k"), proofs) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 	if err != nil { | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		t.Fatalf("VerifyProof error: %v\nproof hashes: %v", err, proofs.Keys()) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if !bytes.Equal(val, []byte("v")) { | 
					
						
							|  |  |  | 		t.Fatalf("VerifyProof returned wrong value: got %x, want 'k'", val) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func TestVerifyBadProof(t *testing.T) { | 
					
						
							|  |  |  | 	trie, vals := randomTrie(800) | 
					
						
							|  |  |  | 	root := trie.Hash() | 
					
						
							|  |  |  | 	for _, kv := range vals { | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		proofs, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		trie.Prove(kv.k, 0, proofs) | 
					
						
							|  |  |  | 		if len(proofs.Keys()) == 0 { | 
					
						
							|  |  |  | 			t.Fatal("zero length proof") | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		keys := proofs.Keys() | 
					
						
							|  |  |  | 		key := keys[mrand.Intn(len(keys))] | 
					
						
							|  |  |  | 		node, _ := proofs.Get(key) | 
					
						
							|  |  |  | 		proofs.Delete(key) | 
					
						
							|  |  |  | 		mutateByte(node) | 
					
						
							|  |  |  | 		proofs.Put(crypto.Keccak256(node), node) | 
					
						
							|  |  |  | 		if _, err, _ := VerifyProof(root, kv.k, proofs); err == nil { | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 			t.Fatalf("expected proof to fail for key %x", kv.k) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // mutateByte changes one byte in b. | 
					
						
							|  |  |  | func mutateByte(b []byte) { | 
					
						
							|  |  |  | 	for r := mrand.Intn(len(b)); ; { | 
					
						
							|  |  |  | 		new := byte(mrand.Intn(255)) | 
					
						
							|  |  |  | 		if new != b[r] { | 
					
						
							|  |  |  | 			b[r] = new | 
					
						
							|  |  |  | 			break | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkProve(b *testing.B) { | 
					
						
							|  |  |  | 	trie, vals := randomTrie(100) | 
					
						
							|  |  |  | 	var keys []string | 
					
						
							|  |  |  | 	for k := range vals { | 
					
						
							|  |  |  | 		keys = append(keys, k) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.ResetTimer() | 
					
						
							|  |  |  | 	for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 		kv := vals[keys[i%len(keys)]] | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		proofs, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		if trie.Prove(kv.k, 0, proofs); len(proofs.Keys()) == 0 { | 
					
						
							|  |  |  | 			b.Fatalf("zero length proof for %x", kv.k) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func BenchmarkVerifyProof(b *testing.B) { | 
					
						
							|  |  |  | 	trie, vals := randomTrie(100) | 
					
						
							|  |  |  | 	root := trie.Hash() | 
					
						
							|  |  |  | 	var keys []string | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 	var proofs []*ethdb.MemDatabase | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 	for k := range vals { | 
					
						
							|  |  |  | 		keys = append(keys, k) | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		proof, _ := ethdb.NewMemDatabase() | 
					
						
							|  |  |  | 		trie.Prove([]byte(k), 0, proof) | 
					
						
							|  |  |  | 		proofs = append(proofs, proof) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	b.ResetTimer() | 
					
						
							|  |  |  | 	for i := 0; i < b.N; i++ { | 
					
						
							|  |  |  | 		im := i % len(keys) | 
					
						
							| 
									
										
										
										
											2017-10-24 15:19:09 +02:00
										 |  |  | 		if _, err, _ := VerifyProof(root, []byte(keys[im]), proofs[im]); err != nil { | 
					
						
							| 
									
										
										
										
											2016-04-15 11:06:57 +02:00
										 |  |  | 			b.Fatalf("key %x: %v", keys[im], err) | 
					
						
							| 
									
										
										
										
											2015-09-09 03:35:41 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func randomTrie(n int) (*Trie, map[string]*kv) { | 
					
						
							|  |  |  | 	trie := new(Trie) | 
					
						
							|  |  |  | 	vals := make(map[string]*kv) | 
					
						
							|  |  |  | 	for i := byte(0); i < 100; i++ { | 
					
						
							|  |  |  | 		value := &kv{common.LeftPadBytes([]byte{i}, 32), []byte{i}, false} | 
					
						
							|  |  |  | 		value2 := &kv{common.LeftPadBytes([]byte{i + 10}, 32), []byte{i}, false} | 
					
						
							|  |  |  | 		trie.Update(value.k, value.v) | 
					
						
							|  |  |  | 		trie.Update(value2.k, value2.v) | 
					
						
							|  |  |  | 		vals[string(value.k)] = value | 
					
						
							|  |  |  | 		vals[string(value2.k)] = value2 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for i := 0; i < n; i++ { | 
					
						
							|  |  |  | 		value := &kv{randBytes(32), randBytes(20), false} | 
					
						
							|  |  |  | 		trie.Update(value.k, value.v) | 
					
						
							|  |  |  | 		vals[string(value.k)] = value | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return trie, vals | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func randBytes(n int) []byte { | 
					
						
							|  |  |  | 	r := make([]byte, n) | 
					
						
							|  |  |  | 	crand.Read(r) | 
					
						
							|  |  |  | 	return r | 
					
						
							|  |  |  | } |