diff options
Diffstat (limited to 'sumdb/client.go')
-rw-r--r-- | sumdb/client.go | 90 |
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() |