github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/helloworld/personality/personality.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 personality runs a simple Trillian personality. 16 package personality 17 18 import ( 19 "context" 20 "flag" 21 "fmt" 22 "time" 23 24 "github.com/google/trillian" 25 tt "github.com/google/trillian/types" 26 "github.com/transparency-dev/formats/log" 27 "github.com/transparency-dev/merkle/rfc6962" 28 "golang.org/x/mod/sumdb/note" 29 "google.golang.org/grpc" 30 "google.golang.org/grpc/credentials/insecure" 31 ) 32 33 var ( 34 connectTimeout = flag.Duration("connect_timeout", 5*time.Second, "the timeout for connecting to the backend") 35 ) 36 37 // SignedCheckpoint is a serialised form of a checkpoint+signatures. 38 type SignedCheckpoint []byte 39 40 // TrillianP is a personality backed by a trillian log. 41 type TrillianP struct { 42 l trillian.TrillianLogClient 43 treeID int64 44 signer note.Signer 45 } 46 47 // NewPersonality creates a new Trillian personality from the flags. 48 func NewPersonality(logAddr string, treeID int64, s note.Signer) (*TrillianP, error) { 49 if treeID <= 0 { 50 return nil, fmt.Errorf("tree_id must be provided and positive, got %d", treeID) 51 } 52 53 ctx, cancel := context.WithTimeout(context.Background(), *connectTimeout) 54 defer cancel() 55 conn, err := grpc.DialContext(ctx, logAddr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()) 56 if err != nil { 57 return nil, fmt.Errorf("did not connect to trillian on %v: %v", logAddr, err) 58 } 59 60 log := trillian.NewTrillianLogClient(conn) 61 62 return &TrillianP{ 63 l: log, 64 treeID: treeID, 65 signer: s, 66 }, nil 67 } 68 69 // formLeaf creates a Trillian log leaf from an entry. 70 func (p *TrillianP) formLeaf(entry []byte) *trillian.LogLeaf { 71 leafHash := rfc6962.DefaultHasher.HashLeaf(entry) 72 return &trillian.LogLeaf{ 73 LeafValue: entry, 74 MerkleLeafHash: leafHash, 75 } 76 } 77 78 // getCheckpoint fetches the latest Trillian root and creates a checkpoint from it. 79 func (p *TrillianP) getCheckpoint(ctx context.Context) (*log.Checkpoint, error) { 80 req := trillian.GetLatestSignedLogRootRequest{LogId: p.treeID} 81 resp, err := p.l.GetLatestSignedLogRoot(ctx, &req) 82 if err != nil { 83 return nil, err 84 } 85 // Unpack the response and convert it to the local Checkpoint 86 // representation. 87 root := resp.GetSignedLogRoot() 88 var logRoot tt.LogRootV1 89 if err := logRoot.UnmarshalBinary(root.LogRoot); err != nil { 90 return nil, err 91 } 92 return &log.Checkpoint{ 93 Origin: "Hello World Log", 94 Hash: logRoot.RootHash, 95 Size: logRoot.TreeSize, 96 }, nil 97 } 98 99 // GetChkpt gets the latest checkpoint. 100 func (p *TrillianP) GetChkpt(ctx context.Context) (SignedCheckpoint, error) { 101 cp, err := p.getCheckpoint(ctx) 102 if err != nil { 103 return nil, fmt.Errorf("failed to fetch Trillian checkpoint: %w", err) 104 } 105 s, err := note.Sign(¬e.Note{Text: string(cp.Marshal())}, p.signer) 106 if err != nil { 107 return nil, err 108 } 109 return s, nil 110 } 111 112 // Append adds an entry to the Trillian log and waits to return the new checkpoint. 113 func (p *TrillianP) Append(ctx context.Context, entry []byte) (SignedCheckpoint, error) { 114 // First get the latest checkpoint. 115 chkpt, err := p.getCheckpoint(ctx) 116 if err != nil { 117 return nil, err 118 } 119 leaf := p.formLeaf(entry) 120 req := trillian.QueueLeafRequest{LogId: p.treeID, Leaf: leaf} 121 if _, err := p.l.QueueLeaf(ctx, &req); err != nil { 122 return nil, err 123 } 124 // Now fetch the new checkpoint, keep going until it's there and 125 // return an error at some point if it isn't. 126 for start := time.Now(); time.Since(start) < 5*time.Second; { 127 chkptNew, err := p.getCheckpoint(ctx) 128 if err != nil { 129 return nil, err 130 } 131 // TODO(meiklejohn): should probably verify that the specific entry was 132 // incorporated into the tree too. 133 if chkpt.Size < chkptNew.Size { 134 s, err := note.Sign(¬e.Note{Text: string(chkptNew.Marshal())}, p.signer) 135 if err != nil { 136 return nil, err 137 } 138 return s, nil 139 } 140 } 141 return nil, fmt.Errorf("did not get an updated checkpoint") 142 } 143 144 // ProveIncl returns an inclusion proof for a given checkpoint and entry. 145 func (p *TrillianP) ProveIncl(ctx context.Context, chkptSize uint64, entry []byte) (*trillian.Proof, error) { 146 // Form the leaf from the entry. 147 leaf := p.formLeaf(entry) 148 // Form the request according to the Trillian API. 149 req := trillian.GetInclusionProofByHashRequest{ 150 LogId: p.treeID, 151 LeafHash: leaf.MerkleLeafHash, 152 TreeSize: int64(chkptSize), 153 } 154 // Process the response. 155 resp, err := p.l.GetInclusionProofByHash(ctx, &req) 156 if err != nil { 157 return nil, err 158 } 159 return resp.GetProof()[0], nil 160 } 161 162 // UpdateChkpt gets the latest checkpoint for the Trillian log and proves its 163 // consistency with a provided one. 164 func (p *TrillianP) UpdateChkpt(ctx context.Context, chkptSize uint64) (SignedCheckpoint, *trillian.Proof, error) { 165 // First get the latest checkpoint 166 chkptNew, err := p.getCheckpoint(ctx) 167 if err != nil { 168 return nil, nil, err 169 } 170 // Now get a consistency proof if one is needed. 171 var pf *trillian.Proof 172 if chkptNew.Size > chkptSize { 173 req := trillian.GetConsistencyProofRequest{ 174 LogId: p.treeID, 175 FirstTreeSize: int64(chkptSize), 176 SecondTreeSize: int64(chkptNew.Size), 177 } 178 resp, err := p.l.GetConsistencyProof(ctx, &req) 179 if err != nil { 180 return nil, nil, err 181 } 182 pf = resp.GetProof() 183 } 184 s, err := note.Sign(¬e.Note{Text: string(chkptNew.Marshal())}, p.signer) 185 if err != nil { 186 return nil, nil, err 187 } 188 return s, pf, nil 189 }