FuzzStrictJSON, the 28th target of scripts/fuzz.sh: on every input the
strict reader accepts, encoding/json must accept it too and read the same
names in the outer object, the same text of each value, the same strings
and the same round. Its seeds are the 38 inputs of drand's JSON in
release.json. Sixty seconds on four workers found no disagreement. It
comes after the tag spec-v0.16 and changes no rule.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@ -80,7 +80,7 @@ moves the next two subsections of §79 one place. A case of §64 that is not in
| 45 | Release API: its answer is the release object of §47.1 (v0.15), and its user a network source; the HTTP form is informative | the object: `provider.EncodeRelease`, `provider.DecodeRelease`; the API itself is out of scope (server, plan §2) | `provider.TestReleaseVectors` |
| 46 | Release Queue | out of scope (server) | — |
| 47 | Release Cache | every release is verified again: `capsule.Open` step 10 and `agewrap.TimeIdentity`; `release_material` is the release object (v0.15): `provider.EncodeRelease` | mutations *release of another round* |
| 47.1 | Release object (v0.15): deterministic CBOR without a frame, `{0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}`; size from 1 to 1024 bytes, then type and version, then schema (`ERR_NON_CANONICAL_CBOR`, a version other than 1 `ERR_UNSUPPORTED_VERSION`); the chain hash, the round and the signature at step 10; drand's JSON accepted as the input of the caller, `ERR_RELEASE_INVALID` when unreadable, and since v0.16 read strictly: no repeated name, names compared exactly once their escapes are decoded, no lone surrogate, round an integer from 1 to 2^53 − 1; a Release Cache and the Release API keep and serve the object | `provider/release.go`: `EncodeRelease`, `DecodeRelease` (`releaseWire` with `codec.CheckSchema` and `codec.Unmarshal`), `ParseRelease`, `ParseDrandJSON`, `NewReleaseObject`, `MaxReleaseObjectSize`, `MaxReleaseJSONSize`; `provider/drandjson.go` (`strictJSON`, `jsonRound`), which `provider/drand` uses for the answers of the relays too; `cmd/datekeys``releaseInHand` | `provider.TestReleaseVectors`, `TestEncodeRelease`, `FuzzDecodeRelease`, `TestStrictJSON`, `TestJSONRound`; `testdata/vectors/release.json`, `testdata/releases/<round>.cbor`; `internal/testkit.TestVectorFilesAreCurrent` |
| 47.1 | Release object (v0.15): deterministic CBOR without a frame, `{0: "datekeys-release", 1: 1, 2: chain_hash, 3: round, 4: signature}`; size from 1 to 1024 bytes, then type and version, then schema (`ERR_NON_CANONICAL_CBOR`, a version other than 1 `ERR_UNSUPPORTED_VERSION`); the chain hash, the round and the signature at step 10; drand's JSON accepted as the input of the caller, `ERR_RELEASE_INVALID` when unreadable, and since v0.16 read strictly: no repeated name, names compared exactly once their escapes are decoded, no lone surrogate, round an integer from 1 to 2^53 − 1; a Release Cache and the Release API keep and serve the object | `provider/release.go`: `EncodeRelease`, `DecodeRelease` (`releaseWire` with `codec.CheckSchema` and `codec.Unmarshal`), `ParseRelease`, `ParseDrandJSON`, `NewReleaseObject`, `MaxReleaseObjectSize`, `MaxReleaseJSONSize`; `provider/drandjson.go` (`strictJSON`, `jsonRound`), which `provider/drand` uses for the answers of the relays too; `cmd/datekeys``releaseInHand` | `provider.TestReleaseVectors`, `TestEncodeRelease`, `FuzzDecodeRelease`, `TestStrictJSON`, `TestJSONRound`, `FuzzStrictJSON` (against `encoding/json`); `testdata/vectors/release.json`, `testdata/releases/<round>.cbor`; `internal/testkit.TestVectorFilesAreCurrent` |
| 48 | Multi-relay; a network source verifies every response with the rules of §63 step 10 and discards the invalid ones: none valid is `ERR_RELEASE_UNAVAILABLE` at step 9, and so is any other failure of a source, with no other code | `provider/drand.Client` (race, first *verified* release wins; the failure of each relay kept as text only, a context that ended detectable with `errors.Is`), the `provider.ReleaseSource` contract, `capsule.Open` (step 9 keeps only the text of a source error with another code or none) | `drand.TestRaceWaitsForAValidSignature`, `TestRejectMalformedRelayResponses`, `TestFetchErrorHasOneCode`, `TestUnavailabilityAndCancellation`; `capsule.TestReleaseFromANetworkSource`, `TestReleaseSourceErrorsAtStep9` |
| 49 | Direct recovery from the provider | `provider/drand`; v0.15: a release in hand, `provider.Supplier` (`provider.Encoded`, `provider.Archive`) in `capsule.OpenOptions.Release`; `datekeys decrypt -release` | `drand.TestLiveRelays`, `capsule.TestLiveLifecycle` (`-tags integration`); `capsule.TestReleaseInHand`, `TestReleaseInHandErrors`; `cmd/datekeys.TestDecryptWithReleaseInHand` |
| 50 | Historical release dependency; v0.15: long-term recovery on archives of all rounds and on cache services that serve them, with no hosting promise, the release archive as an informative format | documented in `README.md`; `provider.Archive`, `provider.EncodeArchiveHeader`; `datekeys decrypt -release` with a local archive | `provider.TestArchive`; `cmd/datekeys.TestDecryptWithReleaseInHand` (*a local archive*); the `archive` block of `testdata/vectors/release.json`, `testdata/releases/archive_1000_1004.bin` |