-
Notifications
You must be signed in to change notification settings - Fork 11
/
log.go
131 lines (120 loc) · 5.42 KB
/
log.go
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
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Copyright 2024 The Tessera authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package tessera
import (
"crypto/sha256"
"errors"
"fmt"
"time"
f_log "github.com/transparency-dev/formats/log"
"github.com/transparency-dev/trillian-tessera/internal/options"
"golang.org/x/mod/sumdb/note"
"k8s.io/klog/v2"
)
// ErrPushback is returned by underlying storage implementations when there are too many
// entries with indices assigned but which have not yet been integrated into the tree.
//
// Personalities encountering this error should apply back-pressure to the source of new entries
// in an appropriate manner (e.g. for HTTP services, return a 503 with a Retry-After header).
var ErrPushback = errors.New("too many unintegrated entries")
// IndexFuture is the signature of a function which can return an assigned index or error.
//
// Implementations of this func are likely to be "futures", or a promise to return this data at
// some point in the future, and as such will block when called if the data isn't yet available.
type IndexFuture func() (uint64, error)
// WithCheckpointSigner is an option for setting the note signer and verifier to use when creating and parsing checkpoints.
//
// A primary signer must be provided:
// - the primary signer is the "canonical" signing identity which should be used when creating new checkpoints.
//
// Zero or more dditional signers may also be provided.
// This enables cases like:
// - a rolling key rotation, where checkpoints are signed by both the old and new keys for some period of time,
// - using different signature schemes for different audiences, etc.
//
// When providing additional signers, their names MUST be identical to the primary signer name, and this name will be used
// as the checkpoint Origin line.
//
// Checkpoints signed by these signer(s) will be standard checkpoints as defined by https://c2sp.org/tlog-checkpoint.
func WithCheckpointSigner(s note.Signer, additionalSigners ...note.Signer) func(*options.StorageOptions) {
origin := s.Name()
for _, signer := range additionalSigners {
if origin != signer.Name() {
klog.Exitf("WithCheckpointSigner: additional signer name (%q) does not match primary signer name (%q)", signer.Name(), origin)
}
}
return func(o *options.StorageOptions) {
o.NewCP = func(size uint64, hash []byte) ([]byte, error) {
// If we're signing a zero-sized tree, the tlog-checkpoint spec says (via RFC6962) that
// the root must be SHA256 of the empty string, so we'll enforce that here:
if size == 0 {
emptyRoot := sha256.Sum256([]byte{})
hash = emptyRoot[:]
}
cpRaw := f_log.Checkpoint{
Origin: origin,
Size: size,
Hash: hash,
}.Marshal()
n, err := note.Sign(¬e.Note{Text: string(cpRaw)}, append([]note.Signer{s}, additionalSigners...)...)
if err != nil {
return nil, fmt.Errorf("note.Sign: %w", err)
}
return n, nil
}
}
}
// WithBatching configures the batching behaviour of leaves being sequenced.
// A batch will be allowed to grow in memory until either:
// - the number of entries in the batch reach maxSize
// - the first entry in the batch has reached maxAge
//
// At this point the batch will be sent to the sequencer.
//
// Configuring these parameters allows the personality to tune to get the desired
// balance of sequencing latency with cost. In general, larger batches allow for
// lower cost of operation, where more frequent batches reduce the amount of time
// required for entries to be included in the log.
func WithBatching(maxSize uint, maxAge time.Duration) func(*options.StorageOptions) {
return func(o *options.StorageOptions) {
o.BatchMaxSize = maxSize
o.BatchMaxAge = maxAge
}
}
// WithPushback allows configuration of when the storage should start pushing back on add requests.
//
// maxOutstanding is the number of "in-flight" add requests - i.e. the number of entries with sequence numbers
// assigned, but which are not yet integrated into the log.
func WithPushback(maxOutstanding uint) func(*options.StorageOptions) {
return func(o *options.StorageOptions) {
o.PushbackMaxOutstanding = maxOutstanding
}
}
// WithCheckpointInterval configures the frequency at which Tessera will attempt to create & publish
// a new checkpoint.
//
// Well behaved clients of the log will only "see" newly sequenced entries once a new checkpoint is published,
// so it's important to consider the value being set.
//
// Regularly publishing new checkpoints:
// - helps show that the log is "live", even if no entries are being added.
// - enables clients of the log to reason about how frequently they need to have their
// view of the log refreshed, which in turn helps reduce work/load across the ecosystem.
//
// Note that this option probably only makes sense for long-lived applications (e.g. HTTP servers).
func WithCheckpointInterval(interval time.Duration) func(*options.StorageOptions) {
return func(o *options.StorageOptions) {
o.CheckpointInterval = interval
}
}