github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/helloworld/client.go (about)

     1  // Copyright 2021 Google LLC
     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  //    https://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 helloworld runs a simple client, designed to interact with a personality.
    16  package helloworld
    17  
    18  import (
    19  	"context"
    20  	"fmt"
    21  
    22  	trillian "github.com/google/trillian"
    23  	"github.com/google/trillian-examples/helloworld/personality"
    24  	"github.com/transparency-dev/merkle"
    25  	"github.com/transparency-dev/merkle/proof"
    26  	"github.com/transparency-dev/merkle/rfc6962"
    27  
    28  	"github.com/transparency-dev/formats/log"
    29  	"golang.org/x/mod/sumdb/note"
    30  )
    31  
    32  // Personality is the front-end for the Trillian log.
    33  type Personality interface {
    34  	// Append stores an entry in the log and, once it is there, returns a
    35  	// new checkpoint that commits to the new entry (in addition to all
    36  	// previous ones).
    37  	Append(ctx context.Context, entry []byte) (personality.SignedCheckpoint, error)
    38  
    39  	// ProveIncl proves the inclusion of a given entry in a given checkpoint size.
    40  	// It is the job of the client to verify this inclusion proof in order to convince
    41  	// itself that the entry really has been included in the log.
    42  	ProveIncl(ctx context.Context, cpSize uint64, entry []byte) (*trillian.Proof, error)
    43  
    44  	// UpdateChkpt provides a new checkpoint along with a consistency proof to it
    45  	// from the specified older checkpoint size.
    46  	// Again, it is the job of the client to verify this consistency proof.
    47  	UpdateChkpt(ctx context.Context, oldCPSize uint64) (personality.SignedCheckpoint, *trillian.Proof, error)
    48  }
    49  
    50  // Client is a verifier that maintains a checkpoint as state.
    51  type Client struct {
    52  	h           merkle.LogHasher
    53  	chkpt       *log.Checkpoint
    54  	person      Personality
    55  	sigVerifier note.Verifier
    56  }
    57  
    58  // NewClient creates a new client with an empty checkpoint and a given
    59  // personality to talk to.  In real usage, a client should persist this
    60  // checkpoint across different runs to ensure consistency.
    61  func NewClient(prsn Personality, nv note.Verifier) Client {
    62  	return Client{
    63  		person:      prsn,
    64  		h:           rfc6962.DefaultHasher,
    65  		sigVerifier: nv,
    66  	}
    67  }
    68  
    69  // VerIncl allows the client to check inclusion of a given entry.
    70  func (c Client) VerIncl(entry []byte, pf *trillian.Proof) bool {
    71  	leafHash := rfc6962.DefaultHasher.HashLeaf(entry)
    72  	if err := proof.VerifyInclusion(c.h, uint64(pf.LeafIndex), c.chkpt.Size, leafHash, pf.Hashes, c.chkpt.Hash); err != nil {
    73  		return false
    74  	}
    75  	return true
    76  }
    77  
    78  // UpdateChkpt allows a client to update its stored checkpoint.  In a real use
    79  // case it would be important for the client to check the signature contained
    80  // in the checkpoint before verifying consistency.
    81  func (c *Client) UpdateChkpt(chkptNewRaw personality.SignedCheckpoint, pf *trillian.Proof) error {
    82  	chkptNew, _, _, err := log.ParseCheckpoint(chkptNewRaw, "Hello World Log", c.sigVerifier)
    83  	if err != nil {
    84  		return fmt.Errorf("failed to verify checkpoint: %w", err)
    85  	}
    86  	// If there is no checkpoint then just use this one no matter what.
    87  	if c.chkpt.Size != 0 {
    88  		// Else make sure this new checkpoint is consistent with the current one.
    89  		hashes := pf.GetHashes()
    90  		if err := proof.VerifyConsistency(c.h, c.chkpt.Size, chkptNew.Size, hashes, c.chkpt.Hash, chkptNew.Hash); err != nil {
    91  			return fmt.Errorf("failed to verify consistency proof: %w", err)
    92  		}
    93  	}
    94  	// If all is good then set this as the new checkpoint.
    95  	c.chkpt = chkptNew
    96  	return nil
    97  }