Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 12 additions & 42 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package client

import (
"bytes"
"context"
"errors"
"fmt"
Expand Down Expand Up @@ -181,49 +180,17 @@ func GetEntryBundle(ctx context.Context, f EntryBundleFetcherFunc, i, logSize ui
// Since the tiles commit only to immutable nodes, the job of building proofs is slightly
// more complex as proofs can touch "ephemeral" nodes, so these need to be synthesized.
type ProofBuilder struct {
cp log.Checkpoint
treeSize uint64
nodeCache nodeCache
}

// NewProofBuilder creates a new ProofBuilder object for a given tree size.
// The returned ProofBuilder can be re-used for proofs related to a given tree size, but
// it is not thread-safe and should not be accessed concurrently.
func NewProofBuilder(ctx context.Context, cp log.Checkpoint, f TileFetcherFunc) (*ProofBuilder, error) {
ctx, span := tracer.Start(ctx, "tessera.client.NewProofBuilder")
defer span.End()

span.SetAttributes(logSizeKey.Int64(otel.Clamp64(cp.Size)))

func NewProofBuilder(ctx context.Context, treeSize uint64, f TileFetcherFunc) (*ProofBuilder, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to mention "no correctness checking of the root hash for the given tree size is performed." in this function doc comment?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think so, it's probably more surprising that a call to this function would go off and start making network requests before calling one of the .*Proof functions below.

pb := &ProofBuilder{
cp: cp,
nodeCache: newNodeCache(f, cp.Size),
}
// Can't re-create the root of a zero size checkpoint other than by convention,
// so return early here in that case.
if cp.Size == 0 {
return pb, nil
}

hashes, err := FetchRangeNodes(ctx, cp.Size, f)
if err != nil {
return nil, fmt.Errorf("failed to fetch range nodes: %v", err)
}
// Create a compact range which represents the state of the log.
r, err := (&compact.RangeFactory{Hash: hasher.HashChildren}).NewRange(0, cp.Size, hashes)
if err != nil {
return nil, err
}

// Recreate the root hash so that:
// a) we validate the self-integrity of the log state, and
// b) we calculate (and cache) and ephemeral nodes present in the tree,
// this is important since they could be required by proofs.
sr, err := r.GetRootHash(pb.nodeCache.SetEphemeralNode)
if err != nil {
return nil, err
}
if !bytes.Equal(cp.Hash, sr) {
return nil, fmt.Errorf("invalid checkpoint hash %x, expected %x", cp.Hash, sr)
treeSize: treeSize,
nodeCache: newNodeCache(f, treeSize),
}
return pb, nil
}
Expand All @@ -238,22 +205,25 @@ func (pb *ProofBuilder) InclusionProof(ctx context.Context, index uint64) ([][]b

span.SetAttributes(indexKey.Int64(otel.Clamp64(index)))

nodes, err := proof.Inclusion(index, pb.cp.Size)
nodes, err := proof.Inclusion(index, pb.treeSize)
if err != nil {
return nil, fmt.Errorf("failed to calculate inclusion proof node list: %v", err)
}
return pb.fetchNodes(ctx, nodes)
}

// ConsistencyProof constructs a consistency proof between the two passed in tree sizes.
// ConsistencyProof constructs a consistency proof between the provided tree sizes.
// This function uses the passed-in function to retrieve tiles containing any log tree
// nodes necessary to build the proof.
func (pb *ProofBuilder) ConsistencyProof(ctx context.Context, smaller, larger uint64) ([][]byte, error) {
ctx, span := tracer.Start(ctx, "tessera.client.ConsistencyProof")
defer span.End()

span.SetAttributes(smallerKey.Int64(otel.Clamp64(smaller)), largerKey.Int64(otel.Clamp64(larger)))

if m := max(smaller, larger); m > pb.treeSize {
return nil, fmt.Errorf("requested consistency proof to %d which is larger than tree size %d", m, pb.treeSize)
}

nodes, err := proof.Consistency(smaller, larger)
if err != nil {
return nil, fmt.Errorf("failed to calculate consistency proof node list: %v", err)
Expand Down Expand Up @@ -318,7 +288,7 @@ func NewLogStateTracker(ctx context.Context, tF TileFetcherFunc, checkpointRaw [
return ret, err
}
ret.latestConsistent = *cp
ret.proofBuilder, err = NewProofBuilder(ctx, ret.latestConsistent, ret.tileFetcher)
ret.proofBuilder, err = NewProofBuilder(ctx, ret.latestConsistent.Size, ret.tileFetcher)
if err != nil {
return ret, fmt.Errorf("NewProofBuilder: %v", err)
}
Expand All @@ -343,7 +313,7 @@ func (lst *LogStateTracker) Update(ctx context.Context) ([]byte, [][]byte, []byt
if err != nil {
return nil, nil, nil, err
}
builder, err := NewProofBuilder(ctx, *c, lst.tileFetcher)
builder, err := NewProofBuilder(ctx, c.Size, lst.tileFetcher)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to create proof builder: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion client/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ func TestHandleZeroRoot(t *testing.T) {
if len(zeroCP.Hash) == 0 {
t.Fatal("BadTestData: checkpoint.0 has empty root hash")
}
if _, err := NewProofBuilder(context.Background(), zeroCP, testLogTileFetcher); err != nil {
if _, err := NewProofBuilder(context.Background(), zeroCP.Size, testLogTileFetcher); err != nil {
t.Fatalf("NewProofBuilder: %v", err)
}
}
Expand Down
2 changes: 1 addition & 1 deletion integration/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ func TestLiveLogIntegration(t *testing.T) {
}

// Step 4.2 - Test inclusion proofs.
pb, err := client.NewProofBuilder(ctx, lst.Latest(), logReadTile)
pb, err := client.NewProofBuilder(ctx, lst.Latest().Size, logReadTile)
if err != nil {
t.Errorf("client.NewProofBuilder: %v", err)
}
Expand Down
2 changes: 1 addition & 1 deletion internal/witness/witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ func (wg *WitnessGateway) Witness(ctx context.Context, cp []byte) ([]byte, error
Size: size,
Hash: hash,
}
pb, err := client.NewProofBuilder(ctx, logCP, wg.fetchTile)
pb, err := client.NewProofBuilder(ctx, logCP.Size, wg.fetchTile)
if err != nil {
return nil, fmt.Errorf("failed to build proof builder: %v", err)
}
Expand Down
Loading