diff options
Diffstat (limited to 'monitor/monitor.go')
-rw-r--r-- | monitor/monitor.go | 117 |
1 files changed, 117 insertions, 0 deletions
diff --git a/monitor/monitor.go b/monitor/monitor.go new file mode 100644 index 0000000..e291a63 --- /dev/null +++ b/monitor/monitor.go @@ -0,0 +1,117 @@ +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 +} |