Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
package provider_test
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"errors"
|
|
|
|
|
"os"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
datekeys "g.activething.com/go/DateKeys"
|
|
|
|
|
"g.activething.com/go/DateKeys/internal/testkit"
|
|
|
|
|
"g.activething.com/go/DateKeys/profile"
|
|
|
|
|
"g.activething.com/go/DateKeys/provider"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Spec v0.15, §47.1: every vector of release.json, replayed from the file
|
|
|
|
|
// alone, gets its result and its text: decoding, then step 10 against the
|
|
|
|
|
// pinned profile and the round of the vector.
|
|
|
|
|
func TestReleaseVectors(t *testing.T) {
|
|
|
|
|
var f testkit.ReleaseVectorFile
|
|
|
|
|
if err := testkit.ReadJSON("../testdata/vectors/release.json", &f); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
check := func(t *testing.T, in []byte, v testkit.ReleaseVector) {
|
|
|
|
|
r, err := provider.ParseRelease(in)
|
|
|
|
|
if err == nil {
|
|
|
|
|
err = provider.Verify(p, provider.Condition{Round: v.Round}, r)
|
|
|
|
|
}
|
|
|
|
|
if got := testkit.Result(err); got != v.Result {
|
|
|
|
|
t.Fatalf("%s, want %s (%v)", got, v.Result, err)
|
|
|
|
|
}
|
|
|
|
|
if err != nil && err.Error() != v.Text {
|
|
|
|
|
t.Fatalf("text %q, want %q", err.Error(), v.Text)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, v := range f.Objects {
|
|
|
|
|
t.Run(v.Name, func(t *testing.T) {
|
|
|
|
|
b, err := hex.DecodeString(v.Encoding)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
check(t, b, v)
|
|
|
|
|
if v.Result == testkit.ResultOK {
|
|
|
|
|
// A valid object is the deterministic encoding of its value.
|
|
|
|
|
r, _ := provider.DecodeRelease(b)
|
|
|
|
|
if again, err := provider.EncodeRelease(r); err != nil || !bytes.Equal(again, b) {
|
|
|
|
|
t.Fatalf("encoding again: %x, %v", again, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
for _, v := range f.JSON {
|
|
|
|
|
t.Run("JSON "+v.Name, func(t *testing.T) { check(t, []byte(v.Input), v) })
|
|
|
|
|
}
|
|
|
|
|
if len(f.Objects) < 30 || len(f.JSON) < 10 {
|
|
|
|
|
t.Fatalf("%d objects and %d JSON inputs", len(f.Objects), len(f.JSON))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
// The local archive of testdata supplies the release object of each round it
|
|
|
|
|
// holds, and nothing for a round it lacks or outside it, for another chain,
|
|
|
|
|
// or when its length is not the one its header announces.
|
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
func TestArchive(t *testing.T) {
|
|
|
|
|
b, err := os.ReadFile("../testdata/releases/" + testkit.ArchiveFile)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
if !provider.IsArchive(b) || provider.IsArchive(mustRead(t, "../testdata/releases/1000.cbor")) {
|
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
t.Fatal("IsArchive")
|
|
|
|
|
}
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
a := provider.NewArchive(bytes.NewReader(b), int64(len(b)))
|
|
|
|
|
for _, round := range []uint64{1000, 1001, 1004} {
|
|
|
|
|
got, err := a.Supply(p, provider.Condition{Round: round})
|
|
|
|
|
if err != nil || !bytes.Equal(got, mustRead(t, "../testdata/releases/"+testkit.ReleaseFileName(round))) {
|
|
|
|
|
t.Fatalf("round %d: %x, %v", round, got, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for _, round := range []uint64{999, 1002, 1003, 1005, 2000} {
|
|
|
|
|
if _, err := a.Supply(p, provider.Condition{Round: round}); !onlyUnavailable(err) {
|
|
|
|
|
t.Fatalf("round %d: %v", round, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
other := p.Clone()
|
|
|
|
|
other.ChainHash[0] ^= 1
|
|
|
|
|
short := provider.NewArchive(bytes.NewReader(b[:len(b)-1]), int64(len(b)-1))
|
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
notArchive := mustRead(t, "../testdata/releases/1000.cbor")
|
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
for name, c := range map[string]struct {
|
|
|
|
|
a *provider.Archive
|
|
|
|
|
p *profile.Profile
|
|
|
|
|
}{
|
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
"another chain": {a, other},
|
|
|
|
|
"a byte missing": {short, p},
|
|
|
|
|
"a release object": {provider.NewArchive(bytes.NewReader(notArchive), int64(len(notArchive))), p},
|
|
|
|
|
"no byte": {provider.NewArchive(bytes.NewReader(nil), 0), p},
|
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
} {
|
|
|
|
|
if _, err := c.a.Supply(c.p, provider.Condition{Round: 1000}); !onlyUnavailable(err) {
|
|
|
|
|
t.Fatalf("%s: %v", name, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// onlyUnavailable reports whether err wraps ErrReleaseUnavailable and no
|
|
|
|
|
// other normative code.
|
|
|
|
|
func onlyUnavailable(err error) bool {
|
|
|
|
|
for _, e := range datekeys.All() {
|
|
|
|
|
if errors.Is(err, e) != (e == datekeys.ErrReleaseUnavailable) {
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mustRead(t *testing.T, path string) []byte {
|
|
|
|
|
t.Helper()
|
|
|
|
|
b, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// EncodeRelease never writes an object that DecodeRelease rejects, and a
|
|
|
|
|
// release of the profile carries its chain hash.
|
|
|
|
|
func TestEncodeRelease(t *testing.T) {
|
|
|
|
|
p := profile.Quicknet()
|
|
|
|
|
r := testkit.Release(1000)
|
|
|
|
|
for _, bad := range []provider.Release{
|
|
|
|
|
r,
|
|
|
|
|
{Round: 0, Signature: r.Signature, ChainHash: p.ChainHash[:]},
|
|
|
|
|
{Round: 1 << 53, Signature: r.Signature, ChainHash: p.ChainHash[:]},
|
|
|
|
|
{Round: 1000, ChainHash: p.ChainHash[:]},
|
|
|
|
|
{Round: 1000, Signature: make([]byte, 97), ChainHash: p.ChainHash[:]},
|
|
|
|
|
} {
|
|
|
|
|
if _, err := provider.EncodeRelease(bad); !errors.Is(err, datekeys.ErrNonCanonicalCBOR) {
|
|
|
|
|
t.Fatalf("%+v: %v", bad, err)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
b, err := provider.NewReleaseObject(p, r)
|
|
|
|
|
if err != nil || len(b) != 111 {
|
|
|
|
|
t.Fatalf("%d bytes, %v", len(b), err)
|
|
|
|
|
}
|
|
|
|
|
got, err := provider.DecodeRelease(b)
|
|
|
|
|
if err != nil || got.Round != 1000 || !bytes.Equal(got.Signature, r.Signature) || !bytes.Equal(got.ChainHash, p.ChainHash[:]) {
|
|
|
|
|
t.Fatalf("%+v, %v", got, err)
|
|
|
|
|
}
|
|
|
|
|
if _, err := provider.EncodeArchiveHeader(p.ChainHash[:], 0, 1); err == nil {
|
|
|
|
|
t.Fatal("archive from round 0")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// FuzzDecodeRelease: whatever the input, DecodeRelease returns exactly one
|
|
|
|
|
// normative code, and what it accepts encodes again to the same bytes.
|
|
|
|
|
func FuzzDecodeRelease(f *testing.F) {
|
Spec v0.15 draft: remove the .dkr file
The author's decision of 7 October 2026. A release saved next to a capsule
cannot exist when the capsule is made, and once the date comes the capsule
can be opened: such a file only opens it again and does not cover the real
case, someone opening it decades later when drand is gone and nobody saved
anything. Long-term recovery rests instead on archives and cache services
that keep the releases of all rounds; a reader asks for its round and
verifies the signature against the pinned key.
Spec: the .dkr extension (section 20, back to v0.14), sections 1, 4, 8, 45,
47.1, 49, 50 (rewritten), 53, 62.1 (rule 28 removed, rule 26 reworded),
63, 70, 73, 74 (the datekeys.release .dkk extension dropped too), 76
(the v0.15 block, with the discarded design) and the annex 79. The
release object, the chain hash at step 10, step 9.c option B and the
archive format stay.
Code: decrypt -save-release and the command datekeys release are gone,
with writeRelease and their tests; decrypt -release FILE stays. The
release objects of testdata/releases are now <round>.cbor, and
TestVectorFilesAreCurrent fails on a file the generator no longer writes.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
f.Add(mustReadF(f, "../testdata/releases/1000.cbor"))
|
Release object, release in hand and step 9.c option B (spec v0.15 draft)
The release of a round becomes a file, .dkr: a release object in
deterministic CBOR, {0: "datekeys-release", 1: 1, 2: chain_hash, 3: round,
4: signature}, which provider.EncodeRelease writes and DecodeRelease reads
with its layers (size, type and version, schema). provider.ParseRelease
also reads drand's JSON as the input of the caller. Verify checks the chain
hash a release names before its round and its signature, with
ERR_PROFILE_MISMATCH. provider.Archive reads a local release archive, the
informative format of the draft.
capsule.OpenOptions.Release takes a release in hand, a provider.Supplier,
exclusive with Source: Open does not compare it with the clock (step 9.c,
option B) and reports a clock behind it in Opened.ClockBehind; a network
source is still never asked before the round time. The CLI gains
decrypt -release FILE (.dkr, drand's JSON or a local archive),
decrypt -save-release FILE.dkr and the command release, which fetches,
verifies and saves the .dkr without opening the capsule.
Test data: vectors/release.json, releases/<round>.dkr for rounds 1000,
1001, 1004 and 2000, and a local archive of rounds 1000 to 1004. In
mutations.json every case says its source, "supplied" or "network"; the
case "round not reached yet", a release in hand, now opens, and four cases
are added: the same with a network source, a release of another round from
a network source, and two release objects of another chain. SpecVersion
stays 0.14 until the author approves the draft.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2 days ago
|
|
|
f.Add([]byte(`{"round":1000,"signature":"00"}`))
|
|
|
|
|
f.Fuzz(func(t *testing.T, b []byte) {
|
|
|
|
|
r, err := provider.ParseRelease(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
n := 0
|
|
|
|
|
for _, e := range datekeys.All() {
|
|
|
|
|
if errors.Is(err, e) {
|
|
|
|
|
n++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if n != 1 {
|
|
|
|
|
t.Fatalf("%d codes: %v", n, err)
|
|
|
|
|
}
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if r.ChainHash == nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
if again, err := provider.EncodeRelease(r); err != nil || !bytes.Equal(again, b) {
|
|
|
|
|
t.Fatalf("encoding again: %x, %v", again, err)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func mustReadF(f *testing.F, path string) []byte {
|
|
|
|
|
b, err := os.ReadFile(path)
|
|
|
|
|
if err != nil {
|
|
|
|
|
f.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
return b
|
|
|
|
|
}
|