package monitor import ( "fmt" "log" "sync" "time" "golang.org/x/mod/sumdb/tlog" ) type Monitor struct { db *Database // the latest verified tree latest tlog.Tree } func NewMonitor(db *Database) *Monitor { return &Monitor{db: db} } // Watch polls the database to check for a new Signed Tree Head every interval. // It runs indefinitely; only returning an error if an inconsistency is found. func (m *Monitor) Watch(interval time.Duration) error { for { log.Printf("checking for new STH") t, err := m.db.FetchSTH() if err != nil { return err } log.Printf("got STH: %#v", t) if m.latest.N == t.N { log.Printf("current STH matches latest proved") } else { if err := m.TreeProof(m.latest, t); err != nil { return err } log.Printf("proof succeeded") m.latest = t } time.Sleep(interval) } return nil } // TreeProof proves that tree older is a prefix of tree newer, returning an // error if the proof fails. func (m *Monitor) TreeProof(older, newer tlog.Tree) error { log.Printf("proving that N=%d is a prefix of N'=%d", older.N, newer.N) if older.N == 0 { log.Printf("N=0, so using bootstrap proof") return m.BootstrapProof(newer) } thr := tlog.TileHashReader(newer, m) p, err := tlog.ProveTree(newer.N, older.N, thr) if err != nil { return err } log.Printf("Proof: %#v", p) return nil } func (m *Monitor) BootstrapProof(t tlog.Tree) error { thr := tlog.TileHashReader(t, m) h, err := tlog.TreeHash(t.N, thr) if err != nil { return err } log.Printf("computed hash: %v", h) log.Printf("expected hash: %v", t.Hash) if h != t.Hash { return fmt.Errorf("computed hash %v does not match expected: %v", h, t.Hash) } return nil } func (m *Monitor) Height() int { return 8 } // ReadTiles reads and returns the requested tiles, // either from the on-disk cache or the server. func (m *Monitor) 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)) var wg sync.WaitGroup for i, tile := range tiles { wg.Add(1) go func(i int, tile tlog.Tile) { defer wg.Done() log.Printf("reading tile %v", tile) data[i], errs[i] = m.readTile(tile) }(i, tile) } wg.Wait() for _, err := range errs { if err != nil { return nil, err } } return data, nil } func (m *Monitor) readTile(tile tlog.Tile) ([]byte, error) { return m.db.readRemote("/" + tile.Path()) } func (m *Monitor) SaveTiles(tiles []tlog.Tile, data [][]byte) { // noop }