Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
58 changes: 13 additions & 45 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,23 +205,24 @@ 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 smaller passed-in treesize, and
// the larger size the ProofBuilder was constructed for.
// 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) {
func (pb *ProofBuilder) ConsistencyProof(ctx context.Context, smaller 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)))
span.SetAttributes(smallerKey.Int64(otel.Clamp64(smaller)), largerKey.Int64(otel.Clamp64(pb.treeSize)))

nodes, err := proof.Consistency(smaller, larger)
nodes, err := proof.Consistency(smaller, pb.treeSize)
if err != nil {
return nil, fmt.Errorf("failed to calculate consistency proof node list: %v", err)
}
Expand Down Expand Up @@ -318,7 +286,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 +311,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 All @@ -354,7 +322,7 @@ func (lst *LogStateTracker) Update(ctx context.Context) ([]byte, [][]byte, []byt
if c.Size <= lst.latestConsistent.Size {
return lst.latestConsistentRaw, p, lst.latestConsistentRaw, nil
}
p, err = builder.ConsistencyProof(ctx, lst.latestConsistent.Size, c.Size)
p, err = builder.ConsistencyProof(ctx, lst.latestConsistent.Size)
if err != nil {
return nil, nil, nil, 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
4 changes: 2 additions & 2 deletions 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 Expand Up @@ -188,7 +188,7 @@ func (pf *sharedConsistencyProofFetcher) ConsistencyProof(ctx context.Context, s
pf.mu.Lock()
if f, ok = pf.results[smaller]; !ok {
f = sync.OnceValues(func() ([][]byte, error) {
return pf.pb.ConsistencyProof(ctx, smaller, larger)
return pf.pb.ConsistencyProof(ctx, smaller)
})
pf.results[smaller] = f
}
Expand Down
Loading