aboutsummaryrefslogtreecommitdiff
path: root/monitor/monitor.go
blob: e291a63319593d0a4364e294afa70c2c39668931 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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
}