aboutsummaryrefslogtreecommitdiff
path: root/sumdb/client.go
diff options
context:
space:
mode:
Diffstat (limited to 'sumdb/client.go')
-rw-r--r--sumdb/client.go90
1 files changed, 58 insertions, 32 deletions
diff --git a/sumdb/client.go b/sumdb/client.go
index 8943eb3..136f611 100644
--- a/sumdb/client.go
+++ b/sumdb/client.go
@@ -8,9 +8,9 @@ import (
"bytes"
"errors"
"fmt"
+ "log"
"strings"
"sync"
- "sync/atomic"
"golang.org/x/mod/module"
"golang.org/x/mod/sumdb/note"
@@ -69,8 +69,6 @@ var ErrSecurity = errors.New("security error: misbehaving server")
type Client struct {
ops ClientOps // access to operations in the external world
- didLookup uint32
-
// one-time initialized data
initOnce sync.Once
initErr error // init error, if any
@@ -107,16 +105,16 @@ func (c *Client) init() error {
// initWork does the actual initialization work.
func (c *Client) initWork() {
+ log.Printf("initializing sumdb client")
defer func() {
+ log.Printf("done init")
if c.initErr != nil {
c.initErr = fmt.Errorf("initializing sumdb.Client: %v", c.initErr)
}
}()
c.tileReader.c = c
- if c.tileHeight == 0 {
- c.tileHeight = 8
- }
+ c.tileHeight = 8
c.tileSaved = make(map[tlog.Tile]bool)
vkey, err := c.ops.ReadConfig("key")
@@ -132,41 +130,24 @@ func (c *Client) initWork() {
c.verifiers = note.VerifierList(verifier)
c.name = verifier.Name()
+ log.Printf("init: reading latest")
data, err := c.ops.ReadConfig(c.name + "/latest")
if err != nil {
c.initErr = err
return
}
+
+ log.Printf("init: merging latest")
if err := c.mergeLatest(data); err != nil {
c.initErr = err
return
}
}
-// SetTileHeight sets the tile height for the Client.
-// Any call to SetTileHeight must happen before the first call to Lookup.
-// If SetTileHeight is not called, the Client defaults to tile height 8.
-// SetTileHeight can be called at most once,
-// and if so it must be called before the first call to Lookup.
-func (c *Client) SetTileHeight(height int) {
- if atomic.LoadUint32(&c.didLookup) != 0 {
- panic("SetTileHeight used after Lookup")
- }
- if height <= 0 {
- panic("invalid call to SetTileHeight")
- }
- if c.tileHeight != 0 {
- panic("multiple calls to SetTileHeight")
- }
- c.tileHeight = height
-}
-
// Lookup returns the go.sum lines for the given module path and version.
// The version may end in a /go.mod suffix, in which case Lookup returns
// the go.sum lines for the module's go.mod-only hash.
func (c *Client) Lookup(path, vers string) (lines []string, err error) {
- atomic.StoreUint32(&c.didLookup, 1)
-
defer func() {
if err != nil {
err = fmt.Errorf("%s@%s: %v", path, vers, err)
@@ -199,7 +180,6 @@ func (c *Client) Lookup(path, vers string) (lines []string, err error) {
err error
}
result := c.record.Do(file, func() interface{} {
- // Try the on-disk cache, or else get from web.
data, err := c.ops.ReadRemote(remotePath)
if err != nil {
return cached{nil, err}
@@ -235,6 +215,37 @@ func (c *Client) Lookup(path, vers string) (lines []string, err error) {
return hashes, nil
}
+func (c *Client) FetchLatest() (*tlog.Tree, error) {
+ if err := c.init(); err != nil {
+ return nil, err
+ }
+
+ msg, err := c.ops.ReadRemote("/latest")
+ if err != nil {
+ return nil, err
+ }
+ note, err := note.Open(msg, c.verifiers)
+ if err != nil {
+ return nil, fmt.Errorf("reading latest note: %v\nnote:\n%s", err, msg)
+ }
+ tree, err := tlog.ParseTree([]byte(note.Text))
+ if err != nil {
+ return nil, fmt.Errorf("reading tree: %v\ntree:\n%s", err, note.Text)
+ }
+ return &tree, nil
+}
+
+func (c *Client) FetchTreeProof(t *tlog.Tree) error {
+ log.Printf("fetching proof for tree %v", t)
+
+ if err := c.checkTrees(tlog.Tree{}, nil, *t, nil); err != nil {
+ log.Printf("check tree error: %v", err)
+ return err
+ }
+
+ return nil
+}
+
// mergeLatest merges the tree head in msg
// with the Client's current latest tree head,
// ensuring the result is a consistent timeline.
@@ -245,14 +256,17 @@ func (c *Client) Lookup(path, vers string) (lines []string, err error) {
// mergeLatest updates the underlying configuration file as well,
// taking care to merge any independent updates to that configuration.
func (c *Client) mergeLatest(msg []byte) error {
+ log.Printf("merge latest: start")
// Merge msg into our in-memory copy of the latest tree head.
when, err := c.mergeLatestMem(msg)
if err != nil {
return err
}
+ log.Printf("merge latest: when=%d", when)
if when != msgFuture {
// msg matched our present or was in the past.
// No change to our present, so no update of config file.
+ log.Printf("merge latest: future; done")
return nil
}
@@ -261,6 +275,7 @@ func (c *Client) mergeLatest(msg []byte) error {
// we need to merge any updates made there as well.
// Note that writeConfig is an atomic compare-and-swap.
for {
+ log.Printf("merge latest: reading latest...")
msg, err := c.ops.ReadConfig(c.name + "/latest")
if err != nil {
return err
@@ -301,6 +316,7 @@ const (
// msgNow means msg was exactly c.latest, and
// msgFuture means msg was from after c.latest, which has now been updated.
func (c *Client) mergeLatestMem(msg []byte) (when int, err error) {
+ log.Printf("mergeLatestMem: start with msg=%v", msg)
if len(msg) == 0 {
// Accept empty msg as the unsigned, empty timeline.
c.latestMu.Lock()
@@ -312,6 +328,7 @@ func (c *Client) mergeLatestMem(msg []byte) (when int, err error) {
return msgPast, nil
}
+ log.Printf("mergeMemLatest: reading tree note: %s", msg)
note, err := note.Open(msg, c.verifiers)
if err != nil {
return 0, fmt.Errorf("reading tree note: %v\nnote:\n%s", err, msg)
@@ -332,6 +349,7 @@ func (c *Client) mergeLatestMem(msg []byte) (when int, err error) {
for {
// If the tree head looks old, check that it is on our timeline.
if tree.N <= latest.N {
+ log.Printf("mergeLatestMem: looks old")
if err := c.checkTrees(tree, msg, latest, latestMsg); err != nil {
return 0, err
}
@@ -343,9 +361,12 @@ func (c *Client) mergeLatestMem(msg []byte) (when int, err error) {
// The tree head looks new. Check that we are on its timeline and try to move our timeline forward.
if err := c.checkTrees(latest, latestMsg, tree, msg); err != nil {
+ log.Printf("mergeLatestMem: looks new")
return 0, err
}
+ log.Printf("mergeLatestMem: install msg if possible")
+
// Install our msg if possible.
// Otherwise we will go around again.
c.latestMu.Lock()
@@ -371,6 +392,7 @@ func (c *Client) mergeLatestMem(msg []byte) (when int, err error) {
// If on the other hand checkTrees finds evidence of misbehavior, it prepares a detailed
// message and calls log.Fatal.
func (c *Client) checkTrees(older tlog.Tree, olderNote []byte, newer tlog.Tree, newerNote []byte) error {
+ log.Printf("checking trees: older=%v, newer=%v", older, newer)
thr := tlog.TileHashReader(newer, &c.tileReader)
h, err := tlog.TreeHash(older.N, thr)
if err != nil {
@@ -379,9 +401,12 @@ func (c *Client) checkTrees(older tlog.Tree, olderNote []byte, newer tlog.Tree,
}
return fmt.Errorf("checking tree#%d against tree#%d: %v", older.N, newer.N, err)
}
+ log.Printf("computed tree hash %v", h)
if h == older.Hash {
+ log.Printf("hashes match: %v", older.Hash)
return nil
}
+ log.Printf("HASHES DO NOT MATCH h=%v older=%v", h, older.Hash)
// Detected a fork in the tree timeline.
// Start by reporting the inconsistent signed tree notes.
@@ -420,6 +445,7 @@ func (c *Client) checkTrees(older tlog.Tree, olderNote []byte, newer tlog.Tree,
// checkRecord checks that record #id's hash matches data.
func (c *Client) checkRecord(id int64, data []byte) error {
+ log.Printf("checking record %d with %s", id, data)
c.latestMu.Lock()
latest := c.latest
c.latestMu.Unlock()
@@ -431,7 +457,9 @@ func (c *Client) checkRecord(id int64, data []byte) error {
if err != nil {
return err
}
+ log.Printf("checkRecord: got hashes %v", hashes)
if hashes[0] == tlog.RecordHash(data) {
+ log.Printf("tile hash %s matches record hash %s", hashes[0], data)
return nil
}
return fmt.Errorf("cannot authenticate record data in server response")
@@ -445,12 +473,14 @@ type tileReader struct {
}
func (r *tileReader) Height() int {
+ log.Printf("checking height")
return r.c.tileHeight
}
// ReadTiles reads and returns the requested tiles,
// either from the on-disk cache or the server.
func (r *tileReader) ReadTiles(tiles []tlog.Tile) ([][]byte, error) {
+ log.Printf("reading %d tiles", len(tiles))
// Read all the tiles in parallel.
data := make([][]byte, len(tiles))
errs := make([]error, len(tiles))
@@ -459,6 +489,7 @@ func (r *tileReader) ReadTiles(tiles []tlog.Tile) ([][]byte, error) {
wg.Add(1)
go func(i int, tile tlog.Tile) {
defer wg.Done()
+ log.Printf("reading tile %v", tile)
data[i], errs[i] = r.c.readTile(tile)
}(i, tile)
}
@@ -473,11 +504,6 @@ func (r *tileReader) ReadTiles(tiles []tlog.Tile) ([][]byte, error) {
return data, nil
}
-// tileCacheKey returns the cache key for the tile.
-func (c *Client) tileCacheKey(tile tlog.Tile) string {
- return c.name + "/" + tile.Path()
-}
-
// tileRemotePath returns the remote path for the tile.
func (c *Client) tileRemotePath(tile tlog.Tile) string {
return "/" + tile.Path()