github.com/letsencrypt/trillian@v1.1.2-0.20180615153820-ae375a99d36a/merkle/log_verifier_test.go (about) 1 // Copyright 2017 Google Inc. All Rights Reserved. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package merkle 16 17 import ( 18 "bytes" 19 "encoding/hex" 20 "fmt" 21 "strings" 22 "testing" 23 24 "github.com/google/trillian/merkle/rfc6962" 25 ) 26 27 type inclusionProofTestVector struct { 28 leaf int64 29 snapshot int64 30 proof [][]byte 31 } 32 33 type consistencyTestVector struct { 34 snapshot1 int64 35 snapshot2 int64 36 proof [][]byte 37 } 38 39 var ( 40 sha256SomeHash = dh("abacaba000000000000000000000000000000000000000000060061e00123456", 32) 41 sha256EmptyTreeHash = dh("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855", 32) 42 43 inclusionProofs = []inclusionProofTestVector{ 44 {0, 0, nil}, 45 {1, 1, nil}, 46 {1, 8, [][]byte{ 47 dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32), 48 dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), 49 dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32)}}, 50 {6, 8, [][]byte{ 51 dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32), 52 dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32), 53 dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32)}}, 54 {3, 3, [][]byte{ 55 dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32)}}, 56 {2, 5, [][]byte{ 57 dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32), 58 dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), 59 dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32)}}, 60 } 61 62 consistencyProofs = []consistencyTestVector{ 63 {1, 1, nil}, 64 {1, 8, [][]byte{ 65 dh("96a296d224f285c67bee93c30f8a309157f0daa35dc5b87e410b78630a09cfc7", 32), 66 dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), 67 dh("6b47aaf29ee3c2af9af889bc1fb9254dabd31177f16232dd6aab035ca39bf6e4", 32)}}, 68 {6, 8, [][]byte{ 69 dh("0ebc5d3437fbe2db158b9f126a1d118e308181031d0a949f8dededebc558ef6a", 32), 70 dh("ca854ea128ed050b41b35ffc1b87b8eb2bde461e9e3b5596ece6b9d5975a0ae0", 32), 71 dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32)}}, 72 {2, 5, [][]byte{ 73 dh("5f083f0a1a33ca076a95279832580db3e0ef4584bdff1f54c8a360f50de3031e", 32), 74 dh("bc1a0643b12e4d2d7c77918f44e0f4f79a838b6cf9ec5b5c283e1f4d88599e6b", 32)}}, 75 } 76 77 roots = [][]byte{ 78 dh("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d", 32), 79 dh("fac54203e7cc696cf0dfcb42c92a1d9dbaf70ad9e621f4bd8d98662f00e3c125", 32), 80 dh("aeb6bcfe274b70a14fb067a5e5578264db0fa9b51af5e0ba159158f329e06e77", 32), 81 dh("d37ee418976dd95753c1c73862b9398fa2a2cf9b4ff0fdfe8b30cd95209614b7", 32), 82 dh("4e3bbb1f7b478dcfe71fb631631519a3bca12c9aefca1612bfce4c13a86264d4", 32), 83 dh("76e67dadbcdf1e10e1b74ddc608abd2f98dfb16fbce75277b5232a127f2087ef", 32), 84 dh("ddb89be403809e325750d3d263cd78929c2942b7942a34b77e122c9594a74c8c", 32), 85 dh("5dc9da79a70659a9ad559cb701ded9a2ab9d823aad2f4960cfe370eff4604328", 32), 86 } 87 88 leaves = [][]byte{ 89 dh("", 0), 90 dh("00", 1), 91 dh("10", 1), 92 dh("2021", 2), 93 dh("3031", 2), 94 dh("40414243", 4), 95 dh("5051525354555657", 8), 96 dh("606162636465666768696a6b6c6d6e6f", 16), 97 } 98 ) 99 100 // inclusionProbe is a parameter set for inclusion proof verification. 101 type inclusionProbe struct { 102 leafIndex int64 103 treeSize int64 104 root []byte 105 leafHash []byte 106 proof [][]byte 107 108 desc string 109 } 110 111 // consistencyProbe is a parameter set for consistency proof verification. 112 type consistencyProbe struct { 113 snapshot1 int64 114 snapshot2 int64 115 root1 []byte 116 root2 []byte 117 proof [][]byte 118 119 desc string 120 } 121 122 func corruptInclusionProof(leafIndex, treeSize int64, proof [][]byte, root, leafHash []byte) []inclusionProbe { 123 ret := []inclusionProbe{ 124 // Wrong leaf index. 125 {leafIndex - 1, treeSize, root, leafHash, proof, "leafIndex - 1"}, 126 {leafIndex + 1, treeSize, root, leafHash, proof, "leafIndex + 1"}, 127 {leafIndex ^ 2, treeSize, root, leafHash, proof, "leafIndex ^ 2"}, 128 // Wrong tree height. 129 {leafIndex, treeSize * 2, root, leafHash, proof, "treeSize * 2"}, 130 {leafIndex, treeSize / 2, root, leafHash, proof, "treeSize / 2"}, 131 // Wrong leaf or root. 132 {leafIndex, treeSize, root, []byte("WrongLeaf"), proof, "wrong leaf"}, 133 {leafIndex, treeSize, sha256EmptyTreeHash, leafHash, proof, "empty root"}, 134 {leafIndex, treeSize, sha256SomeHash, leafHash, proof, "random root"}, 135 // Add garbage at the end. 136 {leafIndex, treeSize, root, leafHash, extend(proof, []byte{}), "trailing garbage"}, 137 {leafIndex, treeSize, root, leafHash, extend(proof, root), "trailing root"}, 138 // Add garbage at the front. 139 {leafIndex, treeSize, root, leafHash, prepend(proof, []byte{}), "preceding garbage"}, 140 {leafIndex, treeSize, root, leafHash, prepend(proof, root), "preceding root"}, 141 } 142 ln := len(proof) 143 144 // Modify single bit in an element of the proof. 145 for i := 0; i < ln; i++ { 146 wrongProof := prepend(proof) // Copy the proof slice. 147 wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data. 148 wrongProof[i][0] ^= 8 // Flip the bit. 149 desc := fmt.Sprintf("modified proof[%d] bit 3", i) 150 ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, desc}) 151 } 152 153 if ln > 0 { 154 ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, proof[:ln-1], "removed component"}) 155 } 156 if ln > 1 { 157 wrongProof := prepend(proof[1:], proof[0], sha256SomeHash) 158 ret = append(ret, inclusionProbe{leafIndex, treeSize, root, leafHash, wrongProof, "inserted component"}) 159 } 160 161 return ret 162 } 163 164 func corruptConsistencyProof(snapshot1, snapshot2 int64, root1, root2 []byte, proof [][]byte) []consistencyProbe { 165 ln := len(proof) 166 ret := []consistencyProbe{ 167 // Wrong snapshot index. 168 {snapshot1 - 1, snapshot2, root1, root2, proof, "snapshot1 - 1"}, 169 {snapshot1 + 1, snapshot2, root1, root2, proof, "snapshot1 + 1"}, 170 {snapshot1 ^ 2, snapshot2, root1, root2, proof, "snapshot1 ^ 2"}, 171 // Wrong tree height. 172 {snapshot1, snapshot2 * 2, root1, root2, proof, "snapshot2 * 2"}, 173 {snapshot1, snapshot2 / 2, root1, root2, proof, "snapshot2 / 2"}, 174 // Wrong root. 175 {snapshot1, snapshot2, []byte("WrongRoot"), root2, proof, "wrong root1"}, 176 {snapshot1, snapshot2, root1, []byte("WrongRoot"), proof, "wrong root2"}, 177 {snapshot1, snapshot2, root2, root1, proof, "swapped roots"}, 178 // Empty proof. 179 {snapshot1, snapshot2, root1, root2, [][]byte{}, "empty proof"}, 180 // Add garbage at the end. 181 {snapshot1, snapshot2, root1, root2, extend(proof, []byte{}), "trailing garbage"}, 182 {snapshot1, snapshot2, root1, root2, extend(proof, root1), "trailing root1"}, 183 {snapshot1, snapshot2, root1, root2, extend(proof, root2), "trailing root2"}, 184 // Add garbage at the front. 185 {snapshot1, snapshot2, root1, root2, prepend(proof, []byte{}), "preceding garbage"}, 186 {snapshot1, snapshot2, root1, root2, prepend(proof, root1), "preceding root1"}, 187 {snapshot1, snapshot2, root1, root2, prepend(proof, root2), "preceding root2"}, 188 {snapshot1, snapshot2, root1, root2, prepend(proof, proof[0]), "preceding proof[0]"}, 189 } 190 191 // Remove a node from the end. 192 if ln > 0 { 193 ret = append(ret, consistencyProbe{snapshot1, snapshot2, root1, root2, proof[:ln-1], "truncated proof"}) 194 } 195 196 // Modify single bit in an element of the proof. 197 for i := 0; i < ln; i++ { 198 wrongProof := prepend(proof) // Copy the proof slice. 199 wrongProof[i] = append([]byte(nil), wrongProof[i]...) // But also the modified data. 200 wrongProof[i][0] ^= 16 // Flip the bit. 201 desc := fmt.Sprintf("modified proof[%d] bit 4", i) 202 ret = append(ret, consistencyProbe{snapshot1, snapshot2, root1, root2, wrongProof, desc}) 203 } 204 205 return ret 206 } 207 208 func verifierCheck(v *LogVerifier, leafIndex, treeSize int64, proof [][]byte, root, leafHash []byte) error { 209 // Verify original inclusion proof. 210 got, err := v.RootFromInclusionProof(leafIndex, treeSize, proof, leafHash) 211 if err != nil { 212 return err 213 } 214 if !bytes.Equal(got, root) { 215 return fmt.Errorf("got root:\n%x\nexpected:\n%x", got, root) 216 } 217 if err := v.VerifyInclusionProof(leafIndex, treeSize, proof, root, leafHash); err != nil { 218 return err 219 } 220 221 probes := corruptInclusionProof(leafIndex, treeSize, proof, root, leafHash) 222 var wrong []string 223 for _, p := range probes { 224 if err := v.VerifyInclusionProof(p.leafIndex, p.treeSize, p.proof, p.root, p.leafHash); err == nil { 225 wrong = append(wrong, p.desc) 226 } 227 } 228 if len(wrong) > 0 { 229 return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", ")) 230 } 231 return nil 232 } 233 234 func verifierConsistencyCheck(v *LogVerifier, snapshot1, snapshot2 int64, root1, root2 []byte, proof [][]byte) error { 235 // Verify original consistency proof. 236 if err := v.VerifyConsistencyProof(snapshot1, snapshot2, root1, root2, proof); err != nil { 237 return err 238 } 239 // For simplicity test only non-trivial proofs that have root1 != root2, 240 // snapshot1 != 0 and snapshot1 != snapshot2. 241 if len(proof) == 0 { 242 return nil 243 } 244 245 probes := corruptConsistencyProof(snapshot1, snapshot2, root1, root2, proof) 246 var wrong []string 247 for _, p := range probes { 248 if err := v.VerifyConsistencyProof(p.snapshot1, p.snapshot2, p.root1, p.root2, p.proof); err == nil { 249 wrong = append(wrong, p.desc) 250 } 251 } 252 if len(wrong) > 0 { 253 return fmt.Errorf("incorrectly verified against: %s", strings.Join(wrong, ", ")) 254 } 255 return nil 256 } 257 258 func TestVerifyInclusionProofSingleEntry(t *testing.T) { 259 v := NewLogVerifier(rfc6962.DefaultHasher) 260 data := []byte("data") 261 // Root and leaf hash for 1-entry tree are the same. 262 hash, _ := v.hasher.HashLeaf(data) 263 // The corresponding inclusion proof is empty. 264 proof := [][]byte{} 265 emptyHash := []byte{} 266 267 for i, tc := range []struct { 268 root []byte 269 leaf []byte 270 wantErr bool 271 }{ 272 {hash, hash, false}, 273 {hash, emptyHash, true}, 274 {emptyHash, hash, true}, 275 {emptyHash, emptyHash, true}, // Wrong hash size. 276 } { 277 t.Run(fmt.Sprintf("test:%d", i), func(t *testing.T) { 278 err := v.VerifyInclusionProof(0, 1, proof, tc.root, tc.leaf) 279 if got, want := (err != nil), tc.wantErr; got != want { 280 t.Errorf("error: %v, want %v", got, want) 281 } 282 }) 283 } 284 } 285 286 func TestVerifyInclusionProof(t *testing.T) { 287 v := NewLogVerifier(rfc6962.DefaultHasher) 288 proof := [][]byte{} 289 290 probes := []struct { 291 index, size int64 292 }{{0, 0}, {0, 1}, {1, 0}, {2, 1}} 293 for _, p := range probes { 294 t.Run(fmt.Sprintf("probe:%d:%d", p.index, p.size), func(t *testing.T) { 295 if err := v.VerifyInclusionProof(p.index, p.size, proof, []byte{}, sha256SomeHash); err == nil { 296 t.Error("Incorrectly verified invalid root/leaf") 297 } 298 if err := v.VerifyInclusionProof(p.index, p.size, proof, sha256EmptyTreeHash, []byte{}); err == nil { 299 t.Error("Incorrectly verified invalid root/leaf") 300 } 301 if err := v.VerifyInclusionProof(p.index, p.size, proof, sha256EmptyTreeHash, sha256SomeHash); err == nil { 302 t.Error("Incorrectly verified invalid root/leaf") 303 } 304 }) 305 } 306 307 // i = 0 is an invalid path. 308 for i := 1; i < 6; i++ { 309 p := inclusionProofs[i] 310 t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) { 311 leafHash, err := rfc6962.DefaultHasher.HashLeaf(leaves[p.leaf-1]) 312 if err != nil { 313 t.Fatalf("HashLeaf(): %v", err) 314 } 315 if err := verifierCheck(&v, p.leaf-1, p.snapshot, p.proof, roots[p.snapshot-1], leafHash); err != nil { 316 t.Errorf("verifierCheck(): %s", err) 317 } 318 }) 319 } 320 } 321 322 func TestVerifyInclusionProofGenerated(t *testing.T) { 323 var sizes []int64 324 for s := 1; s <= 70; s++ { 325 sizes = append(sizes, int64(s)) 326 } 327 sizes = append(sizes, []int64{1024, 5050}...) 328 329 tree, v := createTree(0) 330 for _, size := range sizes { 331 growTree(tree, size) 332 root := tree.CurrentRoot().Hash() 333 for i := int64(0); i < size; i++ { 334 t.Run(fmt.Sprintf("size:%d:index:%d", size, i), func(t *testing.T) { 335 leaf, proof := getLeafAndProof(tree, i) 336 if err := verifierCheck(&v, i, size, proof, root, leaf); err != nil { 337 t.Errorf("verifierCheck(): %v", err) 338 } 339 }) 340 } 341 } 342 } 343 344 func TestVerifyConsistencyProof(t *testing.T) { 345 v := NewLogVerifier(rfc6962.DefaultHasher) 346 347 root1 := []byte("don't care 1") 348 root2 := []byte("don't care 2") 349 proof1 := [][]byte{} 350 proof2 := [][]byte{sha256EmptyTreeHash} 351 352 tests := []struct { 353 snap1, snap2 int64 354 root1, root2 []byte 355 proof [][]byte 356 wantErr bool 357 }{ 358 {0, 0, root1, root2, proof1, true}, 359 {1, 1, root1, root2, proof1, true}, 360 // Snapshots that are always consistent. 361 {0, 0, root1, root1, proof1, false}, 362 {0, 1, root1, root2, proof1, false}, 363 {1, 1, root2, root2, proof1, false}, 364 // Time travel to the past. 365 {1, 0, root1, root2, proof1, true}, 366 {2, 1, root1, root2, proof1, true}, 367 // Empty proof. 368 {1, 2, root1, root2, proof1, true}, 369 // Roots don't match. 370 {0, 0, sha256EmptyTreeHash, root2, proof1, true}, 371 {1, 1, sha256EmptyTreeHash, root2, proof1, true}, 372 // Roots match but the proof is not empty. 373 {0, 0, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, 374 {0, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, 375 {1, 1, sha256EmptyTreeHash, sha256EmptyTreeHash, proof2, true}, 376 } 377 for i, p := range tests { 378 t.Run(fmt.Sprintf("test:%d:snap:%d-%d", i, p.snap1, p.snap2), func(t *testing.T) { 379 err := verifierConsistencyCheck(&v, p.snap1, p.snap2, p.root1, p.root2, p.proof) 380 if p.wantErr && err == nil { 381 t.Errorf("Incorrectly verified") 382 } else if !p.wantErr && err != nil { 383 t.Errorf("Failed to verify: %v", err) 384 } 385 }) 386 } 387 388 for i := 0; i < 4; i++ { 389 p := consistencyProofs[i] 390 t.Run(fmt.Sprintf("proof:%d", i), func(t *testing.T) { 391 err := verifierConsistencyCheck(&v, p.snapshot1, p.snapshot2, 392 roots[p.snapshot1-1], roots[p.snapshot2-1], p.proof) 393 if err != nil { 394 t.Fatalf("Failed to verify known good proof: %s", err) 395 } 396 }) 397 } 398 } 399 400 func TestVerifyConsistencyProofGenerated(t *testing.T) { 401 size := int64(130) 402 tree, v := createTree(size) 403 roots := make([][]byte, size+1) 404 for i := int64(0); i <= size; i++ { 405 roots[i] = tree.RootAtSnapshot(i).Hash() 406 } 407 408 for i := int64(0); i <= size; i++ { 409 for j := i; j <= size; j++ { 410 proof := rawProof(tree.SnapshotConsistency(i, j)) 411 t.Run(fmt.Sprintf("size:%d:consistency:%d-%d", size, i, j), func(t *testing.T) { 412 if err := verifierConsistencyCheck(&v, i, j, roots[i], roots[j], proof); err != nil { 413 t.Errorf("verifierConsistencyCheck(): %v", err) 414 } 415 }) 416 } 417 } 418 } 419 420 func TestPrefixHashFromInclusionProofGenerated(t *testing.T) { 421 var sizes []int64 422 for s := 1; s <= 258; s++ { 423 sizes = append(sizes, int64(s)) 424 } 425 sizes = append(sizes, []int64{1024, 5050, 10000}...) 426 427 tree, v := createTree(0) 428 for _, size := range sizes { 429 growTree(tree, size) 430 root := tree.CurrentRoot().Hash() 431 432 for i := int64(1); i <= size; i++ { 433 t.Run(fmt.Sprintf("size:%d:prefix:%d", size, i), func(t *testing.T) { 434 leaf, proof := getLeafAndProof(tree, i-1) 435 pRoot, err := v.VerifiedPrefixHashFromInclusionProof(i, size, proof, root, leaf) 436 if err != nil { 437 t.Fatalf("VerifiedPrefixHashFromInclusionProof(): %v", err) 438 } 439 exp := tree.RootAtSnapshot(i).Hash() 440 if !bytes.Equal(pRoot, exp) { 441 t.Fatalf("wrong prefix hash: %s, want %s", shortHash(pRoot), shortHash(exp)) 442 } 443 }) 444 } 445 } 446 } 447 448 func TestPrefixHashFromInclusionProofErrors(t *testing.T) { 449 size := int64(307) 450 tree, v := createTree(size) 451 root := tree.CurrentRoot().Hash() 452 453 leaf2, proof2 := getLeafAndProof(tree, 2) 454 _, proof3 := getLeafAndProof(tree, 3) 455 _, proof301 := getLeafAndProof(tree, 301) 456 457 idxTests := []struct { 458 index int64 459 size int64 460 }{ 461 {-1, -1}, {-10, -1}, {-1, -10}, 462 {10, -1}, {10, 0}, {10, 9}, {0, 10}, 463 {0, -1}, {0, 0}, {-1, 0}, 464 {-1, size}, {0, size}, {size, size}, {size + 1, size}, {size + 100, size}, 465 } 466 for _, it := range idxTests { 467 if _, err := v.VerifiedPrefixHashFromInclusionProof(it.index, it.size, proof2, root, leaf2); err == nil { 468 t.Errorf("VerifiedPrefixHashFromInclusionProof(%d,%d): expected error", it.index, it.size) 469 } 470 } 471 472 if _, err := v.VerifiedPrefixHashFromInclusionProof(3, size, proof2, root, leaf2); err != nil { 473 t.Errorf("VerifiedPrefixHashFromInclusionProof(): %v, expected no error", err) 474 } 475 476 // Proof #3 has the same length, but doesn't verify against index #2. 477 // Neither does proof #301 as it has a different length. 478 for _, proof := range [][][]byte{proof3, proof301} { 479 if _, err := v.VerifiedPrefixHashFromInclusionProof(3, size, proof, root, leaf2); err == nil { 480 t.Error("VerifiedPrefixHashFromInclusionProof(): expected error") 481 } 482 } 483 } 484 485 // extend explicitly copies |proof| slice and appends |hashes| to it. 486 func extend(proof [][]byte, hashes ...[]byte) [][]byte { 487 res := make([][]byte, len(proof), len(proof)+len(hashes)) 488 copy(res, proof) 489 return append(res, hashes...) 490 } 491 492 // prepend adds |proof| to the tail of |hashes|. 493 func prepend(proof [][]byte, hashes ...[]byte) [][]byte { 494 return append(hashes, proof...) 495 } 496 497 func dh(h string, expLen int) []byte { 498 r, err := hex.DecodeString(h) 499 if err != nil { 500 panic(err) 501 } 502 if got := len(r); got != expLen { 503 panic(fmt.Sprintf("decode %q: len=%d, want %d", h, got, expLen)) 504 } 505 return r 506 } 507 508 func shortHash(hash []byte) string { 509 if len(hash) == 0 { 510 return "<empty>" 511 } 512 return fmt.Sprintf("%x...", hash[:4]) 513 } 514 515 func createTree(size int64) (*InMemoryMerkleTree, LogVerifier) { 516 tree := NewInMemoryMerkleTree(rfc6962.DefaultHasher) 517 growTree(tree, size) 518 return tree, NewLogVerifier(rfc6962.DefaultHasher) 519 } 520 521 func growTree(tree *InMemoryMerkleTree, upTo int64) { 522 for i := tree.LeafCount(); i < upTo; i++ { 523 data := []byte(fmt.Sprintf("data:%d", i)) 524 tree.AddLeaf(data) 525 } 526 } 527 528 func getLeafAndProof(tree *InMemoryMerkleTree, index int64) ([]byte, [][]byte) { 529 // Note: InMemoryMerkleTree counts leaves from 1. 530 proof := rawProof(tree.PathToCurrentRoot(index + 1)) 531 leafHash := tree.LeafHash(index + 1) 532 return leafHash, proof 533 } 534 535 func rawProof(desc []TreeEntryDescriptor) [][]byte { 536 proof := make([][]byte, len(desc)) 537 for i, d := range desc { 538 proof[i] = d.Value.Hash() 539 } 540 return proof 541 }