diff --git a/witness/http.go b/witness/http.go index 2b8fd356..1c6519ae 100644 --- a/witness/http.go +++ b/witness/http.go @@ -86,7 +86,7 @@ func (a *HTTPHandler) handleUpdate(ctx context.Context, oldSize uint64, newCP [] case errors.Is(updateErr, ErrInvalidProof): return http.StatusUnprocessableEntity, nil, "", nil case errors.Is(updateErr, ErrRootMismatch): - return http.StatusConflict, nil, "", nil + return http.StatusUnprocessableEntity, nil, "", nil case errors.Is(updateErr, ErrPushback): return http.StatusTooManyRequests, nil, "", nil default: diff --git a/witness/http_test.go b/witness/http_test.go index f9186f38..2554058e 100644 --- a/witness/http_test.go +++ b/witness/http_test.go @@ -123,7 +123,7 @@ func TestHandler(t *testing.T) { }, { name: "ErrRootMismatch", witness: &testWitness{updateErr: ErrRootMismatch}, - wantStatus: http.StatusConflict, + wantStatus: http.StatusUnprocessableEntity, }, { name: "ErrPushback", witness: &testWitness{updateErr: ErrPushback}, diff --git a/witness/witness.go b/witness/witness.go index f31d4e35..2f73d1c6 100644 --- a/witness/witness.go +++ b/witness/witness.go @@ -155,6 +155,13 @@ func (w *Witness) Update(ctx context.Context, oldSize uint64, nextRaw []byte, cP return nil, 0, err } + // SPEC: A checkpoint of size zero MUST have the root hash of the empty tree, which + // RFC 6962, Section 2.1 defines as the hash of the empty string. Otherwise, + // the witness MUST respond with a "422 Unprocessable Entity" HTTP status code. + if next.Size == 0 && !bytes.Equal(next.Hash, rfc6962.DefaultHasher.EmptyRoot()) { + return nil, 0, ErrRootMismatch + } + counterUpdateAttempt.Add(ctx, 1, metric.WithAttributes(originKey.String(origin))) var retSigs []byte diff --git a/witness/witness_test.go b/witness/witness_test.go index 16ca4566..40456fb2 100644 --- a/witness/witness_test.go +++ b/witness/witness_test.go @@ -220,6 +220,20 @@ func TestUpdate(t *testing.T) { newC: mNext, pf: consProof, isGood: true, + }, { + desc: "valid zero size hash", + origin: "monkeys", + initC: mustCreateCheckpoint(t, mSK, "monkeys", 0, rfc6962.DefaultHasher.EmptyRoot()), + oldSize: 0, + newC: mustCreateCheckpoint(t, mSK, "monkeys", 0, rfc6962.DefaultHasher.EmptyRoot()), + isGood: true, + }, { + desc: "invalid zero size hash", + origin: "monkeys", + initC: mustCreateCheckpoint(t, mSK, "monkeys", 0, rfc6962.DefaultHasher.EmptyRoot()), + oldSize: 0, + newC: mustCreateCheckpoint(t, mSK, "monkeys", 0, dh("e35b268c1522014ef412d2a54fa94838862d453631617b0307e5c77dcbeefc11", 32)), + wantError: ErrRootMismatch, }, { desc: "oldSize doesn't match current state", origin: "monkeys",