github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/clone/cmd/ctverify/ctverify.go (about)

     1  // Copyright 2021 Google LLC. All Rights Reserved.
     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  //     http://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  // ctverify checks that leaf data downloaded by ctclone is committed to by a checkpoint.
    16  package main
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"encoding/json"
    22  	"flag"
    23  	"io"
    24  	"os"
    25  
    26  	"github.com/golang/glog"
    27  	"github.com/google/trillian-examples/clone/internal/verify"
    28  	"github.com/google/trillian-examples/clone/logdb"
    29  	"github.com/transparency-dev/merkle/rfc6962"
    30  
    31  	_ "github.com/go-sql-driver/mysql"
    32  )
    33  
    34  var (
    35  	mysqlURI = flag.String("mysql_uri", "", "URL of the MySQL database containing the log.")
    36  )
    37  
    38  func main() {
    39  	flag.Parse()
    40  	ctx := context.Background()
    41  
    42  	if len(*mysqlURI) == 0 {
    43  		glog.Exit("Missing required parameter 'mysql_uri'")
    44  	}
    45  	db, err := logdb.NewDatabase(*mysqlURI)
    46  	if err != nil {
    47  		glog.Exitf("Failed to connect to database: %q", err)
    48  	}
    49  
    50  	bs, err := io.ReadAll(os.Stdin)
    51  	if err != nil {
    52  		glog.Exitf("Failed to read checkpoint from stdin: %q", err)
    53  	}
    54  	cp := CTCheckpointResponse{}
    55  	if err := json.Unmarshal(bs, &cp); err != nil {
    56  		glog.Exitf("Failed to read checkpoint from stdin: %q", err)
    57  	}
    58  	glog.Infof("Parsed checkpoint with size %d and root hash %x. Calculating root hash for local data...", cp.TreeSize, cp.RootHash)
    59  
    60  	h := rfc6962.DefaultHasher
    61  	lh := func(_ uint64, preimage []byte) []byte {
    62  		return h.HashLeaf(preimage)
    63  	}
    64  	v := verify.NewLogVerifier(db, lh, h.HashChildren)
    65  	root, crs, err := v.MerkleRoot(ctx, cp.TreeSize)
    66  	if err != nil {
    67  		glog.Exitf("Failed to compute root: %q", err)
    68  	}
    69  	if bytes.Equal(cp.RootHash, root) {
    70  		glog.Infof("Got matching roots for tree size %d: %x", cp.TreeSize, root)
    71  	} else {
    72  		glog.Exitf("Computed root %x != provided checkpoint %x for tree size %d", root, cp.RootHash, cp.TreeSize)
    73  	}
    74  	if err := db.WriteCheckpoint(ctx, cp.TreeSize, bs, crs); err != nil {
    75  		glog.Exitf("Failed to update database with new checkpoint: %v", err)
    76  	}
    77  	glog.Info("Updated database with checkpoint")
    78  }
    79  
    80  // CTCheckpointResponse mirrors the RFC6962 STH format for `get-sth` to allow the
    81  // data to be easy unmarshalled from the JSON response.
    82  type CTCheckpointResponse struct {
    83  	TreeSize  uint64 `json:"tree_size"`
    84  	Timestamp uint64 `json:"timestamp"`
    85  	RootHash  []byte `json:"sha256_root_hash"`
    86  	Sig       []byte `json:"tree_head_signature"`
    87  }