package server
import (
"context"
"encoding/hex"
"fmt"
"testing"
"github.com/google/trillian/storage/testonly"
"github.com/transparency-dev/merkle/proof"
"github.com/transparency-dev/merkle/rfc6962"
inmemory "github.com/transparency-dev/merkle/testonly"
)
const testTreeRevision int64 = 3
func TestTree813FetchAll(t *testing.T) {
ctx := context.Background()
hasher := rfc6962.DefaultHasher.HashChildren
const ts uint64 = 813
mt := treeAtSize(ts)
r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{
{TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()},
})
for l := uint64(271); l < ts; l++ {
nodes, err := proof.Inclusion(l, ts)
if err != nil {
t.Fatal(err)
}
proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes)
if err != nil {
t.Fatal(err)
}
if got, want := proof.LeafIndex, int64(l); got != want {
t.Errorf("leaf index mismatch: got %d, want %d", got, want)
}
refProof, err := mt.InclusionProof(l, ts)
if err != nil {
t.Fatalf("InclusionProof: %v", err)
}
if got, want := len(proof.Hashes), len(refProof); got != want {
for i, id := range nodes.IDs {
t.Errorf("Fetch: %d => %+v", i, id)
}
t.Fatalf("(%d, %d): got proof len: %d, want: %d: %v\n%v", ts, l, got, want, nodes, refProof)
}
for i := 0; i < len(proof.Hashes); i++ {
if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want {
t.Fatalf("(%d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, l, i, got, want, len(proof.Hashes), nodes)
}
}
}
}
func TestTree32InclusionProofFetchAll(t *testing.T) {
ctx := context.Background()
hasher := rfc6962.DefaultHasher.HashChildren
for ts := uint64(2); ts <= 32; ts++ {
mt := treeAtSize(ts)
r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{
{TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()},
})
for s := uint64(2); s <= ts; s++ {
for l := uint64(0); l < s; l++ {
nodes, err := proof.Inclusion(l, s)
if err != nil {
t.Fatal(err)
}
proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes)
if err != nil {
t.Fatal(err)
}
if got, want := proof.LeafIndex, int64(l); got != want {
t.Errorf("leaf index mismatch: got %d, want %d", got, want)
}
refProof, err := mt.InclusionProof(l, s)
if err != nil {
t.Fatalf("InclusionProof: %v", err)
}
if got, want := len(proof.Hashes), len(refProof); got != want {
t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", ts, s, l, got, want, nodes, refProof)
}
for i := 0; i < len(proof.Hashes); i++ {
if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want {
t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, s, l, i, got, want, len(proof.Hashes), nodes)
}
}
}
}
}
}
func TestTree32InclusionProofFetchMultiBatch(t *testing.T) {
ctx := context.Background()
hasher := rfc6962.DefaultHasher.HashChildren
mt := treeAtSize(32)
r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{
{TreeRevision: testTreeRevision, Leaves: expandLeaves(0, 7), ExpectedRoot: treeAtSize(8).Hash()},
{TreeRevision: testTreeRevision + 1, Leaves: expandLeaves(8, 15), ExpectedRoot: treeAtSize(16).Hash()},
{TreeRevision: testTreeRevision + 2, Leaves: expandLeaves(16, 23), ExpectedRoot: treeAtSize(24).Hash()},
{TreeRevision: testTreeRevision + 3, Leaves: expandLeaves(24, 31), ExpectedRoot: mt.Hash()},
})
for s := uint64(2); s <= 32; s++ {
for l := uint64(0); l < s; l++ {
nodes, err := proof.Inclusion(l, s)
if err != nil {
t.Fatal(err)
}
proof, err := fetchNodesAndBuildProof(ctx, r, hasher, l, nodes)
if err != nil {
t.Fatal(err)
}
refProof, err := mt.InclusionProof(l, s)
if err != nil {
t.Fatalf("InclusionProof: %v", err)
}
if got, want := len(proof.Hashes), len(refProof); got != want {
t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", 32, s, l, got, want, nodes, refProof)
}
for i := 0; i < len(proof.Hashes); i++ {
if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want {
t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", 32, s, l, i, got, want, len(proof.Hashes), nodes)
}
}
}
}
}
func TestTree32ConsistencyProofFetchAll(t *testing.T) {
ctx := context.Background()
hasher := rfc6962.DefaultHasher.HashChildren
for ts := uint64(2); ts <= 32; ts++ {
mt := treeAtSize(ts)
r := testonly.NewMultiFakeNodeReaderFromLeaves([]testonly.LeafBatch{
{TreeRevision: testTreeRevision, Leaves: expandLeaves(0, ts-1), ExpectedRoot: mt.Hash()},
})
for s1 := uint64(2); s1 < ts; s1++ {
for s2 := uint64(s1 + 1); s2 < ts; s2++ {
nodes, err := proof.Consistency(s1, s2)
if err != nil {
t.Fatal(err)
}
proof, err := fetchNodesAndBuildProof(ctx, r, hasher, s1, nodes)
if err != nil {
t.Fatal(err)
}
refProof, err := mt.ConsistencyProof(s1, s2)
if err != nil {
t.Fatalf("ConsistencyProof: %v", err)
}
if got, want := len(proof.Hashes), len(refProof); got != want {
t.Fatalf("(%d, %d, %d): got proof len: %d, want: %d: %v\n%v", ts, s1, s2, got, want, nodes, refProof)
}
for i := 0; i < len(proof.Hashes); i++ {
if got, want := hex.EncodeToString(proof.Hashes[i]), hex.EncodeToString(refProof[i]); got != want {
t.Fatalf("(%d, %d, %d): %d got proof node: %s, want: %s l:%d nodes: %v", ts, s1, s2, i, got, want, len(proof.Hashes), nodes)
}
}
}
}
}
}
func expandLeaves(n, m uint64) []string {
leaves := make([]string, 0, m-n+1)
for l := n; l <= m; l++ {
leaves = append(leaves, fmt.Sprintf("Leaf %d", l))
}
return leaves
}
func treeAtSize(n uint64) *inmemory.Tree {
leaves := expandLeaves(0, n-1)
mt := inmemory.New(rfc6962.DefaultHasher)
for _, leaf := range leaves {
mt.AppendData([]byte(leaf))
}
return mt
}