aboutsummaryrefslogtreecommitdiff
path: root/monitor/monitor.go
diff options
context:
space:
mode:
Diffstat (limited to 'monitor/monitor.go')
-rw-r--r--monitor/monitor.go117
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
+}