-
Notifications
You must be signed in to change notification settings - Fork 7
Add MMD verification in CT Hammer #167
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
6dc37e7
a5a6681
c0b1c95
d1c6363
e13d3fe
8736695
5e70a73
c8939d3
22624bb
c6c42a1
643cbbf
fd99c44
8c46bdd
eaa9975
7fb3c47
091816d
3c18e92
304dc0a
02f7de7
8fb6474
4b8fb44
593fad1
b1154cf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -155,42 +155,42 @@ type httpLeafWriter struct { | |
bearerToken string | ||
} | ||
|
||
func (w httpLeafWriter) Write(ctx context.Context, newLeaf []byte) (uint64, error) { | ||
func (w httpLeafWriter) Write(ctx context.Context, newLeaf []byte) (uint64, uint64, error) { | ||
req, err := http.NewRequest(http.MethodPost, w.u.String(), bytes.NewReader(newLeaf)) | ||
if err != nil { | ||
return 0, fmt.Errorf("failed to create request: %v", err) | ||
return 0, 0, fmt.Errorf("failed to create request: %v", err) | ||
} | ||
if w.bearerToken != "" { | ||
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", w.bearerToken)) | ||
} | ||
resp, err := w.hc.Do(req.WithContext(ctx)) | ||
if err != nil { | ||
return 0, fmt.Errorf("failed to write leaf: %v", err) | ||
return 0, 0, fmt.Errorf("failed to write leaf: %v", err) | ||
} | ||
body, err := io.ReadAll(resp.Body) | ||
_ = resp.Body.Close() | ||
if err != nil { | ||
return 0, fmt.Errorf("failed to read body: %v", err) | ||
return 0, 0, fmt.Errorf("failed to read body: %v", err) | ||
} | ||
switch resp.StatusCode { | ||
case http.StatusOK: | ||
if resp.Request.Method != http.MethodPost { | ||
return 0, fmt.Errorf("write leaf was redirected to %s", resp.Request.URL) | ||
return 0, 0, fmt.Errorf("write leaf was redirected to %s", resp.Request.URL) | ||
} | ||
// Continue below | ||
case http.StatusServiceUnavailable, http.StatusBadGateway, http.StatusGatewayTimeout: | ||
// These status codes may indicate a delay before retrying, so handle that here: | ||
time.Sleep(retryDelay(resp.Header.Get("RetryAfter"), time.Second)) | ||
|
||
return 0, fmt.Errorf("log not available. Status code: %d. Body: %q %w", resp.StatusCode, body, ErrRetry) | ||
return 0, 0, fmt.Errorf("log not available. Status code: %d. Body: %q %w", resp.StatusCode, body, ErrRetry) | ||
default: | ||
return 0, fmt.Errorf("write leaf was not OK. Status code: %d. Body: %q", resp.StatusCode, body) | ||
return 0, 0, fmt.Errorf("write leaf was not OK. Status code: %d. Body: %q", resp.StatusCode, body) | ||
} | ||
index, err := parseAddChainResponse(body) | ||
index, timestamp, err := parseAddChainResponse(body) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe in a followup PR, you could check here that the SCT matches with the request. |
||
if err != nil { | ||
return 0, fmt.Errorf("write leaf failed to parse response: %v", body) | ||
return 0, 0, fmt.Errorf("write leaf failed to parse response: %v", body) | ||
} | ||
return index, nil | ||
return index, timestamp, nil | ||
} | ||
|
||
func retryDelay(retryAfter string, defaultDur time.Duration) time.Duration { | ||
|
@@ -216,7 +216,7 @@ type roundRobinLeafWriter struct { | |
ws []httpLeafWriter | ||
} | ||
|
||
func (rr *roundRobinLeafWriter) Write(ctx context.Context, newLeaf []byte) (uint64, error) { | ||
func (rr *roundRobinLeafWriter) Write(ctx context.Context, newLeaf []byte) (uint64, uint64, error) { | ||
w := rr.next() | ||
return w(ctx, newLeaf) | ||
} | ||
|
@@ -232,39 +232,39 @@ func (rr *roundRobinLeafWriter) next() LeafWriter { | |
} | ||
|
||
// parseAddChainResponse parses the add-chain response and returns the leaf | ||
// index from the extensions. | ||
// index from the extensions and timestamp from the response. | ||
// Code is inspired by https://github.com/FiloSottile/sunlight/blob/main/tile.go. | ||
func parseAddChainResponse(body []byte) (uint64, error) { | ||
func parseAddChainResponse(body []byte) (uint64, uint64, error) { | ||
var resp types.AddChainResponse | ||
if err := json.Unmarshal(body, &resp); err != nil { | ||
return 0, fmt.Errorf("can't parse add-chain response: %v", err) | ||
return 0, 0, fmt.Errorf("can't parse add-chain response: %v", err) | ||
} | ||
|
||
extensionBytes, err := base64.StdEncoding.DecodeString(resp.Extensions) | ||
if err != nil { | ||
return 0, fmt.Errorf("can't decode extensions: %v", err) | ||
return 0, 0, fmt.Errorf("can't decode extensions: %v", err) | ||
} | ||
extensions := cryptobyte.String(extensionBytes) | ||
var extensionType uint8 | ||
var extensionData cryptobyte.String | ||
var leafIdx int64 | ||
if !extensions.ReadUint8(&extensionType) { | ||
return 0, fmt.Errorf("can't read extension type") | ||
return 0, 0, fmt.Errorf("can't read extension type") | ||
} | ||
if extensionType != 0 { | ||
return 0, fmt.Errorf("wrong extension type %d, want 0", extensionType) | ||
return 0, 0, fmt.Errorf("wrong extension type %d, want 0", extensionType) | ||
} | ||
if !extensions.ReadUint16LengthPrefixed(&extensionData) { | ||
return 0, fmt.Errorf("can't read extension data") | ||
return 0, 0, fmt.Errorf("can't read extension data") | ||
} | ||
if !readUint40(&extensionData, &leafIdx) { | ||
return 0, fmt.Errorf("can't read leaf index from extension") | ||
return 0, 0, fmt.Errorf("can't read leaf index from extension") | ||
} | ||
if !extensionData.Empty() || | ||
!extensions.Empty() { | ||
return 0, fmt.Errorf("invalid data tile extensions: %v", resp.Extensions) | ||
return 0, 0, fmt.Errorf("invalid data tile extensions: %v", resp.Extensions) | ||
} | ||
return uint64(leafIdx), nil | ||
return uint64(leafIdx), resp.Timestamp, nil | ||
} | ||
|
||
// readUint40 decodes a big-endian, 40-bit value into out and advances over it. | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -31,27 +31,38 @@ type HammerOpts struct { | |
NumReadersRandom int | ||
NumReadersFull int | ||
NumWriters int | ||
NumMMDVerifiers int | ||
MMDDuration time.Duration | ||
} | ||
|
||
func NewHammer(tracker *client.LogStateTracker, f client.EntryBundleFetcherFunc, w LeafWriter, gen func() []byte, seqLeafChan chan<- LeafTime, errChan chan<- error, opts HammerOpts) *Hammer { | ||
readThrottle := NewThrottle(opts.MaxReadOpsPerSecond) | ||
writeThrottle := NewThrottle(opts.MaxWriteOpsPerSecond) | ||
|
||
var leafMMDChan chan LeafMMD | ||
if opts.NumMMDVerifiers > 0 { | ||
leafMMDChan = make(chan LeafMMD, opts.NumWriters*2) | ||
} | ||
|
||
randomReaders := NewWorkerPool(func() Worker { | ||
return NewLeafReader(tracker, f, RandomNextLeaf(), readThrottle.TokenChan, errChan) | ||
}) | ||
fullReaders := NewWorkerPool(func() Worker { | ||
return NewLeafReader(tracker, f, MonotonicallyIncreasingNextLeaf(), readThrottle.TokenChan, errChan) | ||
}) | ||
writers := NewWorkerPool(func() Worker { | ||
return NewLogWriter(w, gen, writeThrottle.TokenChan, errChan, seqLeafChan) | ||
return NewLogWriter(w, gen, writeThrottle.TokenChan, errChan, seqLeafChan, leafMMDChan) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In a followup PR, you could also add a leafMMDChan to
From |
||
}) | ||
mmdVerifiers := NewWorkerPool(func() Worker { | ||
return NewMMDVerifier(tracker, opts.MMDDuration, errChan, leafMMDChan) | ||
}) | ||
|
||
return &Hammer{ | ||
opts: opts, | ||
randomReaders: randomReaders, | ||
fullReaders: fullReaders, | ||
writers: writers, | ||
mmdVerifiers: mmdVerifiers, | ||
readThrottle: readThrottle, | ||
writeThrottle: writeThrottle, | ||
tracker: tracker, | ||
|
@@ -66,6 +77,7 @@ type Hammer struct { | |
randomReaders WorkerPool | ||
fullReaders WorkerPool | ||
writers WorkerPool | ||
mmdVerifiers WorkerPool | ||
readThrottle *Throttle | ||
writeThrottle *Throttle | ||
tracker *client.LogStateTracker | ||
|
@@ -82,6 +94,9 @@ func (h *Hammer) Run(ctx context.Context) { | |
for i := 0; i < h.opts.NumWriters; i++ { | ||
h.writers.Grow(ctx) | ||
} | ||
for i := 0; i < h.opts.NumMMDVerifiers; i++ { | ||
h.mmdVerifiers.Grow(ctx) | ||
} | ||
|
||
go h.readThrottle.Run(ctx) | ||
go h.writeThrottle.Run(ctx) | ||
|
Uh oh!
There was an error while loading. Please reload this page.