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
}
|