Skip to content
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

Witness properly handles ratcheting forward from tree size 0 #266

Merged
merged 3 commits into from
Sep 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions internal/witness/witness.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,20 @@ func (w *Witness) Update(ctx context.Context, logID string, nextRaw []byte, cPro
counterUpdateSuccess.Inc(logID)
return prevRaw, nil
}
if prev.Size == 0 {
// Checkpoints of size 0 are really placeholders and consistency proofs can't be performed.
// If we initialized on a tree size of 0, then we simply ratchet forward and effectively TOFU the new checkpoint.
signed, err := w.signChkpt(nextNote)
if err != nil {
return nil, status.Errorf(codes.Internal, "couldn't sign input checkpoint: %v", err)
}
if err := setInitChkptData(write, logInfo, next, signed, cProof); err != nil {
return nil, status.Errorf(codes.Internal, "couldn't set first non-zero checkpoint: %v", err)
}
counterUpdateSuccess.Inc(logID)
return signed, nil
}

// The only remaining option is next.Size > prev.Size. This might be
// valid so we use either plain consistency proofs or compact ranges to
// verify, depending on the log.
Expand Down
119 changes: 74 additions & 45 deletions internal/witness/witness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"testing"

_ "github.com/mattn/go-sqlite3" // Load drivers for sqlite3
"github.com/transparency-dev/formats/log"
f_note "github.com/transparency-dev/formats/note"
"github.com/transparency-dev/merkle/rfc6962"
"github.com/transparency-dev/witness/internal/persistence/inmemory"
Expand Down Expand Up @@ -259,47 +260,77 @@ func TestGetChkpt(t *testing.T) {
}
}

func mustCreateCheckpoint(t *testing.T, sk string, size uint64, rootHash []byte) []byte {
t.Helper()
cp := log.Checkpoint{
Origin: logOrigin,
Size: size,
Hash: rootHash,
}
signer, err := note.NewSigner(sk)
if err != nil {
t.Fatal(err)
}

msg, err := note.Sign(&note.Note{Text: string(cp.Marshal())}, signer)
if err != nil {
t.Fatal(err)
}
return msg
}

func TestUpdate(t *testing.T) {
for _, test := range []struct {
desc string
initC []byte
initSize uint64
newC []byte
pf [][]byte
useCR bool
initCR [][]byte
isGood bool
desc string
initC []byte
newC []byte
pf [][]byte
useCR bool
initCR [][]byte
isGood bool
}{
{
desc: "vanilla consistency happy path",
initC: mInit,
initSize: 5,
newC: mNext,
pf: consProof,
useCR: false,
isGood: true,
desc: "vanilla consistency happy path",
initC: mustCreateCheckpoint(t, mSK, 5, dh("e35b268c1522014ef412d2a54fa94838862d453631617b0307e5c77dcbeefc11", 32)),
newC: mNext,
pf: consProof,
useCR: false,
isGood: true,
},
{
desc: "vanilla path, but the first line changed",
initC: mInit,
initSize: 5,
newC: []byte("Frog Checkpoint v0\n8\nV8K9aklZ4EPB+RMOk1/8VsJUdFZR77GDtZUQq84vSbo=\n\n— monkeys 202ffgCVdfZmrroccRdQoEfn2TfmXHez4R++GvVrFvFiaI85O12aTV5GpNOvWsuQW77eNxQ2b7ggYeglzF/QSy/EBws=\n"),
pf: consProof,
useCR: false,
isGood: false,
desc: "vanilla consistency starting from tree size 0 with proof",
initC: mustCreateCheckpoint(t, mSK, 0, rfc6962.DefaultHasher.EmptyRoot()),
newC: mustCreateCheckpoint(t, mSK, 5, dh("e35b268c1522014ef412d2a54fa94838862d453631617b0307e5c77dcbeefc11", 32)),
pf: consProof,
useCR: false,
isGood: true,
},
{
desc: "vanilla consistency starting from tree size 0 without proof",
initC: mustCreateCheckpoint(t, mSK, 0, rfc6962.DefaultHasher.EmptyRoot()),
newC: mustCreateCheckpoint(t, mSK, 5, dh("e35b268c1522014ef412d2a54fa94838862d453631617b0307e5c77dcbeefc11", 32)),
pf: [][]byte{},
useCR: false,
isGood: true,
},
{
desc: "vanilla path, but the first line changed",
initC: mInit,
newC: []byte("Frog Checkpoint v0\n8\nV8K9aklZ4EPB+RMOk1/8VsJUdFZR77GDtZUQq84vSbo=\n\n— monkeys 202ffgCVdfZmrroccRdQoEfn2TfmXHez4R++GvVrFvFiaI85O12aTV5GpNOvWsuQW77eNxQ2b7ggYeglzF/QSy/EBws=\n"),
pf: consProof,
useCR: false,
isGood: false,
}, {
desc: "vanilla consistency smaller checkpoint",
initC: mNext,
initSize: 8,
newC: mInit,
pf: consProof,
useCR: false,
isGood: false,
desc: "vanilla consistency smaller checkpoint",
initC: mNext,
newC: mInit,
pf: consProof,
useCR: false,
isGood: false,
}, {
desc: "vanilla consistency garbage proof",
initC: mInit,
initSize: 5,
newC: mNext,
desc: "vanilla consistency garbage proof",
initC: mInit,
newC: mNext,
pf: [][]byte{
dh("aaaa", 2),
dh("bbbb", 2),
Expand All @@ -308,19 +339,17 @@ func TestUpdate(t *testing.T) {
},
isGood: false,
}, {
desc: "compact range happy path",
initC: crInit,
initSize: 10,
newC: crNext,
pf: crProof,
useCR: true,
initCR: crInitRange,
isGood: true,
desc: "compact range happy path",
initC: crInit,
newC: crNext,
pf: crProof,
useCR: true,
initCR: crInitRange,
isGood: true,
}, {
desc: "compact range garbage proof",
initC: crInit,
initSize: 10,
newC: crNext,
desc: "compact range garbage proof",
initC: crInit,
newC: crNext,
pf: [][]byte{
dh("aaaa", 2),
dh("bbbb", 2),
Expand Down
Loading