diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cea6c85..c18f23c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -104,9 +104,11 @@ jobs: - name: The checks must pass against freshly generated vectors run: cargo run -p kt-interop --bin kt-interop-report -- --vectors interop/vectors --out "$RUNNER_TEMP/fresh" - # search.json and monitor.json are excluded because they cannot be reproducible: both - # are served by a live log that stamps wall-clock times and draws random commitment - # openings. See interop/README.md. Every other file must regenerate to the same bytes, + # Four files are excluded because they cannot be reproducible. search.json, monitor.json + # and update.json come from a live log that stamps wall-clock times and draws random + # commitment openings; tree-head-p256.json carries ECDSA signatures, and Go's crypto/ecdsa + # draws a nonce per signature. See interop/README.md. Every other file must regenerate to + # the same bytes, # so an upstream bump that changes behaviour shows up as a diff rather than a silent # pass — and the two exceptions are covered by the fresh-generation run above, which # is a stronger check for them than a diff would be. @@ -114,4 +116,6 @@ jobs: run: | git diff --exit-code -- interop/vectors \ ':(exclude)interop/vectors/search.json' \ - ':(exclude)interop/vectors/monitor.json' + ':(exclude)interop/vectors/monitor.json' \ + ':(exclude)interop/vectors/update.json' \ + ':(exclude)interop/vectors/tree-head-p256.json' diff --git a/Cargo.lock b/Cargo.lock index b69bd46..95e72c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,18 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base16ct" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6" + [[package]] name = "base64ct" version = "1.8.3" @@ -35,6 +47,12 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" +[[package]] +name = "cpubits" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae" + [[package]] name = "cpufeatures" version = "0.3.0" @@ -44,6 +62,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crypto-bigint" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271" +dependencies = [ + "cpubits", + "ctutils", + "hybrid-array", + "num-traits", + "rand_core", + "subtle", + "zeroize", +] + [[package]] name = "crypto-common" version = "0.2.2" @@ -51,6 +84,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", + "rand_core", ] [[package]] @@ -60,6 +94,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e" dependencies = [ "cmov", + "subtle", ] [[package]] @@ -106,10 +141,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer", + "const-oid", "crypto-common", "ctutils", ] +[[package]] +name = "ecdsa" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0" +dependencies = [ + "der", + "digest", + "elliptic-curve", + "rfc6979", + "signature", + "spki", + "zeroize", +] + [[package]] name = "ed25519" version = "3.0.0" @@ -135,12 +186,53 @@ dependencies = [ "zeroize", ] +[[package]] +name = "elliptic-curve" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65" +dependencies = [ + "base16ct", + "crypto-bigint", + "crypto-common", + "digest", + "ff", + "group", + "hybrid-array", + "pkcs8", + "rand_core", + "sec1", + "subtle", + "zeroize", +] + +[[package]] +name = "ff" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f" +dependencies = [ + "rand_core", + "subtle", +] + [[package]] name = "fiat-crypto" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64cd1e32ddd350061ae6edb1b082d7c54915b5c672c389143b9a63403a109f24" +[[package]] +name = "group" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4" +dependencies = [ + "ff", + "rand_core", + "subtle", +] + [[package]] name = "hex" version = "0.4.3" @@ -162,7 +254,9 @@ version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" dependencies = [ + "subtle", "typenum", + "zeroize", ] [[package]] @@ -188,6 +282,7 @@ dependencies = [ "ed25519-dalek", "hmac", "kt-wire", + "p256", "sha2", ] @@ -227,6 +322,28 @@ version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "p256" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primefield", + "primeorder", + "sha2", +] + [[package]] name = "pkcs8" version = "0.11.0" @@ -237,6 +354,32 @@ dependencies = [ "spki", ] +[[package]] +name = "primefield" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4" +dependencies = [ + "crypto-bigint", + "crypto-common", + "ff", + "rand_core", + "subtle", + "zeroize", +] + +[[package]] +name = "primeorder" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06" +dependencies = [ + "elliptic-curve", + "primefield", + "serdect", + "wnaf", +] + [[package]] name = "proc-macro2" version = "1.0.107" @@ -255,6 +398,22 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rfc6979" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b" +dependencies = [ + "crypto-bigint", + "hmac", +] + [[package]] name = "rustc_version" version = "0.4.1" @@ -264,6 +423,20 @@ dependencies = [ "semver", ] +[[package]] +name = "sec1" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d" +dependencies = [ + "base16ct", + "ctutils", + "der", + "hybrid-array", + "subtle", + "zeroize", +] + [[package]] name = "semver" version = "1.0.28" @@ -313,6 +486,16 @@ dependencies = [ "zmij", ] +[[package]] +name = "serdect" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e" +dependencies = [ + "base16ct", + "serde", +] + [[package]] name = "sha2" version = "0.11.0" @@ -329,6 +512,10 @@ name = "signature" version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5" +dependencies = [ + "digest", + "rand_core", +] [[package]] name = "spki" @@ -380,6 +567,17 @@ version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" +[[package]] +name = "wnaf" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1" +dependencies = [ + "ff", + "group", + "hybrid-array", +] + [[package]] name = "zeroize" version = "1.9.0" diff --git a/crates/kt-crypto/Cargo.toml b/crates/kt-crypto/Cargo.toml index 736609c..6d74da5 100644 --- a/crates/kt-crypto/Cargo.toml +++ b/crates/kt-crypto/Cargo.toml @@ -13,6 +13,7 @@ curve25519-dalek = { version = "5.0.0", default-features = false, features = ["a ed25519-dalek = { version = "3.0.0", default-features = false, features = ["alloc"] } hmac = { version = "0.13.0", default-features = false } kt-wire.workspace = true +p256 = { version = "0.14.0", default-features = false, features = ["arithmetic", "alloc", "ecdsa"] } sha2 = { version = "0.11.0", default-features = false } [lints] diff --git a/crates/kt-crypto/src/signature.rs b/crates/kt-crypto/src/signature.rs index 81c3e93..ad40a1b 100644 --- a/crates/kt-crypto/src/signature.rs +++ b/crates/kt-crypto/src/signature.rs @@ -16,11 +16,18 @@ //! disagreement about how a `Configuration` encodes breaks every signature; see //! [`kt_wire::heads`] for one such disagreement between the two Go peers. //! -//! # Scope +//! # Both registered suites //! -//! Ed25519 (RFC 8032) for `KT_128_SHA256_Ed25519`, which is the suite this -//! workspace targets. ECDSA/P-256 for the other registered suite is out of scope -//! and returns [`Error::UnsupportedSuite`] rather than guessing. +//! Ed25519 (RFC 8032) for `KT_128_SHA256_Ed25519`, and ECDSA/P-256 over SHA-256 for +//! `KT_128_SHA256_P256`. §17.1 fixes the latter's encoding as "the concatenation of +//! two 256-bit big endian integers r and s", so 64 fixed-width bytes rather than the +//! ASN.1 sequence ECDSA usually travels in. +//! +//! The two suites also differ in how the *key* is encoded, which §11.2 does not say: +//! the peer emits an Ed25519 key as its 32 raw bytes and a P-256 signature key +//! uncompressed (65 bytes, SEC1 tag `0x04`), while the same `Configuration` carries a +//! P-256 *VRF* key compressed (33 bytes). Rather than fix a length, the P-256 path +//! accepts whatever SEC1 admits; `tree-head.json` pins what the peer sends. use alloc::vec::Vec; @@ -135,9 +142,30 @@ pub fn verify_raw( signature: &[u8], ) -> Result<()> { match suite { - // ECDSA/P-256, whose signatures are "the concatenation of two 256-bit big - // endian integers r and s" (§17.1). Out of scope for this workspace. - CipherSuite::Kt128Sha256P256 => Err(Error::UnsupportedSuite { suite }), + // ECDSA/P-256 over SHA-256, whose signatures are "the concatenation of two + // 256-bit big endian integers r and s" (§17.1) — so 64 fixed-width bytes, + // not the ASN.1 sequence ECDSA is usually seen in. + // + // Note the two key encodings this suite uses. The *signature* key arrives + // uncompressed (65 bytes, SEC1 tag 0x04), because that is what the peer's + // `ParseSigningPublicKey` accepts and what its `Bytes()` emits; the *VRF* + // key arrives compressed (33 bytes). Both live in the same `Configuration`, + // as `opaque signature_public_key<0..2^16-1>` and + // `opaque vrf_public_key<0..2^16-1>`, and §11.2 says nothing about either + // encoding — so this accepts whatever SEC1 admits rather than fixing a + // length, and `tree-head.json` pins what the peer actually sends. + CipherSuite::Kt128Sha256P256 => { + use p256::ecdsa::signature::Verifier as _; + + let key = p256::ecdsa::VerifyingKey::from_sec1_bytes(public_key) + .map_err(|_| Error::MalformedPublicKey)?; + let signature = p256::ecdsa::Signature::from_slice(signature) + .map_err(|_| Error::MalformedSignature)?; + // `Verifier` hashes with the curve's associated digest, SHA-256, which is + // also the suite's hash — unlike the VRF, where the two differ. + key.verify(message, &signature) + .map_err(|_| Error::BadSignature) + } CipherSuite::Kt128Sha256Ed25519 => { let key_bytes = <[u8; PUBLIC_KEY_SIZE]>::try_from(public_key) .map_err(|_| Error::MalformedPublicKey)?; @@ -491,12 +519,11 @@ mod tests { Err(Error::UnknownCipherSuite { value: 0xf00d }) ); - // P-256 is out of scope and says so rather than trying Ed25519 on it. + // An Ed25519 key and signature offered to the P-256 path: 32 bytes is not a SEC1 + // point, so it stops at the key rather than being fed to the wrong curve. assert_eq!( verify_raw(CipherSuite::Kt128Sha256P256, &public, b"m", &head.signature), - Err(Error::UnsupportedSuite { - suite: CipherSuite::Kt128Sha256P256 - }) + Err(Error::MalformedPublicKey) ); } diff --git a/crates/kt-crypto/src/vrf.rs b/crates/kt-crypto/src/vrf/edwards25519.rs similarity index 85% rename from crates/kt-crypto/src/vrf.rs rename to crates/kt-crypto/src/vrf/edwards25519.rs index da4a4a8..8a5714c 100644 --- a/crates/kt-crypto/src/vrf.rs +++ b/crates/kt-crypto/src/vrf/edwards25519.rs @@ -1,41 +1,26 @@ -//! Verifiable Random Function (`draft-ietf-keytrans-protocol-05` §11.7). -//! -//! Each label-version pair's search key in the prefix tree is the VRF output over -//! a [`VrfInput`] (§11.7). That is what keeps labels private: the tree is indexed -//! by a value only the log can compute, so a user who is handed a search key -//! learns nothing about the label behind it, and — because the VRF is *verifiable* -//! — the log cannot use a different key for the same label than the one it -//! proves. -//! -//! # What this implements -//! -//! `ECVRF-EDWARDS25519-SHA512-TAI` from [RFC 9381], which §17.1 selects for +//! `ECVRF-EDWARDS25519-SHA512-TAI` (RFC 9381 §5.5), which §17.1 selects for //! `KT_128_SHA256_Ed25519`, **with the output truncated to 32 bytes** as §17.1 -//! requires. The `KT_128_SHA256_P256` suite's `ECVRF-P256-SHA256-TAI` is not -//! implemented yet; [`Error::UnsupportedSuite`] says so rather than quietly using -//! the wrong curve. +//! requires. //! //! Note the two hash functions in play. The cipher suite's hash is SHA-256 and is -//! what [`crate::hash`] provides; the VRF's hash is **SHA-512**, fixed by the -//! ECVRF ciphersuite, and is used only inside this module. They are not the same -//! parameter and conflating them produces a VRF that verifies against itself and -//! nothing else. +//! what [`crate::hash`] provides; this VRF's hash is **SHA-512**, fixed by the +//! ECVRF ciphersuite, and is used only here. They are not the same parameter, and +//! conflating them produces a VRF that verifies against itself and nothing else. //! //! # Byte-order trap //! //! For the edwards25519 ciphersuites, RFC 9381's `int_to_string` and //! `string_to_int` are **little-endian**, following RFC 8032 — unlike the -//! big-endian integers everywhere else in the KT wire format. `c` and `s` in a -//! proof are little-endian, and so is the challenge read out of a hash. The RFC's -//! own test vectors are what adjudicate this, and they are in the tests below. +//! big-endian integers everywhere else in the KT wire format, and unlike +//! [`super::p256`], where they are big-endian. `c` and `s` in a proof are +//! little-endian, and so is the challenge read out of a hash. The RFC's own test +//! vectors are what adjudicate this, and they are in the tests below. //! //! # Oracles //! //! Three, in increasing distance from this code: RFC 9381's Appendix B vectors //! (implementation-independent, and the ones that matter most), the peer's //! `crypto/vrf/edwards25519`, and `interop/vectors/vrf.json`. -//! -//! [RFC 9381]: https://www.rfc-editor.org/rfc/rfc9381.html use alloc::vec::Vec; use core::fmt; @@ -50,6 +35,8 @@ use kt_wire::structs::{HashValue, VrfInput}; use crate::suite::CipherSuite; +use super::{Error, OUTPUT_SIZE, Output, Result}; + /// `suite_string` for `ECVRF-EDWARDS25519-SHA512-TAI` (RFC 9381 §5.5). const SUITE_STRING: u8 = 0x03; @@ -69,118 +56,12 @@ const PT_LEN: usize = 32; /// `VRF.Np` for `KT_128_SHA256_Ed25519`: the proof size in bytes (§17.1). pub const PROOF_SIZE: usize = PT_LEN + C_LEN + Q_LEN; -/// `VRF.Nh` for both registered suites: the output length in bytes (§17.1). -/// -/// RFC 9381's `beta_string` for this ciphersuite is 64 bytes; §17.1 specifies -/// "with the output truncated to 32 bytes", so that is what a search key is. -pub const OUTPUT_SIZE: usize = 32; - /// A secret key's length in bytes (RFC 8032 §5.1.5). pub const SECRET_KEY_SIZE: usize = 32; /// A public key's length in bytes. pub const PUBLIC_KEY_SIZE: usize = 32; -/// Something wrong with a VRF key, proof, or the suite asked for. -#[derive(Clone, Debug, PartialEq, Eq)] -#[non_exhaustive] -pub enum Error { - /// The cipher suite's VRF is not implemented here. - UnsupportedSuite { - /// The suite that was asked for. - suite: CipherSuite, - }, - /// A public key was not a valid compressed Edwards point. - MalformedPublicKey, - /// A public key was of small order, so it commits to nothing. - /// - /// RFC 9381 §5.4.5 `ECVRF_validate_key`. Checked here because in this protocol - /// the VRF public key arrives in a `Configuration` from the log, and a - /// small-order key would let it produce the same output for every label. - SmallOrderPublicKey, - /// A proof was not `VRF.Np` bytes. - ProofLength { - /// `VRF.Np`. - expected: usize, - /// What was supplied. - actual: usize, - }, - /// A proof's `Gamma` was not a valid compressed Edwards point. - MalformedGamma, - /// A proof's `s` was not a canonical scalar, i.e. `s >= q`. - /// - /// RFC 9381 §5.4.4 step 7 requires rejecting these. Accepting them would make - /// proofs malleable: several byte strings would verify for one signature. - NonCanonicalScalar, - /// The proof did not verify: the recomputed challenge differed. - BadProof, - /// A `VrfInput` could not be encoded, e.g. a label above the `2^8-1` ceiling. - Wire(codec::Error), -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::UnsupportedSuite { suite } => { - write!(f, "the VRF for {suite} is not implemented") - } - Self::MalformedPublicKey => f.write_str("VRF public key is not a valid point"), - Self::SmallOrderPublicKey => f.write_str("VRF public key is of small order"), - Self::ProofLength { expected, actual } => { - write!(f, "VRF proof must be {expected} bytes, got {actual}") - } - Self::MalformedGamma => f.write_str("VRF proof's Gamma is not a valid point"), - Self::NonCanonicalScalar => f.write_str("VRF proof's s is not a canonical scalar"), - Self::BadProof => f.write_str("VRF proof does not verify"), - Self::Wire(err) => write!(f, "encoding the VRF input: {err}"), - } - } -} - -impl core::error::Error for Error { - fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { - match self { - // The wrapping variant has to be walkable, like the one in - // `crate::Error` and `kt_tree::log::Error`: a caller that wants to know - // *which* field of a VrfInput was too long should not have to parse the - // rendered message to find out. - Self::Wire(err) => Some(err), - _ => None, - } - } -} - -impl From for Error { - fn from(err: codec::Error) -> Self { - Self::Wire(err) - } -} - -/// A specialized [`Result`] for VRF operations. -pub type Result = core::result::Result; - -/// A VRF output: the search key for a label-version pair (§11.7). -/// -/// 32 bytes, which is `VRF.Nh` — the truncation §17.1 applies to ECVRF's 64-byte -/// `beta_string`. Only [`PublicKey::verify`] and [`SecretKey::evaluate`] produce -/// one, so an output can only exist alongside a proof that justifies it. -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub struct Output(HashValue); - -impl Output { - /// The output as a prefix-tree search key. - #[must_use] - pub const fn search_key(&self) -> HashValue { - self.0 - } - - /// The output bytes. - #[must_use] - pub const fn as_bytes(&self) -> &[u8; OUTPUT_SIZE] { - self.0.as_bytes() - } -} - /// A VRF proof: `VRF.Np` bytes of `Gamma || c || s` (RFC 9381 §5.4.4). #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Proof([u8; PROOF_SIZE]); @@ -869,8 +750,11 @@ mod tests { ); } - /// The P-256 suite is not implemented, and asking for it says so instead of - /// evaluating the wrong curve. + /// Asking this module for the P-256 suite says so instead of evaluating the wrong + /// curve. The suite argument is not redundant: both suites are implemented, in + /// separate modules with separate key and proof types, and this is what stops an + /// edwards25519 key from being used under a `Configuration` that names P-256. + /// [`super::p256`] is the module that answers for that suite. #[test] fn p256_suite_is_refused() { let secret = SecretKey::from_seed([9; 32]); diff --git a/crates/kt-crypto/src/vrf/mod.rs b/crates/kt-crypto/src/vrf/mod.rs new file mode 100644 index 0000000..1b0a136 --- /dev/null +++ b/crates/kt-crypto/src/vrf/mod.rs @@ -0,0 +1,159 @@ +//! Verifiable Random Function (`draft-ietf-keytrans-protocol-05` §11.7). +//! +//! Each label-version pair's search key in the prefix tree is the VRF output over +//! a [`VrfInput`](kt_wire::structs::VrfInput) (§11.7). That is what keeps labels +//! private: the tree is indexed by a value only the log can compute, so a user who +//! is handed a search key learns nothing about the label behind it, and — because +//! the VRF is *verifiable* — the log cannot use a different key for the same label +//! than the one it proves. +//! +//! One submodule per registered ciphersuite, mirroring both RFC 9381's structure +//! and the peer's `crypto/vrf/{edwards25519,p256}`: +//! +//! | Suite (§17.1) | Module | ECVRF ciphersuite | `VRF.Np` | +//! |---|---|---|---| +//! | `KT_128_SHA256_Ed25519` (`0x0002`) | [`edwards25519`] | `ECVRF-EDWARDS25519-SHA512-TAI` | 80 | +//! | `KT_128_SHA256_P256` (`0x0001`) | [`p256`] | `ECVRF-P256-SHA256-TAI` | 81 | +//! +//! [`Error`] and [`Output`] are shared, because a search key is 32 bytes in both +//! suites — §17.1 truncates edwards25519's 64-byte `beta_string` and takes P-256's +//! whole 32 — and because a caller that cannot parse a proof wants the same error +//! either way. Everything else differs: the curve, the hash, the integer byte +//! order, the encoded sizes. +//! +//! The keys and proofs are deliberately *not* unified behind one enum. A +//! `Configuration` fixes the suite for the whole log, so a caller knows which it +//! holds; a sum type would only move that knowledge to run time and invite reading +//! an 81-byte proof as an 80-byte one. + +pub mod edwards25519; +pub mod p256; + +use core::fmt; + +use kt_wire::codec; +use kt_wire::structs::HashValue; + +use crate::suite::CipherSuite; + +/// Something wrong with a VRF key, proof, or the suite asked for. +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum Error { + /// The cipher suite's VRF is not implemented here. + /// + /// Both registered suites are implemented, so this is only reachable if the + /// registry gains a third. + UnsupportedSuite { + /// The suite that was asked for. + suite: CipherSuite, + }, + /// A public key was not a valid point on the suite's curve. + MalformedPublicKey, + /// A public key was of small order, so it commits to nothing. + /// + /// RFC 9381 §5.4.5 `ECVRF_validate_key`. Checked here because in this protocol + /// the VRF public key arrives in a `Configuration` from the log, and a + /// small-order key would let it produce the same output for every label. Only + /// reachable for edwards25519: P-256 has prime order, so its only small-order + /// element is the identity, which a valid SEC1 encoding cannot express. + SmallOrderPublicKey, + /// A proof was not `VRF.Np` bytes. + ProofLength { + /// `VRF.Np`. + expected: usize, + /// What was supplied. + actual: usize, + }, + /// A proof's `Gamma` was not a valid point on the suite's curve. + MalformedGamma, + /// A proof's `s` was not a canonical scalar, i.e. `s >= q`. + /// + /// RFC 9381 §5.4.4 step 7 requires rejecting these. Accepting them would make + /// proofs malleable: several byte strings would verify for one signature. The + /// peer's P-256 implementation additionally rejects `s == 0`; this does not, + /// because zero is a canonical scalar and RFC 9381 asks only about `s >= q`. + /// No honest prover emits it, so the difference is unreachable in practice. + NonCanonicalScalar, + /// The proof did not verify: the recomputed challenge differed. + BadProof, + /// A `VrfInput` could not be encoded, e.g. a label above the `2^8-1` ceiling. + Wire(codec::Error), +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + Self::UnsupportedSuite { suite } => { + write!(f, "the VRF for {suite} is not implemented") + } + Self::MalformedPublicKey => f.write_str("VRF public key is not a valid point"), + Self::SmallOrderPublicKey => f.write_str("VRF public key is of small order"), + Self::ProofLength { expected, actual } => { + write!(f, "VRF proof must be {expected} bytes, got {actual}") + } + Self::MalformedGamma => f.write_str("VRF proof's Gamma is not a valid point"), + Self::NonCanonicalScalar => f.write_str("VRF proof's s is not a canonical scalar"), + Self::BadProof => f.write_str("VRF proof does not verify"), + Self::Wire(err) => write!(f, "encoding the VRF input: {err}"), + } + } +} + +impl core::error::Error for Error { + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + match self { + // The wrapping variant has to be walkable, like the one in + // `crate::Error` and `kt_tree::log::Error`: a caller that wants to know + // *which* field of a VrfInput was too long should not have to parse the + // rendered message to find out. + Self::Wire(err) => Some(err), + _ => None, + } + } +} + +impl From for Error { + fn from(err: codec::Error) -> Self { + Self::Wire(err) + } +} + +/// A specialized [`Result`] for VRF operations. +pub type Result = core::result::Result; + +/// A VRF output: the search key for a label-version pair (§11.7). +/// +/// 32 bytes, which is `VRF.Nh` — the truncation §17.1 applies to ECVRF's 64-byte +/// `beta_string`. Only [`PublicKey::verify`] and [`SecretKey::evaluate`] produce +/// one, so an output can only exist alongside a proof that justifies it. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Output(HashValue); + +impl Output { + /// Wraps a computed `beta_string`. + /// + /// Crate-internal on purpose: an `Output` should only ever come from a + /// verification or an evaluation, so that holding one is evidence a proof + /// justified it. + pub(crate) const fn from_hash(hash: HashValue) -> Self { + Self(hash) + } + /// The output as a prefix-tree search key. + #[must_use] + pub const fn search_key(&self) -> HashValue { + self.0 + } + + /// The output bytes. + #[must_use] + pub const fn as_bytes(&self) -> &[u8; OUTPUT_SIZE] { + self.0.as_bytes() + } +} + +/// `VRF.Nh` for both registered suites: the output length in bytes (§17.1). +/// +/// RFC 9381's `beta_string` for this ciphersuite is 64 bytes; §17.1 specifies +/// "with the output truncated to 32 bytes", so that is what a search key is. +pub const OUTPUT_SIZE: usize = 32; diff --git a/crates/kt-crypto/src/vrf/p256.rs b/crates/kt-crypto/src/vrf/p256.rs new file mode 100644 index 0000000..d4c4d30 --- /dev/null +++ b/crates/kt-crypto/src/vrf/p256.rs @@ -0,0 +1,519 @@ +//! `ECVRF-P256-SHA256-TAI` (RFC 9381 §5.5), which §17.1 selects for +//! `KT_128_SHA256_P256`. +//! +//! Verification only. A VRF proof is produced by the log and consumed by everyone +//! else, so proving is a log's operation; [`super::edwards25519`] implements it +//! because its test vectors are round-trippable against RFC 9381 without a nonce +//! generator, and P-256's are not — RFC 9381 §5.4.2.1 derives the nonce with +//! RFC 6979, which is a signing concern this implementation has no use for. What a +//! client has to be able to do — take an 81-byte proof from a `BinaryLadderStep` +//! and recover the search key it commits to — is here in full. +//! +//! # Differences from edwards25519, all of them load-bearing +//! +//! | | edwards25519 | P-256 | +//! |---|---|---| +//! | hash | SHA-512 | SHA-256 | +//! | `suite_string` | `0x03` | `0x01` | +//! | integers | little-endian | **big-endian** | +//! | encoded point | 32 bytes | 33 bytes, SEC1 compressed | +//! | `VRF.Np` | 80 | **81** | +//! | `beta_string` | 64 bytes, truncated to 32 | 32 bytes, used whole | +//! | cofactor | 8, cleared explicitly | 1, nothing to clear | +//! +//! The byte order is the one to watch. RFC 9381's `string_to_int` follows the +//! curve's own convention, which for the NIST curves is big-endian — the opposite +//! of the edwards25519 ciphersuites. A `c` or `s` read the other way round produces +//! a verifier that agrees with nothing. +//! +//! # Oracles +//! +//! Two, and they are independent of each other: RFC 9381's Appendix B.1 test +//! vectors for `ECVRF-P256-SHA256-TAI`, which are in the tests below and settle the +//! byte order and every domain separator, and the peer's `crypto/vrf/p256` by way +//! of `interop/vectors/vrf.json`. + +use p256::elliptic_curve::PrimeField as _; +use p256::elliptic_curve::sec1::{FromSec1Point as _, Sec1Point, ToSec1Point as _}; +use p256::elliptic_curve::subtle::ConstantTimeEq as _; +use p256::{AffinePoint, ProjectivePoint, Scalar}; +use sha2::{Digest as _, Sha256}; + +use kt_wire::codec::Encode as _; +use kt_wire::structs::{HashValue, VrfInput}; + +use super::{Error, Output, Result}; + +/// `suite_string` for `ECVRF-P256-SHA256-TAI` (RFC 9381 §5.5). +const SUITE_STRING: u8 = 0x01; + +/// Domain separators (RFC 9381 §5.4.1.1, §5.4.3, §5.4.4). +const ENCODE_TO_CURVE_FRONT: u8 = 0x01; +const CHALLENGE_FRONT: u8 = 0x02; +const PROOF_TO_HASH_FRONT: u8 = 0x03; +const SEPARATOR_BACK: u8 = 0x00; + +/// `cLen`: the challenge's length in bytes (RFC 9381 §5.5). +const C_LEN: usize = 16; +/// `qLen`: the scalar `s`'s length in bytes. +const Q_LEN: usize = 32; +/// `ptLen`: a SEC1 compressed point's length in bytes. +const PT_LEN: usize = 33; + +/// `VRF.Np` for `KT_128_SHA256_P256`: the proof size in bytes (§17.1). +pub const PROOF_SIZE: usize = PT_LEN + C_LEN + Q_LEN; + +/// A public key's length in bytes: SEC1 compressed. +pub const PUBLIC_KEY_SIZE: usize = PT_LEN; + +/// A VRF proof: `VRF.Np` bytes of `Gamma || c || s` (RFC 9381 §5.4.4). +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct Proof([u8; PROOF_SIZE]); + +impl Proof { + /// Wraps `VRF.Np` bytes from the wire. + #[must_use] + pub const fn from_bytes(bytes: [u8; PROOF_SIZE]) -> Self { + Self(bytes) + } + + /// Wraps a slice that must be exactly `VRF.Np` bytes. + /// + /// # Errors + /// + /// [`Error::ProofLength`] if it is not. + pub fn from_slice(bytes: &[u8]) -> Result { + let array = <[u8; PROOF_SIZE]>::try_from(bytes).map_err(|_| Error::ProofLength { + expected: PROOF_SIZE, + actual: bytes.len(), + })?; + Ok(Self(array)) + } + + /// The proof bytes. + #[must_use] + pub const fn as_bytes(&self) -> &[u8; PROOF_SIZE] { + &self.0 + } + + /// Splits the proof into `(Gamma, c, s)` (RFC 9381 §5.4.4 `ECVRF_decode_proof`). + /// + /// `c` is `cLen` bytes and `s` is `qLen`, both big-endian, and both are widened + /// to a full scalar here. Step 7 requires rejecting `s >= q`, which + /// [`Scalar::from_repr`] does by construction. + fn decode(&self) -> Result<(ProjectivePoint, Scalar, Scalar)> { + let gamma_bytes = self.0.get(..PT_LEN).ok_or(Error::MalformedGamma)?; + let gamma = decode_point(gamma_bytes).ok_or(Error::MalformedGamma)?; + + // `c` occupies the *low* `cLen` bytes of a big-endian scalar, so it is + // left-padded rather than right-padded. Getting this backwards multiplies by + // 2^128 times the intended value. + let mut c_wide = [0_u8; Q_LEN]; + let c_slice = self + .0 + .get(PT_LEN..PT_LEN.saturating_add(C_LEN)) + .ok_or(Error::BadProof)?; + c_wide + .get_mut(Q_LEN.saturating_sub(C_LEN)..) + .ok_or(Error::BadProof)? + .copy_from_slice(c_slice); + // A `cLen`-byte value is far below the group order, so this cannot fail. + let c = Option::::from(Scalar::from_repr(c_wide.into())).ok_or(Error::BadProof)?; + + let s_slice = self + .0 + .get(PT_LEN.saturating_add(C_LEN)..) + .ok_or(Error::BadProof)?; + let s_bytes = <[u8; Q_LEN]>::try_from(s_slice).map_err(|_| Error::BadProof)?; + let s = Option::::from(Scalar::from_repr(s_bytes.into())) + .ok_or(Error::NonCanonicalScalar)?; + + Ok((gamma, c, s)) + } + + /// `Gamma`'s SEC1 compressed encoding, which is what `proof_to_hash` hashes. + fn gamma_bytes(&self) -> Result<&[u8]> { + self.0.get(..PT_LEN).ok_or(Error::MalformedGamma) + } +} + +/// A VRF public key: a SEC1 compressed P-256 point. +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub struct PublicKey { + point: ProjectivePoint, + /// The compressed encoding, kept because `encode_to_curve`'s salt is exactly + /// these bytes and recomputing it per counter attempt would be wasteful. + encoded: [u8; PUBLIC_KEY_SIZE], +} + +impl PublicKey { + /// Parses a SEC1 point. + /// + /// Both the compressed (33-byte) and uncompressed (65-byte) encodings are + /// accepted, because §11.2 carries the key as `opaque vrf_public_key<0..2^16-1>` + /// and says nothing about which. The peer emits compressed and accepts either. + /// + /// # Errors + /// + /// [`Error::MalformedPublicKey`] if the bytes are not a point on the curve, or + /// are the point at infinity — which RFC 9381 §5.4.5 requires rejecting, and + /// which for a prime-order curve is the only key that commits to nothing. + pub fn from_slice(bytes: &[u8]) -> Result { + let point = decode_point(bytes).ok_or(Error::MalformedPublicKey)?; + if bool::from(point.to_affine().is_identity()) { + return Err(Error::SmallOrderPublicKey); + } + let encoded = compress(&point); + Ok(Self { point, encoded }) + } + + /// The key's SEC1 compressed encoding. + #[must_use] + pub const fn as_bytes(&self) -> &[u8; PUBLIC_KEY_SIZE] { + &self.encoded + } + + /// Verifies a proof over an arbitrary `alpha` and returns the output (RFC 9381 + /// §5.3 `ECVRF_verify`, §5.2 `ECVRF_proof_to_hash`). + /// + /// # Errors + /// + /// [`Error::MalformedGamma`] or [`Error::NonCanonicalScalar`] if the proof does + /// not decode, and [`Error::BadProof`] if the recomputed challenge differs. + pub fn verify_raw(&self, alpha: &[u8], proof: &Proof) -> Result { + verify_steps(self, alpha, proof) + } + + /// Verifies a proof over a `VrfInput` and returns the search key (§11.7). + /// + /// # Errors + /// + /// [`Error::Wire`] if the input cannot be encoded, plus anything + /// [`PublicKey::verify_raw`] reports. + pub fn verify(&self, input: &VrfInput, proof: &Proof) -> Result { + let mut encoder = kt_wire::codec::Encoder::new(); + input.encode(&mut encoder)?; + self.verify_raw(encoder.as_bytes(), proof) + } +} + +/// RFC 9381 §5.4.1.1 `ECVRF_encode_to_curve_try_and_increment`. +/// +/// Hashes `salt || alpha || ctr` and reads the digest as the *x* coordinate of a +/// compressed point with an even *y*, incrementing `ctr` until one lands on the +/// curve. Two attempts in three succeed, so the loop is short; it is bounded at 256 +/// by the counter being a single byte, and returning [`None`] there rather than +/// panicking is the difference between a malformed input and a crash. +fn encode_to_curve(salt: &[u8; PUBLIC_KEY_SIZE], alpha: &[u8]) -> Option { + for ctr in 0..=u8::MAX { + let mut hasher = Sha256::new(); + hasher.update([SUITE_STRING, ENCODE_TO_CURVE_FRONT]); + hasher.update(salt); + hasher.update(alpha); + hasher.update([ctr, SEPARATOR_BACK]); + let hashed = hasher.finalize(); + + let mut candidate = [0_u8; PT_LEN]; + // SEC1 tag 0x02: compressed, even y. RFC 9381 §5.5 fixes it for this suite, + // so a digest that is a valid x coordinate yields exactly one point. + *candidate.first_mut()? = 0x02; + candidate.get_mut(1..)?.copy_from_slice(&hashed); + + if let Some(point) = decode_point(&candidate) { + // P-256 has cofactor 1, so there is no small-order component to clear — + // the step edwards25519 needs `mul_by_cofactor` for. The identity cannot + // arise here either: a compressed encoding with a tag byte always names + // an affine point. + return Some(point); + } + } + None +} + +/// RFC 9381 §5.4.3 `ECVRF_challenge_generation`, truncated to `cLen` bytes. +fn challenge(points: [&ProjectivePoint; 5]) -> [u8; C_LEN] { + let mut hasher = Sha256::new(); + hasher.update([SUITE_STRING, CHALLENGE_FRONT]); + for point in points { + hasher.update(compress(point)); + } + hasher.update([SEPARATOR_BACK]); + let hashed = hasher.finalize(); + + let mut out = [0_u8; C_LEN]; + // The digest is 32 bytes and `C_LEN` is 16, so the slice always exists. + if let Some(front) = hashed.get(..C_LEN) { + out.copy_from_slice(front); + } + out +} + +/// RFC 9381 §5.2 `ECVRF_proof_to_hash`. +/// +/// No truncation and no cofactor multiplication: `beta_string` is already 32 bytes, +/// which is `VRF.Nh`, and P-256's cofactor is 1. +fn proof_to_hash(gamma_bytes: &[u8]) -> Output { + let mut hasher = Sha256::new(); + hasher.update([SUITE_STRING, PROOF_TO_HASH_FRONT]); + hasher.update(gamma_bytes); + hasher.update([SEPARATOR_BACK]); + let hashed = hasher.finalize(); + + let mut out = [0_u8; 32]; + out.copy_from_slice(&hashed); + Output::from_hash(HashValue::from_bytes(out)) +} + +/// Decodes a SEC1 point, rejecting anything not on the curve. +fn decode_point(bytes: &[u8]) -> Option { + let encoded = Sec1Point::::from_bytes(bytes).ok()?; + let affine = Option::::from(AffinePoint::from_sec1_point(&encoded))?; + Some(ProjectivePoint::from(affine)) +} + +/// A point's SEC1 compressed encoding. +fn compress(point: &ProjectivePoint) -> [u8; PT_LEN] { + let mut out = [0_u8; PT_LEN]; + let bytes = point.to_affine().to_sec1_point(true); + if let Some(slot) = out.get_mut(..bytes.as_bytes().len()) { + slot.copy_from_slice(bytes.as_bytes()); + } + out +} + +/// Everything a verifier needs from a decoded proof, in one place so that +/// [`PublicKey::verify_raw`] reads like RFC 9381 §5.3's numbered steps. +#[allow( + clippy::arithmetic_side_effects, + reason = "these are elliptic-curve group operations on p256 types, not machine integers: scalar multiplication and point addition are total on the group and cannot overflow or panic. The lint reads the operator overloads as arithmetic it should be guarding." +)] +fn verify_steps(key: &PublicKey, alpha: &[u8], proof: &Proof) -> Result { + // Steps 1-3: decode the proof. `c` comes back as a scalar for the arithmetic and + // is compared in its wire form below, because RFC 9381 step 8 compares the + // *strings*, and a scalar comparison would accept a `c` that is congruent but + // differently encoded. + let (gamma, c, s) = proof.decode()?; + + // Step 4: H = encode_to_curve(PK, alpha). + let h = encode_to_curve(key.as_bytes(), alpha).ok_or(Error::BadProof)?; + + // Step 5: U = s*B - c*Y. + let u = (ProjectivePoint::GENERATOR * s) - (key.point * c); + // Step 6: V = s*H - c*Gamma. + let v = (h * s) - (gamma * c); + + // Steps 7-8: c' = challenge(Y, H, Gamma, U, V), and c' must equal c. + let recomputed = challenge([&key.point, &h, &gamma, &u, &v]); + let mut supplied = [0_u8; C_LEN]; + supplied.copy_from_slice( + proof + .0 + .get(PT_LEN..PT_LEN.saturating_add(C_LEN)) + .ok_or(Error::BadProof)?, + ); + // Constant-time, matching the edwards25519 path: a challenge comparison that + // leaks where two proofs first differ is a timing oracle on the log's key. + if !bool::from(recomputed.ct_eq(&supplied)) { + return Err(Error::BadProof); + } + + Ok(proof_to_hash(proof.gamma_bytes()?)) +} + +#[cfg(test)] +#[allow( + clippy::arithmetic_side_effects, + clippy::indexing_slicing, + clippy::panic, + clippy::unwrap_used, + reason = "tests fail loudly by panicking; the lints protect the verification paths" +)] +mod tests { + use super::*; + use alloc::vec::Vec; + + /// RFC 9381 Appendix B.1's `ECVRF-P256-SHA256-TAI` examples, as `(public key, alpha, + /// beta, pi)`. These are the oracle that matters: they are independent of both this + /// implementation and the peer, and they settle every choice this module makes — the + /// suite string, the three domain separators, the big-endian integers, the SEC1 `0x02` + /// tag in `encode_to_curve`, and the absence of truncation in `proof_to_hash`. Getting + /// any one of them wrong fails all three. + const VECTORS: [(&str, &str, &str, &str); 3] = [ + ( + "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", + "73616d706c65", + "a3ad7b0ef73d8fc6655053ea22f9bede8c743f08bbed3d38821f0e16474b505e", + "035b5c726e8c0e2c488a107c600578ee75cb702343c153cb1eb8dec77f4b5071b4a53f0a46f018bc2c\ + 56e58d383f2305e0975972c26feea0eb122fe7893c15af376b33edf7de17c6ea056d4d82de6bc02f", + ), + ( + "0360fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb6", + "74657374", + "a284f94ceec2ff4b3794629da7cbafa49121972671b466cab4ce170aa365f26d", + "034dac60aba508ba0c01aa9be80377ebd7562c4a52d74722e0abae7dc3080ddb56c19e067b15a8a817\ + 4905b13617804534214f935b94c2287f797e393eb0816969d864f37625b443f30f1a5a33f2b3c854", + ), + ( + "03596375e6ce57e0f20294fc46bdfcfd19a39f8161b58695b3ec5b3d16427c274d", + "4578616d706c65207573696e67204543445341206b65792066726f6d20417070656e646978204c2e34\ + 2e32206f6620414e53492e58392d36322d32303035", + "90871e06da5caa39a3c61578ebb844de8635e27ac0b13e829997d0d95dd98c19", + "03d03398bf53aa23831d7d1b2937e005fb0062cbefa06796579f2a1fc7e7b8c667d091c00b0f5c3619\ + d10ecea44363b5a599cadc5b2957e223fec62e81f7b4825fc799a771a3d7334b9186bdbee87316b1", + ), + ]; + + fn unhex(text: &str) -> Vec { + let cleaned: Vec = text.bytes().filter(|b| !b.is_ascii_whitespace()).collect(); + cleaned + .chunks(2) + .map(|pair| { + let digit = |b: u8| match b { + b'0'..=b'9' => b - b'0', + b'a'..=b'f' => b - b'a' + 10, + _ => panic!("not hex: {b}"), + }; + digit(pair[0]) * 16 + digit(pair[1]) + }) + .collect() + } + + #[test] + fn rfc_9381_vectors_verify() { + for (index, (public, alpha, beta, pi)) in VECTORS.iter().enumerate() { + let key = PublicKey::from_slice(&unhex(public)).unwrap(); + let proof = Proof::from_slice(&unhex(pi)).unwrap(); + let output = key.verify_raw(&unhex(alpha), &proof).unwrap(); + assert_eq!( + output.as_bytes().as_slice(), + unhex(beta).as_slice(), + "vector {index}" + ); + // A compressed key round-trips to the bytes it came from. + assert_eq!(key.as_bytes().as_slice(), unhex(public).as_slice()); + } + } + + /// The proof is bound to the message. RFC 9381's own vectors share a key across two + /// different `alpha`s, so each one's proof must fail against the other's message — + /// which is a sharper check than a random tamper, because both proofs are individually + /// valid. + #[test] + fn a_proof_does_not_verify_for_another_message() { + let key = PublicKey::from_slice(&unhex(VECTORS[0].0)).unwrap(); + let first = Proof::from_slice(&unhex(VECTORS[0].3)).unwrap(); + let second = Proof::from_slice(&unhex(VECTORS[1].3)).unwrap(); + assert_eq!( + key.verify_raw(&unhex(VECTORS[1].1), &first), + Err(Error::BadProof) + ); + assert_eq!( + key.verify_raw(&unhex(VECTORS[0].1), &second), + Err(Error::BadProof) + ); + } + + /// And to the key. Vector 2 uses a different key from vectors 0 and 1, so crossing them + /// must fail — the check that `encode_to_curve`'s salt really is the public key. + #[test] + fn a_proof_does_not_verify_under_another_key() { + let other = PublicKey::from_slice(&unhex(VECTORS[2].0)).unwrap(); + let proof = Proof::from_slice(&unhex(VECTORS[0].3)).unwrap(); + assert_eq!( + other.verify_raw(&unhex(VECTORS[0].1), &proof), + Err(Error::BadProof) + ); + } + + /// Every single-byte mutation of a valid proof must be rejected. This is where the + /// `Gamma`/`c`/`s` boundaries get checked without having to reason about them: a flipped + /// bit in `Gamma` usually leaves the curve, one in `c` or `s` changes the arithmetic, and + /// all three must come back as an error rather than a different output. + #[test] + fn every_single_byte_mutation_is_rejected() { + let key = PublicKey::from_slice(&unhex(VECTORS[0].0)).unwrap(); + let bytes = unhex(VECTORS[0].3); + let alpha = unhex(VECTORS[0].1); + for position in 0..bytes.len() { + let mut mutated = bytes.clone(); + mutated[position] ^= 0x01; + let proof = Proof::from_slice(&mutated).unwrap(); + assert!( + key.verify_raw(&alpha, &proof).is_err(), + "byte {position} flipped and the proof still verified" + ); + } + } + + /// The length is `VRF.Np`, and nothing else. §13.1's `BinaryLadderStep` reads a proof as + /// a fixed-size field, so a length mismatch means the response was decoded under the + /// wrong cipher suite — 80 bytes is an edwards25519 proof. + #[test] + fn the_proof_length_is_np() { + assert_eq!(PROOF_SIZE, 81); + let short = unhex(VECTORS[0].3)[..80].to_vec(); + assert_eq!( + Proof::from_slice(&short), + Err(Error::ProofLength { + expected: 81, + actual: 80 + }) + ); + } + + /// A public key must be a point on the curve, and must not be the identity. On a + /// prime-order curve the identity is the only element that commits to nothing, and SEC1 + /// spells it as a single zero byte. + #[test] + fn malformed_public_keys_are_refused() { + // Right length and tag, wrong x coordinate: not on the curve. + let mut bogus = unhex(VECTORS[0].0); + bogus[1] ^= 0xff; + assert_eq!( + PublicKey::from_slice(&bogus), + Err(Error::MalformedPublicKey) + ); + // SEC1's identity encoding is a single zero byte, and it *parses* — so it is caught + // by the explicit RFC 9381 §5.4.5 check rather than by point decoding, and reports + // the more specific error. + assert_eq!( + PublicKey::from_slice(&[0x00]), + Err(Error::SmallOrderPublicKey) + ); + assert_eq!(PublicKey::from_slice(&[]), Err(Error::MalformedPublicKey)); + // An edwards25519 key is 32 bytes, which is not a SEC1 encoding at all. + assert_eq!( + PublicKey::from_slice(&[0x11; 32]), + Err(Error::MalformedPublicKey) + ); + } + + /// `s >= q` is rejected outright (RFC 9381 §5.4.4 step 7), rather than reduced. Accepting + /// it would make proofs malleable: `s` and `s + q` would both verify. + #[test] + fn a_non_canonical_s_is_refused() { + let mut bytes = unhex(VECTORS[0].3); + // The group order itself, which is the smallest non-canonical value. + let order = unhex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"); + bytes[49..].copy_from_slice(&order); + let proof = Proof::from_slice(&bytes).unwrap(); + let key = PublicKey::from_slice(&unhex(VECTORS[0].0)).unwrap(); + assert_eq!( + key.verify_raw(&unhex(VECTORS[0].1), &proof), + Err(Error::NonCanonicalScalar) + ); + } + + /// The §11.7 entry point: a `VrfInput` rather than raw bytes. The search key is the + /// output, so this is the shape a prefix tree lookup actually uses. + #[test] + fn a_vrf_input_produces_a_search_key() { + let key = PublicKey::from_slice(&unhex(VECTORS[0].0)).unwrap(); + let proof = Proof::from_slice(&unhex(VECTORS[0].3)).unwrap(); + let input = VrfInput::new(b"alice@example.com".to_vec(), 3); + // The vectors' alpha is not a VrfInput encoding, so this must fail rather than + // silently accept — the point is that `verify` encodes the input rather than + // taking bytes. + assert_eq!(key.verify(&input, &proof), Err(Error::BadProof)); + } +} diff --git a/crates/kt-tree/src/combined.rs b/crates/kt-tree/src/combined.rs index 5e6ce5a..516a59a 100644 --- a/crates/kt-tree/src/combined.rs +++ b/crates/kt-tree/src/combined.rs @@ -746,6 +746,29 @@ impl Established { (left_inclusion, right_non_inclusion) } + /// Records what an entry's ladder *would* have proved, without one having been received. + /// + /// §9.1 step 2.2 needs this: lookups skipped by step 2.1 "will still be omitted as if the log + /// entries had been inspected", so the omission state has to include ladders that never + /// arrived. What each lookup would have shown follows from the greatest version at that entry — + /// a version at or below it is present, one above it is absent — which is exactly the + /// invariant that makes the omission rule sound in the first place. + /// + /// `greatest` is `None` where the label did not exist there, in which case every lookup would + /// have proven non-inclusion. + fn assume(&mut self, position: u64, greatest: Option, versions: &[u32]) { + let mut included = Vec::new(); + let mut absent = Vec::new(); + for version in versions { + if greatest.is_some_and(|bound| *version <= bound) { + included.push(*version); + } else { + absent.push(*version); + } + } + self.entries.push((position, included, absent)); + } + /// Records what an entry's ladder proved. fn record(&mut self, position: u64, versions: &[u32], results: &[PrefixSearchResult]) { let mut included = Vec::new(); @@ -935,7 +958,7 @@ pub fn greatest_version_search( }; reader.establish_root(current, root)?; inspected.push((current, root)); - return Ok(Outcome::NoVersions { start, inspected }); + return Ok(Outcome::NegativeResult { start, inspected }); } return Err(SearchError::RightmostInconsistent { position: current, @@ -992,13 +1015,23 @@ pub fn greatest_version_search( pub enum Outcome { /// The log's claim about the greatest version holds. Found(Search), - /// The label has no versions at all. + /// The response reports that the label has no versions, which §13.1 requires a client to + /// **reject**. /// - /// §6.3 has no branch for this, which is `DRAFT-08` in `docs/interop.md`: the log answers - /// with a claim of version 0 and a proof that version 0 does not exist, and step 2 read - /// literally rejects it. Reported as an outcome because the response is well formed and - /// says something true — just not something §6.3 anticipates. - NoVersions { + /// The log answers with a claim of version 0 and a proof that version 0 does not exist. + /// §6.3 step 2 read literally rejects it, and §6.3 offers no branch that accepts it — + /// `DRAFT-08`. As of 2026-07-28 the draft says why: "this document doesn't define a way to + /// encode a negative result (for a missing label or version) in a `SearchResponse`. This + /// functionality was omitted due to its low expected utility. Unless a client has adopted + /// a documented protocol for encoding negative search results, clients MUST consider any + /// `SearchResponse` with a negative result as invalid and having failed validation." + /// + /// So this is not a successful search. It is reported as a distinct outcome rather than an + /// error because the two situations are worth telling apart — a proof that is malformed and + /// a proof that is well formed and reports a negative result — and because the peer serves + /// such responses (`KT-05`), so a harness needs to name what it received. A client MUST NOT + /// treat it as an answer about the label absent the documented protocol §13.1 refers to. + NegativeResult { /// Where the search started. start: u64, /// The entries inspected, with the prefix tree roots their proofs computed. @@ -1621,9 +1654,11 @@ mod search_tests { } /// A label with no versions: `DRAFT-08`. The log claims version 0 and proves it absent, and - /// §6.3 step 2 read literally rejects the only answer available. + /// §6.3 step 2 read literally rejects the only answer available — which §13.1 now confirms + /// is the right thing to do, since the draft defines no encoding for a negative result and + /// requires clients to treat one as failed validation. #[test] - fn a_label_with_no_versions_is_an_outcome_not_a_failure() { + fn a_label_with_no_versions_is_reported_as_a_negative_result() { let size = 1_u64; let window = 604_800_000; let empty = prefix::PrefixTree::new(); @@ -1645,12 +1680,12 @@ mod search_tests { let outcome = greatest_version_search(SUITE, size, window, 0, &keys_through(0), &mut reader).unwrap(); match outcome { - Outcome::NoVersions { start, inspected } => { + Outcome::NegativeResult { start, inspected } => { assert_eq!(start, 0); assert_eq!(inspected.len(), 1); assert_eq!(inspected[0].1, empty.root(SUITE)); } - Outcome::Found(_) => panic!("the label has no versions"), + Outcome::Found(_) => panic!("a negative result must not read as a found version"), } reader.finish().unwrap(); } @@ -3838,3 +3873,1590 @@ mod owner_monitor_tests { assert!(core::error::Error::source(&wrapped).is_some()); } } + +/// A label owner's local state about one label (§8.3, §9.1). +/// +/// An owner is the only party in the protocol that keeps state *about versions it created*, and +/// §9.1 leans on all of it: which entry each version went into, what the greatest version was at +/// the reference point, and where the last update landed. §13.5 step 1 is a check against this +/// state and nothing else — "verify that `position` is to the right of where the previous greatest +/// version of the label was inserted" — so a verifier without it cannot check an update at all. +/// +/// The representation is positions rather than versions because that is what the owner learns: +/// each update tells it "your new versions are in entry `position`", and the version numbers +/// follow from counting. `version_at_starting` is `None` when the label did not exist at the +/// reference point, which is the ordinary case for a label's first owner. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct OwnerState { + /// The rightmost distinguished entry the owner has verified, from §8.3's first algorithm. + pub starting: u64, + /// The greatest version that existed at `starting`, or `None` if the label did not exist. + pub version_at_starting: Option, + /// The entry each version created since `starting` was inserted into, ascending. + /// + /// One element per version, so two versions in one entry appear as that position twice. + pub upcoming: Vec, +} + +impl OwnerState { + /// The greatest version the owner believes exists, or `None` if the label does not yet. + /// + /// # Errors + /// + /// [`UpdateError::VersionOverflow`] if the count does not fit the `uint32` version field. + fn greatest_version(&self) -> core::result::Result, UpdateError> { + self.version_after(self.upcoming.len()) + } + + /// The greatest version the owner believes existed as of entry `position`. + /// + /// §9.1 step 2.2's ladders target the previous greatest version, and §9.1's seeding of which + /// lookups to omit needs the same figure at the *parent* of the entry the walk starts from. + /// Neither is the global greatest: a version created in a later entry did not exist here. + /// + /// Only meaningful for positions at or after `starting`, which is where the owner's knowledge + /// begins. + /// + /// # Errors + /// + /// [`UpdateError::VersionOverflow`] if the count does not fit the `uint32` version field. + fn greatest_version_at(&self, position: u64) -> core::result::Result, UpdateError> { + let created = self + .upcoming + .iter() + .filter(|entry| **entry <= position) + .count(); + self.version_after(created) + } + + /// The greatest version after `created` versions have been added to `version_at_starting`. + /// + /// The awkward case is a label that did not exist at the reference point: `created` versions + /// then run from 0, so the greatest is `created - 1`, and zero of them means the label still + /// does not exist. + fn version_after(&self, created: usize) -> core::result::Result, UpdateError> { + let created = u64::try_from(created).map_err(|_| UpdateError::VersionOverflow)?; + let total = match self.version_at_starting { + Some(version) => u64::from(version) + .checked_add(created) + .ok_or(UpdateError::VersionOverflow)?, + None if created == 0 => return Ok(None), + None => created.saturating_sub(1), + }; + u32::try_from(total) + .map(Some) + .map_err(|_| UpdateError::VersionOverflow) + } + + /// The entry the most recent version went into, or `starting` if there have been none. + /// + /// §13.5 step 1 compares an update's position against this: a new version cannot appear to + /// the left of one the owner already knows about, because the prefix tree only grows. + #[must_use] + pub fn last_update(&self) -> u64 { + self.upcoming.last().copied().unwrap_or(self.starting) + } +} + +/// What verifying an update established (§9.1). +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Updated { + /// The previous tree's frontier entries whose ladders were checked, with the prefix tree + /// root each proof computed (§9.1 step 2). + pub previous: Vec<(u64, HashValue)>, + /// The prefix tree root established at the entry holding the new versions. + /// + /// `None` when nothing was looked up there at all: a distinguished entry whose new versions + /// are all covered by a search binary ladder for the new greatest version needs no proof of + /// its own, because §8.3's second algorithm will reach it. + pub root: Option, + /// Whether the entry holding the new versions is distinguished, which is what §9.1 steps 3 + /// and 4 branch on. + pub distinguished: bool, + /// The owner's state with the new versions recorded. + pub owner: OwnerState, + /// The entry to add to the contact monitoring map, present only in step 4's case. + /// + /// A distinguished entry needs no map entry: owner monitoring visits it anyway. A + /// non-distinguished one does, and §9.1 step 4 says so — the mapping is `position` to the + /// new greatest version. + pub contact: Option<(u64, u32)>, +} + +/// Runs §9.1: a label owner checking that new versions of its label were inserted correctly. +/// +/// This is the protocol's only *two-tree* algorithm. Everything else verifies a claim about the +/// tree as presented; this one verifies a claim about the boundary between two trees — the +/// **previous tree**, "the log tree up to but excluding the log entry where the new versions were +/// added", and the **current tree** as presented now. The reason §9.1 gives for the first phase is +/// worth quoting, because it is the whole point of the algorithm: without it a log "might create a +/// malicious version of a label, later change the corresponding value to something non-malicious, +/// and try to only inform the label owner of the non-malicious value". +/// +/// So phase one is a greatest-version search over the previous tree's frontier, proving that the +/// owner's *previous* greatest version really was the greatest before the update. Phase two checks +/// how the new versions were created, and branches on whether their entry is distinguished — not +/// for security but for economy, since §8.3's second algorithm will visit a distinguished entry +/// anyway. +/// +/// `position` is where the log says the new versions went, `versions` how many were created, and +/// `owner` the state the owner held *before* this update. `keys` maps each version in §13.5's +/// `binary_ladder` to its search key; [`ladder::update_binary_ladder`] says which versions that +/// must be, and [`update_ladder_keys`] builds the map while making §13.5 step 4's checks. +/// +/// # Errors +/// +/// [`UpdateError`] where the proof does not establish what §9.1 requires, including §13.5 step 1's +/// check on `position` and the reader's own refusals. +#[allow( + clippy::too_many_arguments, + reason = "an update is checked against three separate things — the tree, the owner's state and \ + the response's own claims — and every argument belongs to one of them. Grouping them \ + into a struct would put the log's parameters and the owner's state behind the same \ + name, which is the distinction the algorithm turns on" +)] +pub fn owner_update( + suite: CipherSuite, + size: u64, + window: u64, + position: u64, + versions: usize, + owner: &OwnerState, + keys: &BTreeMap, + reader: &mut Reader<'_>, +) -> core::result::Result { + if position >= size { + return Err(UpdateError::EntryDoesNotExist { position, size }); + } + // §13.5 step 1. This is also what makes the previous tree non-empty: `last_update` is at + // worst the owner's starting position, so `position` is at least 1 and the previous tree has + // at least one entry to run phase one over. + let last_update = owner.last_update(); + if position <= last_update { + return Err(UpdateError::PositionNotAdvancing { + position, + last_update, + }); + } + // §13.5 step 2 requires `info` to be non-empty, and `versions` is its length. + if versions == 0 { + return Err(UpdateError::NoNewVersions); + } + + let previous_greatest = owner.greatest_version()?; + let created = u32::try_from(versions).map_err(|_| UpdateError::VersionOverflow)?; + let start_ver = match previous_greatest { + Some(version) => version.checked_add(1).ok_or(UpdateError::VersionOverflow)?, + None => 0, + }; + let end_ver = start_ver + .checked_add(created.saturating_sub(1)) + .ok_or(UpdateError::VersionOverflow)?; + // A ladder for `u32::MAX` needs a lookup for `2^32`, so that version can never be shown to be + // the greatest. Refusing here rather than at the ladder keeps the reason attached to the + // request that caused it. + if end_ver == u32::MAX { + return Err(UpdateError::VersionOverflow); + } + + // The new versions that a search binary ladder for the new greatest version would *not* look + // up. Steps 3 and 4 both ask for a plain inclusion proof for these, ascending: the ladder + // proves where the top of the history is, and these prove the rest of the update is really + // there rather than merely claimed. + let covered = ladder::search_binary_ladder(end_ver, end_ver, &[], &[])?; + let additional: Vec = (start_ver..=end_ver) + .filter(|version| !covered.contains(version)) + .collect(); + + // Phase one, over the previous tree. + let mut established = Established::default(); + let previous = verify_previous_tree( + suite, + size, + window, + position, + owner, + previous_greatest, + keys, + &mut established, + reader, + )?; + + // Phase two. §9.1 steps 3 and 4 are the same question asked of a different entry, so the + // distinguished check runs the same way it did in phase one. + let distinguished = non_distinguished_ancestor(size, window, position, reader)?.is_none(); + let mut root = None; + let mut contact = None; + if !distinguished { + // Step 4's first proof: a search binary ladder for the new greatest version, with + // redundant lookups omitted — and the omissions carry over from phase one, because the + // entries inspected there are to this entry's left. + let (left_inclusion, right_non_inclusion) = established.sets_for(position); + let ladder_versions = + ladder::search_binary_ladder(end_ver, end_ver, &left_inclusion, &right_non_inclusion)?; + let computed = evaluate_ladder( + suite, + position, + end_ver, + &ladder_versions, + keys, + &mut established, + reader, + )?; + root = Some(computed); + contact = Some((position, end_ver)); + } + + // Steps 3 and 4's second proof: the new versions the ladder did not cover, in ascending order, + // every one of which must be included. An empty list means no proof at all — not a proof of + // nothing — which is what makes §12.3's exact-count rule bite here. + if !additional.is_empty() { + reader.timestamp(position)?; + let proof = reader.prefix_proof(position)?; + if proof.results.len() != additional.len() { + return Err(UpdateError::AdditionalLength { + position, + expected: additional.len(), + actual: proof.results.len(), + }); + } + let mut entries = Vec::new(); + for (version, result) in additional.iter().zip(proof.results.iter()) { + if !result.is_inclusion() { + return Err(UpdateError::VersionNotCreated { + position, + version: *version, + }); + } + let key = keys.get(version).ok_or(UpdateError::MissingLadderKey { + position, + version: *version, + })?; + entries.push(prefix::SearchEntry::included( + key.vrf_output, + key.commitment.ok_or(UpdateError::MissingCommitment { + position, + version: *version, + })?, + )); + } + let computed = prefix::evaluate(suite, &entries, proof)?; + // §12.3.4's rule that two proofs for one entry must agree is what ties this to the ladder + // above: both are proofs against the same prefix tree, and a log that answered the two + // from different trees is caught here rather than anywhere else. + reader.establish_root(position, computed)?; + root = Some(computed); + } + + let mut updated = owner.clone(); + for _ in 0..versions { + updated.upcoming.push(position); + } + Ok(Updated { + previous, + root, + distinguished, + owner: updated, + contact, + }) +} + +/// §9.1 steps 1 and 2: the greatest-version search over the previous tree. +#[allow( + clippy::too_many_arguments, + reason = "the log's parameters, the owner's state, the ladder keys, the omission state and the \ + reader are all inputs to the numbered steps; bundling them would hide which step \ + reads what" +)] +fn verify_previous_tree( + suite: CipherSuite, + size: u64, + window: u64, + position: u64, + owner: &OwnerState, + previous_greatest: Option, + keys: &BTreeMap, + established: &mut Established, + reader: &mut Reader<'_>, +) -> core::result::Result, UpdateError> { + // The previous tree is the log up to but excluding `position`, so its size *is* `position`, + // and its rightmost entry is `position - 1`. `owner_update` has already established that + // `position` exceeds the owner's last update, so this cannot underflow. + let previous_size = position; + let rightmost = position.saturating_sub(1); + + // Step 1. Note which tree each half of this question belongs to: the entries walked are the + // previous tree's frontier, and whether they are distinguished is decided in the *current* + // tree. A frontier entry that was distinguished before the update may not be now. + let Some(first) = non_distinguished_ancestor(size, window, rightmost, reader)? else { + // Every entry down to the previous tree's rightmost is distinguished in the current tree, + // so §8.3's second algorithm will cover them all and step 2 has nothing to do. + return Ok(Vec::new()); + }; + + // The entry found may sit above the previous tree — the current tree is taller — in which case + // the first non-distinguished frontier entry is the previous tree's root, and descending left + // is how to reach it. Ancestor-closure is what makes this equivalent to walking the previous + // frontier and testing each: below a non-distinguished entry nothing is distinguished. + let mut current = first; + while current > rightmost { + current = ibst::left(current)?; + } + + // Step 2.2's omissions, seeded before the walk starts. §9.1 is explicit that lookups skipped + // by step 2.1 "will still be omitted as if the log entries had been inspected", so the + // omission state has to include ladders that were never actually received. Two of them: the + // ladder a greatest-version search would have produced at the parent of the entry the walk + // starts from, and one at the entry itself. + if let Some(greatest) = previous_greatest { + let ladder = ladder::search_binary_ladder(greatest, greatest, &[], &[])?; + let root = ibst::root(previous_size)?; + if current != root { + let parent = *ibst::direct_path(current, previous_size)? + .first() + .ok_or(UpdateError::NoParent { position: current })?; + established.assume(parent, owner.greatest_version_at(parent)?, &ladder); + } + // Not strictly true that `greatest` exists at `current` — it may not — but no lookup + // happens before it does, so the omissions this produces are the same either way. + established.assume(current, Some(greatest), &ladder); + } + + let mut inspected = Vec::new(); + // Step 2.1, as it read from 2026-07-28: "If a previous version of the label existed, and the + // current log entry's index is less than or equal to the index of the log entry where the + // previous greatest version was inserted, skip inspecting this log entry." The condition on a + // previous version having existed is load-bearing and easy to lose: with no previous version + // there is no earlier update whose response could have carried the ladder, so nothing is + // skipped — not even entries at or before the owner's reference point. + // + // The earlier text said "if a binary ladder would have already been received from this log + // entry in step 2.2 when processing a previous label update", which is the same rule for a + // label that has versions and silently different for one that does not. The peer implements + // the earlier reading, falling back to the reference point unconditionally; no recorded case + // separates them, because every case's walk begins to the right of the reference point. + // Recorded as `KT-06`. + let skip_through = previous_greatest.map(|_| owner.last_update()); + loop { + if skip_through.is_none_or(|through| current > through) { + // Step 2.2. The target is the previous greatest version, or 0 where the label did not + // exist — there is no lower version to ask about. + let target = previous_greatest.unwrap_or(0); + let (left_inclusion, right_non_inclusion) = established.sets_for(current); + let versions = ladder::search_binary_ladder( + target, + target, + &left_inclusion, + &right_non_inclusion, + )?; + let root = evaluate_previous_ladder( + suite, + current, + target, + previous_greatest.is_some(), + &versions, + keys, + established, + reader, + )?; + inspected.push((current, root)); + } + + if current == rightmost { + return Ok(inspected); + } + current = ibst::right(current, previous_size)?; + } +} + +/// Reads one of step 2.2's ladders and checks step 2.3's conclusion. +/// +/// Step 2.3 wants the ladder to terminate "in a way that is consistent with the previous greatest +/// version of the label being the greatest that exists". Where the label existed that means the +/// ladder places the target exactly; where it did not, it means the single lookup for version 0 +/// proves version 0 absent. +#[allow( + clippy::too_many_arguments, + reason = "one call site, and every argument names something a numbered step refers to" +)] +fn evaluate_previous_ladder( + suite: CipherSuite, + position: u64, + target: u32, + exists: bool, + versions: &[u32], + keys: &BTreeMap, + established: &mut Established, + reader: &mut Reader<'_>, +) -> core::result::Result { + reader.timestamp(position)?; + let proof = reader.prefix_proof(position)?; + let used = versions + .get(..proof.results.len()) + .ok_or(UpdateError::LadderLength { + position, + expected: versions.len(), + actual: proof.results.len(), + })?; + let ordering = ladder::interpret_search_ladder(used, target, &proof.results)?; + let wanted = if exists { + core::cmp::Ordering::Equal + } else { + core::cmp::Ordering::Less + }; + if ordering != wanted { + return Err(UpdateError::PreviousTreeInconsistent { + position, + claimed: target, + exists, + }); + } + let entries = ladder_entries(keys, position, used, &proof.results)?; + established.record(position, used, &proof.results); + let root = prefix::evaluate(suite, &entries, proof)?; + reader.establish_root(position, root)?; + Ok(root) +} + +/// Reads step 4's ladder at the entry holding the new versions. +fn evaluate_ladder( + suite: CipherSuite, + position: u64, + target: u32, + versions: &[u32], + keys: &BTreeMap, + established: &mut Established, + reader: &mut Reader<'_>, +) -> core::result::Result { + reader.timestamp(position)?; + let proof = reader.prefix_proof(position)?; + let used = versions + .get(..proof.results.len()) + .ok_or(UpdateError::LadderLength { + position, + expected: versions.len(), + actual: proof.results.len(), + })?; + let ordering = ladder::interpret_search_ladder(used, target, &proof.results)?; + if ordering != core::cmp::Ordering::Equal { + return Err(UpdateError::NewVersionInconsistent { + position, + claimed: target, + }); + } + let entries = ladder_entries(keys, position, used, &proof.results)?; + established.record(position, used, &proof.results); + let root = prefix::evaluate(suite, &entries, proof)?; + reader.establish_root(position, root)?; + Ok(root) +} + +/// Turns a ladder's versions and results into the prefix tree lookups they stand for. +fn ladder_entries( + keys: &BTreeMap, + position: u64, + versions: &[u32], + results: &[PrefixSearchResult], +) -> core::result::Result, UpdateError> { + let mut entries = Vec::new(); + for (version, result) in versions.iter().zip(results.iter()) { + let key = keys.get(version).ok_or(UpdateError::MissingLadderKey { + position, + version: *version, + })?; + entries.push(if result.is_inclusion() { + prefix::SearchEntry::included( + key.vrf_output, + key.commitment.ok_or(UpdateError::MissingCommitment { + position, + version: *version, + })?, + ) + } else { + prefix::SearchEntry::absent(key.vrf_output) + }); + } + Ok(entries) +} + +/// The first entry on the path from the root towards `target` that is not distinguished, or `None` +/// if `target` itself is distinguished (§9.1 steps 1 and 3). +/// +/// §6.1's ancestor-closure is what makes one descent enough: a non-distinguished entry has no +/// distinguished descendants, so the first one found on the way down settles every entry below it. +/// The timestamps are read through the reader in descent order, which is the order §12.3.5 lists +/// them in — "the timestamp of each log entry on the path from the root to the parent … stopping +/// if a non-distinguished log entry is established". +fn non_distinguished_ancestor( + size: u64, + window: u64, + target: u64, + reader: &mut Reader<'_>, +) -> core::result::Result, UpdateError> { + let last = size.saturating_sub(1); + let mut current = ibst::root(size)?; + // The brackets §6.1 measures the gap between. The left one starts at the epoch rather than at + // an entry: there is nothing to the left of the tree, and a log's first entry is distinguished + // by construction. + let mut left = (0_u64, 0_u64); + let mut right = (last, reader.timestamp(last)?); + + loop { + if !distinguished::is_distinguished(window, left, right)? { + return Ok(Some(current)); + } + if current == target { + return Ok(None); + } + let timestamp = reader.timestamp(current)?; + // Which bracket a step tightens depends on which way it goes: descending right raises the + // left bracket to this entry, descending left lowers the right one. + if current < target { + let next = ibst::right(current, size)?; + left = (current, timestamp); + current = next; + } else { + let next = ibst::left(current)?; + right = (current, timestamp); + current = next; + } + } +} + +/// Builds the search keys for an update's binary ladder, making §13.5 step 4's checks (§13.5). +/// +/// Step 4 wants three things verified about `binary_ladder`: that "the expected number of entries +/// is present", that every proof evaluates to a VRF output — the caller's job, since that needs +/// the log's public key — and that "no commitment is provided for any version greater than +/// `greatest_version`". The last is the interesting one. A commitment for a version above what the +/// owner advertised would be the log volunteering a value the owner never asked about and cannot +/// check, so §13.5 forbids it outright rather than leaving it to the algorithm. +/// +/// `outputs` are the VRF outputs the caller evaluated, in the ladder's order; `commitments` are the +/// ladder's `commitment` fields in the same order. +/// +/// # Errors +/// +/// [`UpdateError::LadderCount`] if the ladder is not the length §9.1 calls for, or +/// [`UpdateError::UnexpectedCommitment`] for a commitment above `advertised`. +pub fn update_ladder_keys( + start_ver: u32, + end_ver: u32, + advertised: Option, + outputs: &[HashValue], + commitments: &[Option], +) -> core::result::Result, UpdateError> { + let versions = ladder::update_binary_ladder(start_ver, end_ver)?; + if outputs.len() != versions.len() || commitments.len() != versions.len() { + return Err(UpdateError::LadderCount { + expected: versions.len(), + actual: outputs.len().max(commitments.len()), + }); + } + let mut keys = BTreeMap::new(); + for ((version, output), commitment) in versions.iter().zip(outputs.iter()).zip(commitments) { + if commitment.is_some() && advertised.is_none_or(|greatest| *version > greatest) { + return Err(UpdateError::UnexpectedCommitment { version: *version }); + } + keys.insert( + *version, + LadderKey { + vrf_output: *output, + commitment: *commitment, + }, + ); + } + Ok(keys) +} + +/// Why an update was rejected (§9.1, §13.5). +#[derive(Clone, Debug, PartialEq, Eq)] +#[non_exhaustive] +pub enum UpdateError { + /// The claimed position is not in the tree. + EntryDoesNotExist { + /// The position claimed. + position: u64, + /// The tree size. + size: u64, + }, + /// §13.5 step 1: the position is not to the right of the owner's last known version. + PositionNotAdvancing { + /// The position claimed. + position: u64, + /// Where the owner's last known version was inserted. + last_update: u64, + }, + /// §13.5 step 2: `info` was empty, so the response reports no new versions at all. + NoNewVersions, + /// The version count does not fit the `uint32` version field. + /// + /// Also reported for a new greatest version of `u32::MAX`, which no ladder can establish: the + /// proof would need a lookup for version `2^32`. + VersionOverflow, + /// A step 2.2 ladder does not agree with the owner's previous greatest version. + PreviousTreeInconsistent { + /// The entry whose ladder disagreed. + position: u64, + /// The version the owner expected to be greatest there. + claimed: u32, + /// Whether the owner believed the label existed at all. + exists: bool, + }, + /// Step 4's ladder does not place the new greatest version where the response claims. + NewVersionInconsistent { + /// The entry holding the new versions. + position: u64, + /// The new greatest version. + claimed: u32, + }, + /// A ladder carried more results than it has lookups. + LadderLength { + /// The entry. + position: u64, + /// How many lookups the ladder has. + expected: usize, + /// How many results arrived. + actual: usize, + }, + /// The proof for the versions outside the ladder is the wrong length (§9.1 steps 3 and 4). + AdditionalLength { + /// The entry holding the new versions. + position: u64, + /// How many versions were outside the ladder. + expected: usize, + /// How many results arrived. + actual: usize, + }, + /// A version the update claims to have created is not in the prefix tree. + VersionNotCreated { + /// The entry holding the new versions. + position: u64, + /// The version whose lookup came back as non-inclusion. + version: u32, + }, + /// §13.5 step 4: `binary_ladder` is not the length §9.1 calls for. + LadderCount { + /// How many steps §9.1's version set calls for. + expected: usize, + /// How many arrived. + actual: usize, + }, + /// §13.5 step 4: a commitment for a version above the owner's advertised greatest version. + UnexpectedCommitment { + /// The version that carried one. + version: u32, + }, + /// A version in a ladder has no entry in the response's `binary_ladder`. + MissingLadderKey { + /// The entry being inspected. + position: u64, + /// The version with no search key. + version: u32, + }, + /// A version proven included has no commitment, so its leaf cannot be reconstructed. + MissingCommitment { + /// The entry being inspected. + position: u64, + /// The version with no commitment. + version: u32, + }, + /// The previous tree's frontier walk found an entry with no parent, which cannot happen. + NoParent { + /// The entry. + position: u64, + }, + /// The proof did not supply something the algorithm asked for, or supplied too much. + Proof(Error), + /// A prefix tree proof did not evaluate. + Prefix(prefix::Error), + /// The search tree rejected a position. + Ibst(ibst::Error), + /// A ladder could not be built or interpreted. + Ladder(ladder::Error), + /// Deciding whether an entry is distinguished failed. + Distinguished(distinguished::Error), +} + +impl From for UpdateError { + fn from(err: Error) -> Self { + Self::Proof(err) + } +} + +impl From for UpdateError { + fn from(err: prefix::Error) -> Self { + Self::Prefix(err) + } +} + +impl From for UpdateError { + fn from(err: ibst::Error) -> Self { + Self::Ibst(err) + } +} + +impl From for UpdateError { + fn from(err: ladder::Error) -> Self { + Self::Ladder(err) + } +} + +impl From for UpdateError { + fn from(err: distinguished::Error) -> Self { + Self::Distinguished(err) + } +} + +impl core::fmt::Display for UpdateError { + fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { + match self { + Self::EntryDoesNotExist { position, size } => { + write!(f, "log entry {position} does not exist in a log of {size}") + } + Self::PositionNotAdvancing { + position, + last_update, + } => write!( + f, + "new versions claimed at log entry {position}, which is not to the right of \ + entry {last_update} where the owner's last version was inserted" + ), + Self::NoNewVersions => write!(f, "the response reports no new versions"), + Self::VersionOverflow => { + write!(f, "the new greatest version does not fit a uint32 version") + } + Self::PreviousTreeInconsistent { + position, + claimed, + exists, + } => { + if *exists { + write!( + f, + "log entry {position} does not show version {claimed} as the greatest \ + that existed before the update" + ) + } else { + write!( + f, + "log entry {position} shows a version of a label the owner believed did \ + not exist" + ) + } + } + Self::NewVersionInconsistent { position, claimed } => write!( + f, + "log entry {position} does not show version {claimed} as the new greatest version" + ), + Self::LadderLength { + position, + expected, + actual, + } => write!( + f, + "log entry {position} answered a ladder of {expected} lookups with {actual} results" + ), + Self::AdditionalLength { + position, + expected, + actual, + } => write!( + f, + "log entry {position} answered {expected} new-version lookups with {actual} results" + ), + Self::VersionNotCreated { position, version } => write!( + f, + "version {version} is claimed to have been created in log entry {position} but is \ + not present there" + ), + Self::LadderCount { expected, actual } => write!( + f, + "the update's binary ladder should have {expected} steps but has {actual}" + ), + Self::UnexpectedCommitment { version } => write!( + f, + "a commitment was provided for version {version}, which is above the greatest \ + version the owner advertised" + ), + Self::MissingLadderKey { position, version } => write!( + f, + "no search key for version {version}, needed at log entry {position}" + ), + Self::MissingCommitment { position, version } => write!( + f, + "no commitment for version {version}, proven to exist at log entry {position}" + ), + Self::NoParent { position } => { + write!(f, "log entry {position} has no parent in the previous tree") + } + Self::Proof(err) => write!(f, "reading the proof: {err}"), + Self::Prefix(err) => write!(f, "evaluating a prefix tree proof: {err}"), + Self::Ibst(err) => write!(f, "walking the search tree: {err}"), + Self::Ladder(err) => write!(f, "building a binary ladder: {err}"), + Self::Distinguished(err) => write!(f, "checking distinguished entries: {err}"), + } + } +} + +impl core::error::Error for UpdateError { + fn source(&self) -> Option<&(dyn core::error::Error + 'static)> { + match self { + Self::Proof(err) => Some(err), + Self::Prefix(err) => Some(err), + Self::Ibst(err) => Some(err), + Self::Ladder(err) => Some(err), + Self::Distinguished(err) => Some(err), + _ => None, + } + } +} + +#[cfg(test)] +#[allow( + clippy::arithmetic_side_effects, + clippy::indexing_slicing, + clippy::unwrap_used, + reason = "tests fail loudly by panicking; the lints protect the library paths" +)] +mod update_tests { + use super::search_tests::{commitment_for, key_for}; + use super::*; + use kt_wire::proofs::{InclusionProof, PrefixLeaf}; + + const SUITE: CipherSuite = CipherSuite::Kt128Sha256Ed25519; + + /// A window far wider than the log's whole span, so only the root is distinguished. This is + /// the ordinary case for a log whose entries arrive close together, and the one where §9.1 + /// step 4 applies. + const WEEK: u64 = 604_800_000; + + /// A log where version `i` of the label was created in entry `layout[i]`. + /// + /// The other test modules use a log where entry `v` holds versions `0..=v`, which cannot + /// express what §9.1 is about: an owner whose versions went into particular entries, with + /// entries in between that added nothing. So this one takes the layout explicitly. + struct Log { + size: u64, + layout: Vec, + stamps: Vec, + } + + impl Log { + fn new(size: u64, layout: &[u64]) -> Self { + Self { + size, + layout: layout.to_vec(), + stamps: (0..size).map(|i| 1_700_000_000_000 + i).collect(), + } + } + + fn last(&self) -> u64 { + self.size - 1 + } + + fn stamp(&self, entry: u64) -> u64 { + self.stamps[entry as usize] + } + + /// The greatest version present in entry `entry`, or `None` if the label is not there yet. + fn greatest_at(&self, entry: u64) -> Option { + let created = self.layout.iter().filter(|at| **at <= entry).count(); + u32::try_from(created).unwrap().checked_sub(1) + } + + /// The prefix tree of entry `entry`: every version created at or before it. + fn tree_at(&self, entry: u64) -> prefix::PrefixTree { + let mut tree = prefix::PrefixTree::new(); + for (version, at) in self.layout.iter().enumerate() { + if *at <= entry { + tree.insert(PrefixLeaf { + vrf_output: key_for(u32::try_from(version).unwrap()), + commitment: commitment_for(u32::try_from(version).unwrap()), + }) + .unwrap(); + } + } + tree + } + + /// The descent §9.1 steps 1 and 3 make: which entries' timestamps a verifier reads on the + /// way towards `target`, and the first entry it finds that is not distinguished. + fn descend(&self, window: u64, target: u64) -> (Vec, Option) { + let last = self.last(); + let mut reads = vec![last]; + let mut current = ibst::root(self.size).unwrap(); + let mut left = (0_u64, 0_u64); + let mut right = (last, self.stamp(last)); + loop { + if !distinguished::is_distinguished(window, left, right).unwrap() { + return (reads, Some(current)); + } + if current == target { + return (reads, None); + } + reads.push(current); + let timestamp = self.stamp(current); + if current < target { + let next = ibst::right(current, self.size).unwrap(); + left = (current, timestamp); + current = next; + } else { + let next = ibst::left(current).unwrap(); + right = (current, timestamp); + current = next; + } + } + } + } + + /// Every search key the owner could need: the versions in the layout exist and so have + /// commitments, and the versions above it are looked up but do not. + fn keys_for(log: &Log) -> BTreeMap { + let created = u32::try_from(log.layout.len()).unwrap(); + let mut keys = BTreeMap::new(); + for version in 0..created.saturating_add(16) { + keys.insert( + version, + LadderKey { + vrf_output: key_for(version), + commitment: (version < created).then(|| commitment_for(version)), + }, + ); + } + keys + } + + fn push(entry: u64, log: &Log, timestamps: &mut Vec, seen: &mut Vec) { + if !seen.contains(&entry) { + timestamps.push(log.stamp(entry)); + seen.push(entry); + } + } + + /// The proof a log would serve for §9.1, built by walking the same steps and asking each + /// entry's real prefix tree for exactly the lookups that step would make. + /// + /// The ladders here are indexed on the greatest version *at that entry*, not on the target, + /// which is what makes the results a prefix of the verifier's ladder rather than a match for + /// it — the same asymmetry as in a greatest-version search. + fn build_update( + log: &Log, + window: u64, + position: u64, + versions: usize, + owner: &OwnerState, + ) -> CombinedTreeProof { + let mut timestamps = Vec::new(); + let mut seen = Vec::new(); + let mut proofs = Vec::new(); + let mut with_proofs: Vec = Vec::new(); + + // The view update supplies the frontier's timestamps before the algorithm starts. + for entry in ibst::frontier(log.size).unwrap() { + push(entry, log, &mut timestamps, &mut seen); + } + + let previous_greatest = owner.greatest_version().unwrap(); + let start_ver = previous_greatest.map_or(0, |version| version + 1); + let end_ver = start_ver + u32::try_from(versions).unwrap() - 1; + let mut established = Established::default(); + + // Phase one, over the previous tree. + let rightmost = position - 1; + let (reads, first) = log.descend(window, rightmost); + for entry in reads { + push(entry, log, &mut timestamps, &mut seen); + } + if let Some(first) = first { + let mut current = first; + while current > rightmost { + current = ibst::left(current).unwrap(); + } + if let Some(greatest) = previous_greatest { + let ladder = ladder::search_binary_ladder(greatest, greatest, &[], &[]).unwrap(); + if current != ibst::root(position).unwrap() { + let parent = ibst::direct_path(current, position).unwrap()[0]; + established.assume(parent, owner.greatest_version_at(parent).unwrap(), &ladder); + } + established.assume(current, Some(greatest), &ladder); + } + // Step 2.1 as the current text has it: nothing is skipped unless a previous version + // existed, in which case entries at or before where it was inserted are. + let skip_through = previous_greatest.map(|_| owner.last_update()); + loop { + if skip_through.is_none_or(|through| current > through) { + let target = previous_greatest.unwrap_or(0); + let (left_inclusion, right_non_inclusion) = established.sets_for(current); + // A label with no versions at this entry cannot be expressed as a `greatest` + // — the field is a `u32` and every value of it means some version exists — + // so the honest ladder is written out directly: one lookup for version 0, + // which comes back absent and ends it. This is the log-side face of + // `DRAFT-08`. + let asked = match log.greatest_at(current) { + Some(greatest) => ladder::search_binary_ladder( + target, + greatest, + &left_inclusion, + &right_non_inclusion, + ) + .unwrap(), + None => vec![0], + }; + let searches: Vec = asked.iter().map(|v| key_for(*v)).collect(); + let proof = log.tree_at(current).prove(SUITE, &searches).unwrap(); + established.record(current, &asked, &proof.results); + push(current, log, &mut timestamps, &mut seen); + proofs.push(proof); + with_proofs.push(current); + } + if current == rightmost { + break; + } + current = ibst::right(current, position).unwrap(); + } + } + + // Phase two. + let (reads, first) = log.descend(window, position); + for entry in reads { + push(entry, log, &mut timestamps, &mut seen); + } + if first.is_some() { + let (left_inclusion, right_non_inclusion) = established.sets_for(position); + let asked = ladder::search_binary_ladder( + end_ver, + log.greatest_at(position).unwrap_or(0), + &left_inclusion, + &right_non_inclusion, + ) + .unwrap(); + let searches: Vec = asked.iter().map(|v| key_for(*v)).collect(); + let proof = log.tree_at(position).prove(SUITE, &searches).unwrap(); + established.record(position, &asked, &proof.results); + push(position, log, &mut timestamps, &mut seen); + proofs.push(proof); + with_proofs.push(position); + } + + // Steps 3 and 4's proof of the new versions the ladder did not cover. + let covered = ladder::search_binary_ladder(end_ver, end_ver, &[], &[]).unwrap(); + let additional: Vec = (start_ver..=end_ver) + .filter(|version| !covered.contains(version)) + .collect(); + if !additional.is_empty() { + let searches: Vec = additional.iter().map(|v| key_for(*v)).collect(); + push(position, log, &mut timestamps, &mut seen); + proofs.push(log.tree_at(position).prove(SUITE, &searches).unwrap()); + with_proofs.push(position); + } + + // §12.3.2: a prefix tree root for each entry whose timestamp was supplied but which no + // proof reconstructs, in ascending order of position. + let mut owed: Vec = seen + .iter() + .copied() + .filter(|entry| !with_proofs.contains(entry)) + .collect(); + owed.sort_unstable(); + let prefix_roots = owed + .iter() + .map(|entry| log.tree_at(*entry).root(SUITE)) + .collect(); + + CombinedTreeProof { + timestamps, + prefix_proofs: proofs, + prefix_roots, + inclusion: InclusionProof::new(Vec::new()), + } + } + + /// Runs a verifier over a built proof, consuming the view update's timestamps first and + /// checking that nothing is left over afterwards. + fn verify( + log: &Log, + proof: &CombinedTreeProof, + window: u64, + position: u64, + versions: usize, + owner: &OwnerState, + ) -> core::result::Result { + let retained = Retained::none(); + let mut reader = Reader::new(proof, &retained); + for entry in ibst::frontier(log.size).unwrap() { + reader.timestamp(entry).unwrap(); + } + let updated = owner_update( + SUITE, + log.size, + window, + position, + versions, + owner, + &keys_for(log), + &mut reader, + )?; + for entry in reader.entries_owed_roots() { + reader.prefix_root(entry).unwrap(); + } + reader.finish()?; + Ok(updated) + } + + /// A whole honest update: one new version, in a non-distinguished entry, with a previous tree + /// whose frontier still has to be checked. Every part of §9.1 is exercised — step 1's descent, + /// step 2's ladder over the previous tree, step 4's ladder at the new entry — and §12.3's + /// exact-count rule confirms the reading, since the proof is consumed to the last element. + #[test] + fn an_honest_update_consumes_the_proof_exactly() { + // Version 0 was created in entry 0; the update puts version 1 in entry 5. + let log = Log::new(7, &[0, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + let proof = build_update(&log, WEEK, 5, 1, &owner); + let updated = verify(&log, &proof, WEEK, 5, 1, &owner).unwrap(); + + assert!(!updated.distinguished, "entry 5 is not distinguished here"); + assert_eq!(updated.root, Some(log.tree_at(5).root(SUITE))); + // Step 2 checked the previous tree's rightmost entry, which is entry 4. + assert_eq!(updated.previous, vec![(4, log.tree_at(4).root(SUITE))]); + // Step 4's contact map entry: the new greatest version, at the entry holding it. + assert_eq!(updated.contact, Some((5, 1))); + assert_eq!(updated.owner.upcoming, vec![5]); + assert_eq!(updated.owner.greatest_version().unwrap(), Some(1)); + } + + /// The reason §9.1 has a first phase at all. The log created version 1 in entry 4 — inside the + /// previous tree, without telling the owner — and now presents the update as if version 1 were + /// new in entry 5. Step 2's ladder over entry 4 shows version 1 already there, which is + /// exactly the misbehaviour §9.1 describes: "create a malicious version of a label, later + /// change the corresponding value to something non-malicious, and try to only inform the label + /// owner of the non-malicious value". + #[test] + fn a_version_hidden_in_the_previous_tree_is_caught() { + let honest = Log::new(7, &[0, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + // The log's real history, which the owner has not been told about. + let hidden = Log::new(7, &[0, 4]); + let proof = build_update(&hidden, WEEK, 5, 1, &owner); + let refused = verify(&honest, &proof, WEEK, 5, 1, &owner); + assert_eq!( + refused, + Err(UpdateError::PreviousTreeInconsistent { + position: 4, + claimed: 0, + exists: true, + }) + ); + } + + /// Step 2.1's skip. The owner's last version went into entry 4, which is also the previous + /// tree's rightmost entry, so the ladder for it arrived with *that* update and this response + /// carries none. The walk still passes through the entry — its omissions still count towards + /// step 4's ladder — but nothing is read from it, and the proof is consumed exactly. + #[test] + fn an_entry_already_laddered_is_skipped() { + let log = Log::new(7, &[0, 4, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: vec![4], + }; + assert_eq!(owner.last_update(), 4); + let proof = build_update(&log, WEEK, 5, 1, &owner); + let updated = verify(&log, &proof, WEEK, 5, 1, &owner).unwrap(); + assert!(updated.previous.is_empty(), "{:?}", updated.previous); + assert_eq!(updated.contact, Some((5, 2))); + assert_eq!(updated.owner.upcoming, vec![4, 5]); + } + + /// A multi-version update, which is the only case where §9.1's "additional" proof carries + /// anything. Creating versions 1, 2 and 3 at once means a search ladder for version 3 covers + /// 1 and 3 but not 2, so version 2 gets its own inclusion proof — from the same entry, which + /// makes this the first algorithm to lean on §12.3.4's rule that two proofs for one log entry + /// must agree about its prefix tree root. + #[test] + fn a_multi_version_update_proves_the_versions_the_ladder_misses() { + let log = Log::new(7, &[0, 5, 5, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + let covered = ladder::search_binary_ladder(3, 3, &[], &[]).unwrap(); + assert!(!covered.contains(&2), "version 2 should need its own proof"); + + let proof = build_update(&log, WEEK, 5, 3, &owner); + // Two proofs from entry 5: step 4's ladder and the additional lookups. + assert_eq!(proof.prefix_proofs.len(), 3); + let updated = verify(&log, &proof, WEEK, 5, 3, &owner).unwrap(); + assert_eq!(updated.root, Some(log.tree_at(5).root(SUITE))); + assert_eq!(updated.contact, Some((5, 3))); + assert_eq!(updated.owner.upcoming, vec![5, 5, 5]); + assert_eq!(updated.owner.greatest_version().unwrap(), Some(3)); + } + + /// A version the update claims to have created, but which is not in the prefix tree. This is + /// the check that makes the additional proof worth sending: without it a log could claim three + /// new versions and insert two. + #[test] + fn a_claimed_version_that_was_not_created_is_caught() { + let honest = Log::new(7, &[0, 5, 5, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + // Version 2 was never inserted, though 1 and 3 were. + let mut proof = build_update(&honest, WEEK, 5, 3, &owner); + let short = Log::new(7, &[0, 5, 6, 5]); + let additional = [key_for(2)]; + proof.prefix_proofs[2] = short.tree_at(5).prove(SUITE, &additional).unwrap(); + let refused = verify(&honest, &proof, WEEK, 5, 3, &owner); + assert_eq!( + refused, + Err(UpdateError::VersionNotCreated { + position: 5, + version: 2, + }) + ); + } + + /// With every entry distinguished — a window of zero — §9.1 has almost nothing to do: step 1 + /// finds no non-distinguished entry so step 2 is skipped entirely, and step 3 asks only for the + /// new versions a ladder would miss. The response carries no ladder at all. + /// + /// This is the degenerate shape, recorded deliberately: it passes while testing very little, + /// and mistaking it for coverage is how a verifier ends up never exercising step 4. + #[test] + fn a_distinguished_entry_needs_no_ladder() { + let log = Log::new(7, &[0, 5, 5, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + let proof = build_update(&log, 0, 5, 3, &owner); + // One proof only: the additional lookup for version 2. + assert_eq!(proof.prefix_proofs.len(), 1); + let updated = verify(&log, &proof, 0, 5, 3, &owner).unwrap(); + assert!(updated.distinguished); + assert!(updated.previous.is_empty()); + assert_eq!(updated.contact, None, "owner monitoring will reach entry 5"); + assert_eq!(updated.root, Some(log.tree_at(5).root(SUITE))); + } + + /// The most degenerate case of all, and a legitimate one: a distinguished entry whose single + /// new version a search ladder already covers. §9.1 asks for nothing, and the whole response + /// is a tree head plus the view update. Worth pinning, because a verifier that insists on + /// consuming at least one prefix proof would reject it. + #[test] + fn a_distinguished_single_version_update_asks_for_nothing() { + let log = Log::new(7, &[0, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: Vec::new(), + }; + let proof = build_update(&log, 0, 5, 1, &owner); + assert!(proof.prefix_proofs.is_empty()); + let updated = verify(&log, &proof, 0, 5, 1, &owner).unwrap(); + assert!(updated.distinguished); + assert_eq!(updated.root, None); + assert!(updated.previous.is_empty()); + } + + /// §13.5 step 1: a new version cannot appear at or to the left of one the owner already knows + /// about. The prefix tree only grows, so a log claiming otherwise is describing a tree that + /// cannot exist. + #[test] + fn a_position_that_does_not_advance_is_refused() { + let log = Log::new(7, &[0, 4]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(0), + upcoming: vec![4], + }; + let proof = CombinedTreeProof { + timestamps: Vec::new(), + prefix_proofs: Vec::new(), + prefix_roots: Vec::new(), + inclusion: InclusionProof::new(Vec::new()), + }; + let retained = Retained::none(); + let mut reader = Reader::new(&proof, &retained); + assert_eq!( + owner_update( + SUITE, + log.size, + WEEK, + 4, + 1, + &owner, + &keys_for(&log), + &mut reader + ), + Err(UpdateError::PositionNotAdvancing { + position: 4, + last_update: 4, + }) + ); + assert_eq!( + owner_update( + SUITE, + log.size, + WEEK, + 9, + 1, + &owner, + &keys_for(&log), + &mut reader + ), + Err(UpdateError::EntryDoesNotExist { + position: 9, + size: 7, + }) + ); + // §13.5 step 2 requires `info` to be non-empty: a response reporting no new versions has + // nothing to verify and cannot be treated as a successful update. + assert_eq!( + owner_update( + SUITE, + log.size, + WEEK, + 5, + 0, + &owner, + &keys_for(&log), + &mut reader + ), + Err(UpdateError::NoNewVersions) + ); + } + + /// The `uint32` version field again. An owner at `u32::MAX - 1` creating one more version + /// would take the greatest version to `u32::MAX`, which no ladder can establish: proving it is + /// the greatest needs a non-inclusion proof for version `2^32`. + #[test] + fn a_version_at_the_ceiling_is_refused() { + let log = Log::new(7, &[0, 5]); + let owner = OwnerState { + starting: 3, + version_at_starting: Some(u32::MAX - 1), + upcoming: Vec::new(), + }; + let proof = CombinedTreeProof { + timestamps: Vec::new(), + prefix_proofs: Vec::new(), + prefix_roots: Vec::new(), + inclusion: InclusionProof::new(Vec::new()), + }; + let retained = Retained::none(); + let mut reader = Reader::new(&proof, &retained); + assert_eq!( + owner_update( + SUITE, + log.size, + WEEK, + 5, + 1, + &owner, + &keys_for(&log), + &mut reader + ), + Err(UpdateError::VersionOverflow) + ); + } + + /// An owner's own arithmetic. The awkward case is a label that did not exist at the reference + /// point: the versions created since then run from zero, so two of them means the greatest is + /// version 1 — and none of them means the label still does not exist. + #[test] + fn owner_state_counts_versions_from_where_it_started() { + let fresh = OwnerState { + starting: 3, + version_at_starting: None, + upcoming: vec![4, 4, 6], + }; + assert_eq!(fresh.greatest_version().unwrap(), Some(2)); + assert_eq!(fresh.greatest_version_at(3).unwrap(), None); + assert_eq!(fresh.greatest_version_at(4).unwrap(), Some(1)); + assert_eq!(fresh.greatest_version_at(5).unwrap(), Some(1)); + assert_eq!(fresh.greatest_version_at(6).unwrap(), Some(2)); + assert_eq!(fresh.last_update(), 6); + + let empty = OwnerState { + starting: 3, + version_at_starting: None, + upcoming: Vec::new(), + }; + assert_eq!(empty.greatest_version().unwrap(), None); + assert_eq!(empty.last_update(), 3, "no updates, so the starting entry"); + + let established = OwnerState { + starting: 3, + version_at_starting: Some(7), + upcoming: vec![5], + }; + assert_eq!(established.greatest_version().unwrap(), Some(8)); + assert_eq!(established.greatest_version_at(4).unwrap(), Some(7)); + + let overflowing = OwnerState { + starting: 3, + version_at_starting: Some(u32::MAX), + upcoming: vec![5], + }; + assert_eq!( + overflowing.greatest_version(), + Err(UpdateError::VersionOverflow) + ); + } + + /// §13.5 step 4's two checks on `binary_ladder`, which are about the response's shape rather + /// than the proof: the right number of steps, and no commitment for a version above what the + /// owner said it knew. A commitment above that is the log volunteering a value the owner never + /// asked about and has no way to check. + #[test] + fn the_response_ladder_is_checked_for_shape() { + let versions = ladder::update_binary_ladder(1, 1).unwrap(); + assert_eq!(versions, vec![2, 3]); + + let outputs: Vec = versions.iter().map(|v| key_for(*v)).collect(); + let none = vec![None; versions.len()]; + let keys = update_ladder_keys(1, 1, Some(0), &outputs, &none).unwrap(); + assert_eq!(keys.len(), 2); + assert!(keys[&2].commitment.is_none()); + + assert_eq!( + update_ladder_keys(1, 1, Some(0), &outputs[..1], &none[..1]), + Err(UpdateError::LadderCount { + expected: 2, + actual: 1, + }) + ); + // A commitment for version 2, which is above the advertised greatest version of 0. + let volunteered = vec![Some(commitment_for(2)), None]; + assert_eq!( + update_ladder_keys(1, 1, Some(0), &outputs, &volunteered), + Err(UpdateError::UnexpectedCommitment { version: 2 }) + ); + // An owner that advertised no version at all cannot be sent any commitment. + assert_eq!( + update_ladder_keys(1, 1, None, &outputs, &volunteered), + Err(UpdateError::UnexpectedCommitment { version: 2 }) + ); + } + + #[test] + fn update_errors_describe_themselves() { + use alloc::string::ToString; + let errors = [ + UpdateError::EntryDoesNotExist { + position: 9, + size: 7, + }, + UpdateError::PositionNotAdvancing { + position: 4, + last_update: 4, + }, + UpdateError::NoNewVersions, + UpdateError::VersionOverflow, + UpdateError::PreviousTreeInconsistent { + position: 4, + claimed: 0, + exists: true, + }, + UpdateError::PreviousTreeInconsistent { + position: 4, + claimed: 0, + exists: false, + }, + UpdateError::NewVersionInconsistent { + position: 5, + claimed: 1, + }, + UpdateError::LadderLength { + position: 5, + expected: 2, + actual: 1, + }, + UpdateError::AdditionalLength { + position: 5, + expected: 1, + actual: 0, + }, + UpdateError::VersionNotCreated { + position: 5, + version: 2, + }, + UpdateError::LadderCount { + expected: 2, + actual: 1, + }, + UpdateError::UnexpectedCommitment { version: 2 }, + UpdateError::MissingLadderKey { + position: 5, + version: 2, + }, + UpdateError::MissingCommitment { + position: 5, + version: 2, + }, + UpdateError::NoParent { position: 4 }, + UpdateError::from(Error::Exhausted { + array: "prefix_proofs", + position: 5, + }), + UpdateError::from(prefix::Error::DepthOverflow { depth: 256 }), + UpdateError::from(ibst::Error::EmptyLog), + UpdateError::from(ladder::Error::UnrepresentableRung { + rung: 1 << 33, + greatest: u32::MAX, + }), + UpdateError::from(distinguished::Error::MissingTimestamp { position: 1 }), + ]; + for error in &errors { + assert!(!error.to_string().is_empty(), "{error:?}"); + } + assert!(core::error::Error::source(&errors[0]).is_none()); + for error in &errors[15..] { + assert!(core::error::Error::source(error).is_some(), "{error:?}"); + } + } + + /// Step 2.1's condition on a previous version having existed, which is the one thing the + /// 2026-07-28 rewording changed. The owner is creating a label's first version from a + /// reference point at entry 3, and the previous tree's frontier reaches back past it — so + /// under the current text entry 3 is inspected, where the earlier text (and the peer) would + /// have skipped it as "already laddered" by an update that never happened. + #[test] + fn a_first_version_skips_nothing_in_the_previous_tree() { + // dave's label is created in entry 6; nothing exists before it. + let log = Log::new(7, &[6]); + let owner = OwnerState { + starting: 5, + version_at_starting: None, + upcoming: Vec::new(), + }; + assert_eq!( + owner.last_update(), + 5, + "the reference point, with no updates" + ); + + let proof = build_update(&log, WEEK, 6, 1, &owner); + let updated = verify(&log, &proof, WEEK, 6, 1, &owner).unwrap(); + // Entry 5 is at the owner's reference point, so the superseded reading would have read + // nothing here. The current one inspects it, and the proof carries the ladder for it. + assert_eq!(updated.previous, vec![(5, log.tree_at(5).root(SUITE))]); + assert_eq!(updated.contact, Some((6, 0))); + } +} diff --git a/crates/kt-tree/src/ibst.rs b/crates/kt-tree/src/ibst.rs index ea8d627..aeb75dd 100644 --- a/crates/kt-tree/src/ibst.rs +++ b/crates/kt-tree/src/ibst.rs @@ -572,9 +572,17 @@ pub fn direct_path(x: u64, size: u64) -> Result> { /// outwards is what makes the sequence monotonic, which is the property the user is /// checking. /// -/// Returns an empty sequence when the user is already up to date — and also, per the -/// procedure as written, in a case where they are *not*: see -/// [`leaves_right_edge_unchecked`]. +/// Returns an empty sequence only when the user is already up to date. +/// +/// # The restart, and why the peer does not do it +/// +/// The clause "unless there were no such log entries, in which case the log entry with +/// index `size-1` lies on the new tree's frontier" was added on 2026-07-28. Before it, +/// the walk had nothing to start from when the direct path contributed nothing, and the +/// procedure yielded *no entries at all* even though the log had grown — the hole +/// recorded as `DRAFT-06`. The Go peer implements the earlier text and is preserved here +/// as [`update_view_ancestors_only`], because a proof the peer built is ordered by the +/// procedure the peer ran: consuming one requires asking in its order, not this one. /// /// # Errors /// @@ -603,15 +611,73 @@ pub fn update_view(size: u64, advertised: Option) -> Result> { .collect(); // Root-downwards becomes closest-ancestor-first. ancestors.reverse(); + if ancestors.is_empty() { + // §4.2's restart: "unless there were no such log entries, in which case + // the log entry with index `size-1` lies on the new tree's frontier". + // `from` is that entry — the rightmost the user retained — and it is on + // the new frontier precisely when the filter above removed everything, + // since a frontier entry's ancestors all have smaller indices. + alloc::vec![from] + } else { + ancestors + } + } + }; + + // Continue along the frontier to the rightmost entry. §4.2: "Starting from this + // frontier log entry, compute the remainder of the frontier. That is, compute the + // log entry's right child, the right child's right child, and so on." + while out.last().is_some_and(|node| *node != last) { + let Some(node) = out.last().copied() else { + break; + }; + out.push(right(node, size)?); + } + Ok(out) +} + +/// §4.2 as it read before 2026-07-28, which is what the Go peer implements. +/// +/// Identical to [`update_view`] except that it does not restart the frontier walk from +/// the user's own rightmost entry: where the direct-path filter removes everything, this +/// returns an empty sequence. That is `DRAFT-06`, and it is not a rare corner — see +/// [`ancestors_only_leaves_right_edge_unchecked`]. +/// +/// This is not offered as an alternative reading of the current text. It exists because +/// a `CombinedTreeProof` carries its elements "in the order that the algorithm the user +/// is executing would request them" (§12.3), so the order a proof was *built* in is the +/// order it must be *read* in. Consuming a proof from a log that runs the earlier +/// procedure means asking in the earlier procedure's order; asking in the current one +/// assigns the wrong timestamp to the wrong entry, which the monotonicity check then +/// rejects. Measured: adopting the current text alone turns 31 checks against the pinned +/// peer from agreements into disagreements, one of them a live proof. +/// +/// # Errors +/// +/// [`Error::EmptyLog`] if `size` is 0, or [`Error::IndexOutOfRange`] if `advertised` +/// exceeds `size`. +pub fn update_view_ancestors_only(size: u64, advertised: Option) -> Result> { + let last = root(size).and(Ok(size.saturating_sub(1)))?; + + let mut out = match advertised { + Some(previous) if previous == size => return Ok(Vec::new()), + Some(previous) if previous > size => { + return Err(Error::IndexOutOfRange { + index: previous, + size, + }); + } + None | Some(0) => alloc::vec![root(size)?], + Some(previous) => { + let mut ancestors: Vec = direct_path(previous.saturating_sub(1), size)? + .into_iter() + .filter(|ancestor| *ancestor >= previous) + .collect(); + ancestors.reverse(); ancestors } }; - // Continue along the frontier to the rightmost entry. §4.2: "The last of these - // log entries will lie on the new tree's frontier. From this log entry, compute - // the remainder of the frontier." With nothing to start from there is nothing to - // continue, which is where the gap documented on - // `leaves_right_edge_unchecked` comes from. while out.last().is_some_and(|node| *node != last) { let Some(node) = out.last().copied() else { break; @@ -621,14 +687,12 @@ pub fn update_view(size: u64, advertised: Option) -> Result> { Ok(out) } -/// Whether §4.2's procedure leaves the new rightmost entry's timestamp unchecked. +/// Whether the superseded reading leaves the new rightmost entry's timestamp unchecked. /// -/// The procedure starts from the direct path of entry `advertised - 1`. When that -/// entry *is* the root of the new tree, its direct path is empty, so there is nothing -/// to filter and nothing to continue the frontier walk from — and [`update_view`] -/// yields no entries at all even though the log has grown. The Go peer reproduces -/// this exactly (`UpdateView(12, 8)` is empty there too), so it is a property of the -/// procedure rather than of either implementation. +/// Reports on [`update_view_ancestors_only`], not on [`update_view`]: the current text +/// restarts the walk and so can never leave the rightmost entry out. The Go peer +/// reproduces the gap exactly (`UpdateView(12, 8)` is empty there too), so it was a +/// property of the procedure rather than of either implementation. /// /// It is not a rare corner. The condition is exactly that `advertised - 1` still lies /// on the *new* tree's frontier: everything above a frontier node is reached by @@ -644,16 +708,20 @@ pub fn update_view(size: u64, advertised: Option) -> Result> { /// and the clock bounds `max_ahead` and `max_behind`, which are checked against the /// rightmost entry's timestamp — the one they are never given. /// -/// This function exists so a client can notice and refuse rather than treat an empty -/// response as "nothing to check". Tracked as `DRAFT-06` in `docs/interop.md`; not filed -/// upstream. +/// This function exists so a client talking to a log that runs the earlier procedure can +/// notice and refuse rather than treat an empty response as "nothing to check". Tracked as +/// `DRAFT-06` in `docs/interop.md`, fixed in the draft on 2026-07-28, and still live +/// against the pinned peer. /// /// # Errors /// /// [`Error::EmptyLog`] if `size` is 0, or [`Error::IndexOutOfRange`] if `advertised` /// exceeds `size`. -pub fn leaves_right_edge_unchecked(size: u64, advertised: Option) -> Result { - let view = update_view(size, advertised)?; +pub fn ancestors_only_leaves_right_edge_unchecked( + size: u64, + advertised: Option, +) -> Result { + let view = update_view_ancestors_only(size, advertised)?; let up_to_date = advertised == Some(size); Ok(!up_to_date && view.last() != Some(&size.saturating_sub(1))) } @@ -763,15 +831,11 @@ mod view_tests { assert!(view.is_empty()); continue; } - if view.is_empty() { - // The §4.2 gap, which has its own test below. Not an error here, - // but not something to paper over either. - assert!( - leaves_right_edge_unchecked(size, Some(advertised)).unwrap(), - "size {size}, advertised {advertised}: empty for another reason" - ); - continue; - } + assert!( + !view.is_empty(), + "size {size}, advertised {advertised}: the current §4.2 always sends \ + something to a user who is behind" + ); assert_eq!( *view.last().unwrap(), size - 1, @@ -800,15 +864,20 @@ mod view_tests { } } - /// §4.2 provides nothing when the user's previous rightmost entry is still on the - /// new frontier, leaving everything added since unverified. The Go peer does the - /// same, so this is the procedure and not an implementation choice; the test pins - /// the condition so a future draft revision changing it shows up as a failure. + /// The superseded procedure provides nothing when the user's previous rightmost entry + /// is still on the new frontier, leaving everything added since unverified. The Go peer + /// does the same, so this was the procedure and not an implementation choice; the test + /// pins the condition, and the amendment's effect on each affected case is pinned + /// alongside it. #[test] fn the_procedure_checks_nothing_from_a_frontier_size() { // The example from the write-up: a user who saw 8 entries, shown 12. - assert!(update_view(12, Some(8)).unwrap().is_empty()); - assert!(leaves_right_edge_unchecked(12, Some(8)).unwrap()); + assert!(update_view_ancestors_only(12, Some(8)).unwrap().is_empty()); + assert!(ancestors_only_leaves_right_edge_unchecked(12, Some(8)).unwrap()); + // What the 2026-07-28 clause does with the same case: restart from entry 7 — the + // user's own rightmost — and walk the rest of the frontier, so the timestamp their + // clock bounds are checked against arrives after all. + assert_eq!(update_view(12, Some(8)).unwrap(), alloc::vec![7, 11]); // Yet the frontier grew, so there was something the user should have checked. assert_eq!(frontier(12).unwrap(), alloc::vec![7, 11]); assert_eq!(frontier(8).unwrap(), alloc::vec![7]); @@ -819,7 +888,8 @@ mod view_tests { for size in 2_u64..=400 { let edge = frontier(size).unwrap(); for advertised in 1..size { - let unchecked = leaves_right_edge_unchecked(size, Some(advertised)).unwrap(); + let unchecked = + ancestors_only_leaves_right_edge_unchecked(size, Some(advertised)).unwrap(); assert_eq!( unchecked, edge.contains(&(advertised - 1)), @@ -831,7 +901,7 @@ mod view_tests { // How often that is: for a log of 1000 entries, these advertised sizes get // nothing back — the frontier shifted by one, less the up-to-date case. let affected: Vec = (1..1000) - .filter(|m| leaves_right_edge_unchecked(1000, Some(*m)).unwrap()) + .filter(|m| ancestors_only_leaves_right_edge_unchecked(1000, Some(*m)).unwrap()) .collect(); assert_eq!(affected, alloc::vec![512, 768, 896, 960, 992]); assert_eq!( @@ -840,8 +910,40 @@ mod view_tests { ); // A user who is up to date, or who has never synchronised, is unaffected. - assert!(!leaves_right_edge_unchecked(12, Some(12)).unwrap()); - assert!(!leaves_right_edge_unchecked(12, None).unwrap()); + assert!(!ancestors_only_leaves_right_edge_unchecked(12, Some(12)).unwrap()); + assert!(!ancestors_only_leaves_right_edge_unchecked(12, None).unwrap()); + } + + /// Where the two readings agree, and where they do not. They differ only on the cases + /// the amendment was written for — and there the superseded one sends nothing — so a + /// client that has to talk to a log running either can tell which it is dealing with + /// from the first element it receives. + #[test] + fn the_two_readings_differ_only_where_the_earlier_one_sent_nothing() { + for size in 1_u64..=200 { + for advertised in 0..=size { + let current = update_view(size, Some(advertised)).unwrap(); + let earlier = update_view_ancestors_only(size, Some(advertised)).unwrap(); + if earlier.is_empty() && advertised != size { + assert_eq!( + *current.first().unwrap(), + advertised - 1, + "size {size}, advertised {advertised}: the restart begins at the \ + user's own rightmost entry" + ); + assert_eq!(*current.last().unwrap(), size - 1); + } else { + assert_eq!(current, earlier, "size {size}, advertised {advertised}"); + } + } + } + // With nothing advertised the two are the same procedure: the whole frontier. + for size in 1_u64..=200 { + assert_eq!( + update_view(size, None).unwrap(), + update_view_ancestors_only(size, None).unwrap() + ); + } } /// The monotonicity §4.2 depends on, checked against timestamps built to respect diff --git a/crates/kt-tree/src/ladder.rs b/crates/kt-tree/src/ladder.rs index bf718e3..a74c37a 100644 --- a/crates/kt-tree/src/ladder.rs +++ b/crates/kt-tree/src/ladder.rs @@ -22,6 +22,11 @@ //! a proof given for a log entry to the left or right, which is what makes a //! `CombinedTreeProof` as small as it is (§12.3). //! +//! [`update_binary_ladder`] is a fourth shape, and the one Appendix B does not +//! have: §9.1 defines it in prose as a *set* of versions rather than a sequence +//! with a stopping rule, because a label owner creating new versions is not +//! searching for anything. +//! //! # Versions are `uint32`, ladders are not //! //! Appendix B is Python, so its ladder for `n = 2^32 - 1` happily contains @@ -189,6 +194,62 @@ pub fn monitoring_binary_ladder(target: u32, left_inclusion: &[u32]) -> Vec .collect() } +/// The versions an `UpdateResponse` carries VRF proofs for (§9.1). +/// +/// This is the fourth ladder shape, and the only one that is not in Appendix B: §9.1 spells it +/// out in prose instead, as three bullets under "VRF proofs for the following versions of the +/// label". `start_ver` is the first version the update created and `end_ver` the last; they are +/// equal when only one version was created. +/// +/// - every version in a search binary ladder for `end_ver`, which is the new greatest version; +/// - each individual version in `start_ver..end_ver`, so that a multi-version update can be +/// checked version by version rather than only at its top; +/// - minus every version in a search binary ladder for `start_ver - 1`, the previous greatest, +/// "as the label owner is expected to already know these VRF outputs". Where there was no +/// previous version, §9.1 says to "omit only the version zero" — there is nothing else the +/// owner could already know. +/// +/// The result is ascending and free of duplicates, which is what §13.5's `binary_ladder` field +/// requires ("in ascending order by version"). Unlike the three Appendix B shapes this is a +/// *set*: it does not stop at a rung and has no notion of termination, because the owner is not +/// searching. It says only which VRF outputs the response has to carry. +/// +/// A caller with `start_ver > end_ver` gets the first and third bullets alone; §9.1 has no such +/// case, since an update creates at least one version, and [`crate::combined::owner_update`] +/// establishes the ordering before calling here. +/// +/// # Errors +/// +/// [`Error::UnrepresentableRung`] if `end_ver` is `u32::MAX`; see the module documentation. +pub fn update_binary_ladder(start_ver: u32, end_ver: u32) -> Result> { + let mut out = search_binary_ladder(end_ver, end_ver, &[], &[])?; + for version in start_ver..end_ver { + if !out.contains(&version) { + out.push(version); + } + } + + // The previous greatest version's own ladder is what the owner already holds. With no + // previous version there is no ladder to subtract, and §9.1 names version zero explicitly: + // an owner about to create version 0 knows nothing about the label, so version 0's VRF + // output is the one thing the log need not repeat — it is the search key the owner used to + // ask in the first place. + let known = if start_ver == 0 { + alloc::vec![0] + } else { + search_binary_ladder( + start_ver.saturating_sub(1), + start_ver.saturating_sub(1), + &[], + &[], + )? + }; + out.retain(|version| !known.contains(version)); + + out.sort_unstable(); + Ok(out) +} + /// Appendix B's `base_binary_ladder`, computed in `u64` so that the rungs above /// `u32::MAX` are representable and can be reported rather than wrapped. /// @@ -469,6 +530,118 @@ mod tests { ); } } + + /// §9.1's worked-through consequence for the simplest possible update: an owner creating + /// version 0. The new greatest version is 0, whose ladder is `[0, 1]` — version 0 included, + /// version 1 absent — and version 0 itself is the one the bullet omits, so the response + /// carries a VRF proof for version 1 alone. + #[test] + fn the_first_version_needs_only_the_rung_above_it() { + assert_eq!(update_binary_ladder(0, 0).unwrap(), vec![1]); + } + + /// The three bullets, each visible in the answer, and the third doing more work than it + /// looks like it will. An owner at version 3 creating versions 4, 5 and 6: the ladder for 6 + /// is `[0, 1, 3, 7, 5, 6]` and the individual new versions add 4. But the ladder for the + /// previous greatest version is `[0, 1, 3, 7, 5, 4]` — a base ladder does not stop once it + /// has bracketed the version, it narrows until it has pinned it, so 5 and 4 are already in + /// the owner's hands. One VRF proof survives, for the new greatest version itself. + #[test] + fn a_multi_version_update_keeps_the_new_versions_and_drops_the_known_ones() { + assert_eq!(base_binary_ladder(6).unwrap(), vec![0, 1, 3, 7, 5, 6]); + assert_eq!(base_binary_ladder(3).unwrap(), vec![0, 1, 3, 7, 5, 4]); + assert_eq!(update_binary_ladder(4, 6).unwrap(), vec![6]); + } + + /// Ascending and duplicate-free, which §13.5's `binary_ladder` field requires. Worth + /// checking across a range rather than at one point: the second bullet's individual + /// versions overlap the first bullet's ladder whenever a new version happens to be a rung, + /// and a set that merely appended would repeat it. + #[test] + fn the_set_is_ascending_and_has_no_repeats() { + for start in 0_u32..40 { + for end in start..40 { + let versions = update_binary_ladder(start, end).unwrap(); + assert!( + versions.windows(2).all(|pair| pair[0] < pair[1]), + "start {start}, end {end}: {versions:?}" + ); + } + } + } + + /// Every version the update created is present, and the previous greatest version's own + /// rungs are absent. Together these are what make the response checkable: the owner needs a + /// search key for each version it is claiming, and needs no repeat of what it holds. + #[test] + fn new_versions_are_covered_and_known_rungs_are_not() { + for start in 0_u32..30 { + for end in start..30 { + let versions = update_binary_ladder(start, end).unwrap(); + for created in start..=end { + let known = if start == 0 { + created == 0 + } else { + base_binary_ladder(start - 1).unwrap().contains(&created) + }; + assert_eq!( + versions.contains(&created), + !known, + "start {start}, end {end}, version {created}" + ); + } + if start > 0 { + for known in base_binary_ladder(start - 1).unwrap() { + assert!( + !versions.contains(&known), + "start {start}, end {end}: {known} is already held" + ); + } + } + } + } + } + + /// The `uint32` ceiling reaches this shape too, through the ladder for the new greatest + /// version. An owner cannot be told that `u32::MAX` is the greatest version of a label, + /// here for the same reason as everywhere else: the proof would need version `2^32`. + #[test] + fn the_top_version_is_unrepresentable_here_too() { + assert_eq!( + update_binary_ladder(u32::MAX, u32::MAX), + Err(Error::UnrepresentableRung { + rung: (1 << 33) - 1, + greatest: u32::MAX, + }) + ); + // One below the ceiling is fine, and comes out empty for the reason below. + assert_eq!( + update_binary_ladder(u32::MAX - 1, u32::MAX - 1).unwrap(), + [] + ); + } + + /// A single-version update can need no VRF proofs at all, and that is not the log holding + /// something back. Pinning version 1 as the greatest takes a non-inclusion proof for + /// version 2 — a base ladder narrows until it has the version exactly — so an owner going + /// from 1 to 2 already holds every search key the new ladder asks for. §13.5's + /// `binary_ladder` is legitimately empty there, and a verifier that treats empty as + /// suspicious would reject honest responses. + /// + /// It is worth knowing how often: 19 of the first 39 single-version updates need nothing at + /// all. Roughly half, not all, because a version that ends a base ladder's first phase — a + /// power of two minus one — is bracketed by the previous ladder rather than pinned by it, + /// and its own ladder reaches a rung further right. + #[test] + fn a_single_version_update_often_needs_nothing() { + assert_eq!(update_binary_ladder(2, 2).unwrap(), []); + let empty = (1_u32..40) + .filter(|version| update_binary_ladder(*version, *version).unwrap().is_empty()) + .count(); + assert_eq!(empty, 19); + // Version 3 is `2^2 - 1`, so its ladder probes 7 and the owner has never seen it. + assert_eq!(update_binary_ladder(3, 3).unwrap(), vec![4, 5, 7]); + } } #[cfg(test)] diff --git a/crates/kt-tree/src/prefix.rs b/crates/kt-tree/src/prefix.rs index 0bfde86..7e5d874 100644 --- a/crates/kt-tree/src/prefix.rs +++ b/crates/kt-tree/src/prefix.rs @@ -25,15 +25,17 @@ //! //! Both are resolved here the way the Go peer resolves them, and both are pinned //! by `interop/vectors/prefix-tree.json` so the choice is checked rather than -//! assumed. They are worth an upstream question: +//! assumed. The draft has since settled the first: //! -//! 1. **What `depth` counts for `nonInclusionParent`.** §12.2 says the terminal -//! node is "a parent node that lacks the desired child" and that `depth` is -//! "the depth of the terminal node". Read literally those give different -//! numbers, one apart: the parent sits one level above the child slot the -//! search wanted. This implementation uses the number of bits consumed to reach -//! the *missing child slot*, which is what the peer does and what makes `depth` -//! mean the same thing for all three result types. +//! 1. **What `depth` counts for `nonInclusionParent`** — *resolved on 2026-07-28 in +//! favour of the reading here.* §12.2 said the terminal node is "a parent node +//! that lacks the desired child" and that `depth` is "the depth of the terminal +//! node", which read literally gave numbers one apart. It now adds: "In the +//! context of a `nonInclusionParent` result, the `depth` field contains the depth +//! of the missing child node, not the depth of the parent." That is the number of +//! bits consumed to reach the *missing child slot*, which is what this +//! implementation and the peer both use, and what makes `depth` mean the same +//! thing for all three result types. //! 2. **Whether a missing child consumes a proof element.** §12.2 says `elements` //! holds "the fewest node values that can be hashed together with the provided //! leaves to produce the root", and also that an all-zero string is "listed diff --git a/crates/kt-wire/src/heads.rs b/crates/kt-wire/src/heads.rs index 12a8601..72a8995 100644 --- a/crates/kt-wire/src/heads.rs +++ b/crates/kt-wire/src/heads.rs @@ -7,9 +7,14 @@ //! disagreement about how a configuration encodes is a disagreement about every //! signature the log has ever produced. //! -//! # A disagreement about §11.2, found by comparing implementations +//! # A disagreement about §11.2, found by comparing implementations — since resolved //! -//! §11.2 writes the mode-dependent part of `Configuration` as: +//! **Resolved on 2026-07-28: the draft deleted `case contactMonitoring:`, which is the reading +//! this module implements.** The account below is kept because the negative vector in +//! `tree-head.json` — a signature valid only under the other reading — still guards the choice, +//! and because the reasoning is what a future grouped `select` should be read against. +//! +//! §11.2 used to write the mode-dependent part of `Configuration` as: //! //! ```tls-presentation //! select (Configuration.mode) { @@ -41,11 +46,12 @@ //! `thirdPartyManagement`, so under contact monitoring the key would have nothing //! to verify. So the `case contactMonitoring:` label looks like an editing slip. //! -//! This module follows katie and the prose, because that is what interoperates and -//! what makes semantic sense. [`Configuration::leaf_public_key_modes`] states the +//! This module followed katie and the prose, because that is what interoperated and +//! what made semantic sense. [`Configuration::leaf_public_key_modes`] states the //! rule in one place, and `interop/vectors/tree-head.json` pins it in all three -//! modes so the choice is checked rather than assumed. Filed upstream; if the -//! working group resolves it the other way, that function and its vector change. +//! modes so the choice is checked rather than assumed. The draft has since said the +//! same, so nothing here changes; had it gone the other way, that function and its +//! vector would have. use alloc::vec::Vec; diff --git a/crates/kt-wire/src/requests.rs b/crates/kt-wire/src/requests.rs index 613ff86..5f1da08 100644 --- a/crates/kt-wire/src/requests.rs +++ b/crates/kt-wire/src/requests.rs @@ -382,6 +382,97 @@ impl Decode for UpdateRequest { } } +/// An update request as forwarded to a Third-Party Manager (§14 `ManagerUpdateRequest`). +/// +/// Under `thirdPartyManagement` a user's `UpdateRequest` goes to the Service Operator, which +/// checks access control and forwards it to the Manager with its own signature over each new +/// value attached. So this is an [`UpdateRequest`] with `values` promoted from [`LabelValue`] to +/// [`UpdateValue`] — the difference being the signature — plus `signed_version`. +/// +/// `signed_version` exists because the signature covers a *version number* (§11.5's `UpdateTBS`), +/// and the Service Operator has to pick one before knowing what the Manager will assign. §14 says +/// it is "the version that was used in the computation of the Service Operator's signature over +/// the first element of `values`", zero when `values` is empty, and that a Manager seeing a +/// `signed_version` above the next version to be created MUST insert dummy entries — an all-zero +/// commitment per version — until the numbers line up. A `signed_version` *below* it "generally +/// indicates a bug in the Service Operator" and MUST be rejected. +/// +/// # The field order is the draft's, and the peer disagrees +/// +/// §14's presentation used to open with `UpdateRequest request;` and then list every field of an +/// `UpdateRequest` again inline, so each appeared twice — a leftover from when the structure was +/// `{ UpdateRequest request; opaque signature<...>; }`. That was fixed on 2026-07-28, and with the +/// listing well formed it is now the statement of the field order: `signed_version` comes **after** +/// `values`. +/// +/// The Go peer puts it before, having implemented this a fortnight before the rework landed. This +/// follows the draft, and the draft's editor has confirmed that is right: "the spec is correct. +/// Katie is out-of-date here" (draft-protocol#50). Recorded as `DRAFT-11` (the duplication, since +/// fixed) and `KT-07` (the order the peer uses). +/// +/// Note what is *not* done about `KT-07`, and why it differs from `KT-05`. §4.2's divergence needed +/// the peer's reading implemented alongside the draft's, because a `CombinedTreeProof`'s elements +/// are ordered by the algorithm that built one, so consuming a peer's proof means asking in the +/// peer's order. Nothing of the sort applies here: this structure travels from the Service Operator +/// to the Third-Party Manager, never to a user, so there is no peer artifact to read and no reason +/// to carry a second encoding. One order, the specified one. +#[derive(Clone, Debug, Default, PartialEq, Eq)] +pub struct ManagerUpdateRequest { + /// The tree size the user last observed. + pub last: Option, + /// The label being updated. + pub label: Vec, + /// The greatest version the user is aware of. + pub greatest_version: Option, + /// The version the Service Operator's first signature was computed over, or zero. + pub signed_version: u32, + /// The values to publish, each with the Service Operator's signature. + pub values: Vec, +} + +impl ManagerUpdateRequest { + /// `UpdateValue values<0..2^8-1>`. + pub const VALUES: VectorSpec = VectorSpec::new((1 << 8) - 1); + + /// Reads a `ManagerUpdateRequest` under `mode`. + /// + /// The mode is needed for `values`: an [`UpdateValue`]'s suffix is present only under + /// `thirdPartyManagement`, which is also the only mode where this structure exists at all. + /// + /// # Errors + /// + /// Codec errors from any member. + pub fn decode_with_mode( + dec: &mut Decoder<'_>, + mode: crate::structs::DeploymentMode, + ) -> Result { + let last = dec.optional()?; + let label = dec.opaque_vector(LABEL)?.to_vec(); + let greatest_version = dec.optional()?; + let values = + dec.vector_with(Self::VALUES, |dec| UpdateValue::decode_with_mode(dec, mode))?; + let signed_version = dec.u32()?; + Ok(Self { + last, + label, + greatest_version, + signed_version, + values, + }) + } +} + +impl Encode for ManagerUpdateRequest { + fn encode(&self, enc: &mut Encoder) -> Result<()> { + enc.optional(self.last.as_ref())?; + enc.opaque_vector(LABEL, &self.label)?; + enc.optional(self.greatest_version.as_ref())?; + enc.vector(Self::VALUES, &self.values)?; + enc.u32(self.signed_version); + Ok(()) + } +} + /// The signed statement a Service Operator makes about an update (§11.5 `UpdateTBS`). /// /// ```tls-presentation @@ -758,4 +849,67 @@ mod tests { ); } } + + /// A `ManagerUpdateRequest` round-trips, and its field order is the one recorded in the type's + /// documentation: `signed_version` after `values`, as §14's listing has had it since the + /// duplicated members were removed on 2026-07-28. The Go peer puts it before. Pinning it as + /// bytes here is what makes the divergence visible if either side changes its mind. + #[test] + fn a_manager_update_request_round_trips() { + let request = ManagerUpdateRequest { + last: Some(7), + label: b"alice".to_vec(), + greatest_version: Some(3), + signed_version: 4, + values: vec![UpdateValue { + value: vec![9, 9], + suffix: UpdateSuffix::ThirdPartyManagement { + signature: vec![0xab; 64], + }, + }], + }; + let bytes = encode(&request).unwrap(); + let mut dec = Decoder::new(&bytes); + assert_eq!( + ManagerUpdateRequest::decode_with_mode( + &mut dec, + crate::structs::DeploymentMode::ThirdPartyManagement + ) + .unwrap(), + request + ); + + // `signed_version` is the last four bytes, after `values`. Everything before it is an + // `UpdateRequest` with `UpdateValue`s in place of `LabelValue`s. + assert_eq!(&bytes[bytes.len() - 4..], &[0, 0, 0, 4]); + // And `values` begins where an `UpdateRequest`'s would: `last` is 1 + 8 bytes, the label + // 1 + 5, `greatest_version` 1 + 4, then the vector's own count. + let prefix = 1 + 8 + 1 + 5 + 1 + 4; + assert_eq!(bytes[prefix], 1); + } + + /// `signed_version` is zero when there is nothing signed, which §14 states as a rule rather + /// than leaving to convention: an empty `values` field means the Service Operator signed + /// nothing, so there is no version its signature could have covered. + #[test] + fn an_empty_manager_update_request_signs_version_zero() { + let request = ManagerUpdateRequest { + last: None, + label: b"bob".to_vec(), + greatest_version: None, + signed_version: 0, + values: Vec::new(), + }; + let bytes = encode(&request).unwrap(); + let mut dec = Decoder::new(&bytes); + assert_eq!( + ManagerUpdateRequest::decode_with_mode( + &mut dec, + crate::structs::DeploymentMode::ThirdPartyManagement + ) + .unwrap(), + request + ); + assert!(dec.is_empty()); + } } diff --git a/crates/kt-wire/src/responses.rs b/crates/kt-wire/src/responses.rs index f40ca33..4c09ff1 100644 --- a/crates/kt-wire/src/responses.rs +++ b/crates/kt-wire/src/responses.rs @@ -16,7 +16,7 @@ use alloc::vec::Vec; use crate::codec::{Decode, Decoder, Encode, Encoder, Result, VectorSpec}; use crate::heads::FullTreeHead; use crate::proofs::CombinedTreeProof; -use crate::requests::BinaryLadderStep; +use crate::requests::{BinaryLadderStep, LabelValue, UpdateInfo}; use crate::structs::{DeploymentMode, UpdateValue}; /// The log's answer to a `SearchRequest` (§13.1). @@ -275,6 +275,135 @@ impl Encode for OwnerInitResponse { } } +/// The log's answer to an `UpdateRequest` (§13.5). +/// +/// ```text +/// struct { +/// FullTreeHead full_tree_head; +/// +/// uint64 position; +/// select (Configuration.mode) { +/// case thirdPartyManagement: +/// uint32 skipped_versions; +/// } +/// LabelValue values<0..2^8-1>; +/// UpdateInfo info<0..2^8-1>; +/// +/// BinaryLadderStep binary_ladder<0..2^8-1>; +/// CombinedTreeProof update; +/// } UpdateResponse; +/// ``` +/// +/// `position` is where the new versions were inserted, and §13.5 warns that it "may or may not be +/// the rightmost log entry" — an update is sequenced into whatever entry the log is building, and +/// entries to its right may already exist by the time the response is sent. +/// +/// `skipped_versions` counts version counters that were skipped "due to desynchronization between +/// the Service Operator and the Third-Party Manager": under `thirdPartyManagement` the Service +/// Operator signs a version number before the Manager assigns one, and where its number is ahead +/// the Manager fills the gap with dummy versions whose commitment is all zeros. They take the +/// *lower* counters, so the versions this response reports begin after them. +/// +/// `values` carries a meaning by its emptiness rather than its contents. Empty means every version +/// in the request was created and nothing else was: the ordinary success. Non-empty means the +/// request was *disregarded* — the user's `greatest_version` was behind, so the log is reporting +/// the versions that already exist above it instead. `info` corresponds to whichever list is in +/// play, one element per version created, and §13.5 step 2 requires it to be non-empty either way. +/// +/// # One request, several responses +/// +/// §13.5 lets a log answer one `UpdateRequest` with a *stream* of `UpdateResponse`s, each covering +/// a later `position`. They are processed "serially as if an `UpdateRequest` with the following +/// parameters had been sent": `last` set to the previous response's tree size, `greatest_version` +/// advanced over the versions it reported, and `values` left alone "until the first +/// `UpdateResponse` with an empty `values` field is received", empty from then on. +/// +/// That last rule is the subtle one, and it follows from what an empty `values` means. Until an +/// empty one arrives the log has been reporting versions the user did not ask for, so the user's +/// own values are still outstanding; the response that comes back empty is the one that finally +/// created them, and there is nothing left to submit. A client that kept resubmitting would ask +/// for the same values twice. +/// +/// The state each step advances is the owner state of +/// [`kt_tree::combined::owner_update`](../../kt_tree/combined/fn.owner_update.html), which is why +/// there is no combined "verify the stream" entry point here: each response is verified against the +/// state the previous one produced. +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct UpdateResponse { + /// The tree head, and the auditor's where the mode has one. + pub full_tree_head: FullTreeHead, + /// The log entry the new versions were inserted into. + pub position: u64, + /// Version counters skipped by dummy entries, under `thirdPartyManagement` only (§14). + pub skipped_versions: Option, + /// The values created, when the request was disregarded; empty when it was honoured. + pub values: Vec, + /// One entry per version created: its commitment opening, and its signature under + /// `thirdPartyManagement`. + pub info: Vec, + /// One step per version in §9.1's version set, ascending by version. + pub binary_ladder: Vec, + /// The proof: the view update, then §9.1's algorithm. + pub update: CombinedTreeProof, +} + +impl UpdateResponse { + /// `LabelValue values<0..2^8-1>`. + pub const VALUES: VectorSpec = VectorSpec::new((1 << 8) - 1); + /// `UpdateInfo info<0..2^8-1>`. + pub const INFO: VectorSpec = VectorSpec::new((1 << 8) - 1); + /// `BinaryLadderStep binary_ladder<0..2^8-1>`. + pub const LADDER: VectorSpec = VectorSpec::new((1 << 8) - 1); + + /// Reads an `UpdateResponse` under `mode`, with `nc`-byte openings and `VRF.Np`-byte proofs. + /// + /// # Errors + /// + /// Codec errors from any member. + pub fn decode_with( + dec: &mut Decoder<'_>, + mode: DeploymentMode, + nc: usize, + proof_size: usize, + ) -> Result { + let full_tree_head = FullTreeHead::decode_with_mode(dec, mode)?; + let position = dec.u64()?; + let skipped_versions = match mode { + DeploymentMode::ThirdPartyManagement => Some(dec.u32()?), + DeploymentMode::ContactMonitoring | DeploymentMode::ThirdPartyAuditing => None, + }; + let values = dec.vector(Self::VALUES)?; + let info = dec.vector_with(Self::INFO, |dec| UpdateInfo::decode_with(dec, nc, mode))?; + let binary_ladder = dec.vector_with(Self::LADDER, |dec| { + BinaryLadderStep::decode_with_proof_size(dec, proof_size) + })?; + let update = CombinedTreeProof::decode(dec)?; + Ok(Self { + full_tree_head, + position, + skipped_versions, + values, + info, + binary_ladder, + update, + }) + } +} + +impl Encode for UpdateResponse { + fn encode(&self, enc: &mut Encoder) -> Result<()> { + self.full_tree_head.encode(enc)?; + enc.u64(self.position); + if let Some(skipped) = self.skipped_versions { + enc.u32(skipped); + } + enc.vector(Self::VALUES, &self.values)?; + enc.vector(Self::INFO, &self.info)?; + enc.vector(Self::LADDER, &self.binary_ladder)?; + self.update.encode(enc) + } +} + #[cfg(test)] #[allow( clippy::indexing_slicing, @@ -285,7 +414,7 @@ mod tests { use super::*; use crate::codec::encode; use crate::heads::TreeHead; - use crate::proofs::{InclusionProof, PrefixProof, PrefixSearchResult}; + use crate::proofs::{InclusionProof, PrefixLeaf, PrefixProof, PrefixSearchResult}; use crate::structs::{HashValue, UpdateSuffix}; use alloc::vec; @@ -431,4 +560,134 @@ mod tests { ), } } + + /// An update response round-trips under `thirdPartyManagement`, which is the mode with the most + /// context-dependent parts: an `UpdateInfo` carries both an `Nc`-byte opening and a signature + /// suffix, and neither length is on the wire. + #[test] + fn an_update_response_round_trips() { + let value = UpdateResponse { + full_tree_head: FullTreeHead::Updated { + tree_head: TreeHead { + tree_size: 12, + signature: vec![3; 64], + }, + auditor_tree_head: None, + }, + position: 9, + skipped_versions: Some(2), + values: vec![LabelValue::new(vec![1, 2, 3])], + info: vec![UpdateInfo { + opening: vec![0xcd; 16], + suffix: UpdateSuffix::ThirdPartyManagement { + signature: vec![0xef; 64], + }, + }], + binary_ladder: vec![BinaryLadderStep { + proof: vec![0x77; 80], + commitment: None, + }], + update: CombinedTreeProof { + timestamps: vec![5], + prefix_proofs: vec![PrefixProof { + results: vec![PrefixSearchResult::NonInclusionLeaf { + leaf: PrefixLeaf { + vrf_output: HashValue::from_bytes([0x88; 32]), + commitment: HashValue::from_bytes([0x89; 32]), + }, + depth: 3, + }], + elements: vec![HashValue::from_bytes([0x99; 32])], + }], + prefix_roots: Vec::new(), + inclusion: InclusionProof::new(Vec::new()), + }, + }; + let bytes = encode(&value).unwrap(); + let mut dec = Decoder::new(&bytes); + let decoded = + UpdateResponse::decode_with(&mut dec, DeploymentMode::ThirdPartyManagement, 16, 80) + .unwrap(); + assert_eq!(decoded, value); + } + + /// The empty cases, all of which §13.5 gives a meaning to. `values` empty means the request was + /// honoured; `binary_ladder` empty means every search key the update needs is one the owner + /// already holds, which is the common case for a single-version update. Both have to survive a + /// round-trip as *empty* rather than being read as absent. + #[test] + fn an_update_response_with_nothing_optional_round_trips() { + let value = UpdateResponse { + full_tree_head: FullTreeHead::Same, + position: 0, + skipped_versions: None, + values: Vec::new(), + info: vec![UpdateInfo { + opening: vec![0; 16], + suffix: UpdateSuffix::Empty, + }], + binary_ladder: Vec::new(), + update: CombinedTreeProof { + timestamps: Vec::new(), + prefix_proofs: Vec::new(), + prefix_roots: Vec::new(), + inclusion: InclusionProof::new(Vec::new()), + }, + }; + let bytes = encode(&value).unwrap(); + let mut dec = Decoder::new(&bytes); + let decoded = + UpdateResponse::decode_with(&mut dec, DeploymentMode::ContactMonitoring, 16, 80) + .unwrap(); + assert_eq!(decoded, value); + assert!(decoded.values.is_empty() && decoded.binary_ladder.is_empty()); + } + + /// The mode decides whether four bytes sit between `position` and `values`, and nothing in + /// the bytes says which — the same hazard as every other context-dependent field here, with + /// a sharper edge: `skipped_versions` and the length prefix of `values` are both plausible + /// readings of the same offset, so a response read under the wrong mode does not fail, it + /// silently reports a different position and a different number of values. + #[test] + fn skipped_versions_is_present_only_under_third_party_management() { + let value = UpdateResponse { + full_tree_head: FullTreeHead::Same, + position: 4, + skipped_versions: Some(3), + values: Vec::new(), + info: vec![UpdateInfo { + opening: vec![0x11; 16], + suffix: UpdateSuffix::ThirdPartyManagement { + signature: vec![0x22; 64], + }, + }], + binary_ladder: Vec::new(), + update: CombinedTreeProof { + timestamps: Vec::new(), + prefix_proofs: Vec::new(), + prefix_roots: Vec::new(), + inclusion: InclusionProof::new(Vec::new()), + }, + }; + let bytes = encode(&value).unwrap(); + // 1 byte of head type, 8 of position, then the four that only this mode carries. + assert_eq!(&bytes[9..13], &[0, 0, 0, 3]); + + let mut dec = Decoder::new(&bytes); + assert_eq!( + UpdateResponse::decode_with(&mut dec, DeploymentMode::ThirdPartyManagement, 16, 80) + .unwrap(), + value + ); + + // The same bytes under a mode that has no such field: the four bytes are read as the + // start of `values` instead, so everything after `position` shifts. It must not + // reproduce the original — here it fails outright, because the misread count runs off + // the end of the input. + let mut dec = Decoder::new(&bytes); + match UpdateResponse::decode_with(&mut dec, DeploymentMode::ContactMonitoring, 16, 80) { + Err(_) => {} + Ok(decoded) => assert_ne!(decoded, value), + } + } } diff --git a/docs/interop.md b/docs/interop.md index 45739f6..e7bec8f 100644 --- a/docs/interop.md +++ b/docs/interop.md @@ -5,6 +5,22 @@ implementation that provably agrees with the Go ones, byte for byte." That requires a harness, and the harness constrains the design — so it is planned first. +## Which text, and which peer + +The draft submodule is pinned at `12121fd` (2026-07-29), whose `docname` is +`draft-ietf-keytrans-protocol-latest` — the editor's copy, some way past published -05. Section +numbers throughout this repository still cite "-05" because that is the last published revision and +the numbering has not moved; where the *text* has moved, the code says so at the point it matters +and the register below records it. + +**The two are no longer in step.** The draft took four commits on 2026-07-28/29; katie has not moved +since 2026-06-30. So there are now places where the draft says one thing, the peer does another, and +both are recorded rather than reconciled: §4.2's frontier restart (`KT-05`), §14's +`ManagerUpdateRequest` field order (`KT-07`), §9.1 step 2.1's condition on a previous version +having existed (`KT-06`). Where a *proof* is at stake the peer's reading has to be implemented as +well, because a `CombinedTreeProof`'s elements are ordered by the algorithm that built it — see +`ibst::update_view_ancestors_only`. + ## What we are interoperating with **`upstream/katie`** ([Bren2010/katie](https://github.com/Bren2010/katie), pinned at @@ -14,7 +30,7 @@ first. |---|---|---| | `crypto/suites` | `kt-crypto::suite` | §11.1, §17.1 | | `crypto/commitments` | `kt-crypto::commitment` | §11.6 | -| `crypto/vrf/{edwards25519,p256}` | `kt-crypto::vrf` | §11.7 | +| `crypto/vrf/{edwards25519,p256}` | `kt-crypto::vrf::{edwards25519,p256}` | §11.7 | | `tree/log`, `tree/log/math` | `kt-tree::log` | §3.2, §11.8, §12.1 | | `tree/prefix` | `kt-tree::prefix` | §3.3, §11.9, §12.2 | | `tree/transparency/math` (ladders, tracker) | `kt-tree::ladder`, `kt-tree::ibst` | §4.1, §5, App. A/B | @@ -397,7 +413,24 @@ shape drifts. The two implementations arrive differently: katie indexes a chain and propagates a carry, `kt-tree::log` keeps subtree lengths beside their heads and merges the rightmost pair while the lengths match. -### DRAFT-06: §4.2 can leave a user checking nothing at all +### DRAFT-06: §4.2 can leave a user checking nothing at all — fixed, and the fix does not interoperate + +**Fixed in the draft on 2026-07-28**, by adding a restart: "unless there were no such log entries, +in which case the log entry with index `size-1` lies on the new tree's frontier". The walk then +begins at the user's own rightmost entry and reaches the new one, so the timestamp their clock +bounds are checked against always arrives. + +Adopting it alone would have cost 31 checks against the pinned peer — 30 in `update-view.json`, and +one live proof in `search.json`, which fails with "entry 6's timestamp contradicts entry 3's". That +is not a bookkeeping difference: §12.3 orders a proof's elements by "the order that the algorithm +the user is executing would request them", so a proof built under the earlier procedure must be read +under the earlier procedure. Both readings are therefore implemented — +`ibst::update_view` for the current text, `ibst::update_view_ancestors_only` for the peer's — and +the replays consume katie's proofs with the latter. Recorded against the peer as `KT-05`. + +The original write-up follows, because the argument for *why* the hole was not a rare corner is +what makes the fix worth having. + Implementing §4.2's update-view procedure turned up a hole, and `update-view.json` records the Go peer reproducing it, which is what makes it the procedure's behaviour @@ -682,6 +715,38 @@ versions. Measured against katie: for a seven-entry log with greatest version 6, either implementation — but the draft never says the results are a prefix, and it is the first thing an implementer gets wrong. +**[KT-05] katie implements §4.2 as it read before 2026-07-28.** Its `UpdateView` has no frontier +restart, so for the 15 advertised sizes in `update-view.json` where the user's rightmost entry is +still on the new frontier it returns nothing at all. That is `DRAFT-06`, now fixed in the draft and +still live in the peer. The consequence is not only a different list: proofs the peer builds are +ordered by the procedure the peer ran, so consuming one means asking in its order. Both readings are +implemented here; `update-view.json` compares the peer against its own and checks the current text +against the guarantee the amendment added. + +**[KT-06] katie skips a previous-tree entry §9.1 no longer lets it skip.** Step 2.1 now reads "if a +previous version of the label existed, and the current log entry's index is less than or equal to +the index of the log entry where the previous greatest version was inserted". katie's condition is +`x <= m.Owner.LastUpdate()`, and `LastUpdate` falls back to the owner's reference point when no +version has been created since — so for a label whose first version is being created, katie skips +entries at or before the reference point where the current text inspects them. No recorded case +separates the two, because every case's walk begins to the right of the reference point; a +`kt-tree` test covers the shape that does. + +**[KT-07] katie orders `ManagerUpdateRequest.signed_version` before `values`; §14 puts it after.** +katie implemented the structure a fortnight before the July 2026 rework, and the rework's listing — +once its duplicated members were removed on 2026-07-28 — puts `signed_version` last. This +implementation follows the draft, and the draft's editor confirmed that is right: *"Yes, the spec is +correct. Katie is out-of-date here"* ([#50](https://github.com/ietf-wg-keytrans/draft-protocol/issues/50)). + +Worth contrasting with `KT-05`, because the two peer divergences were handled differently on +purpose. §4.2's needed the peer's reading implemented *alongside* the draft's, since a +`CombinedTreeProof`'s elements are ordered by the algorithm that built one — consuming a peer's +proof means asking in the peer's order, whatever the text now says. Nothing of the sort applies +here: a `ManagerUpdateRequest` travels from the Service Operator to the Third-Party Manager and +never to a user, so there is no peer artifact to read and no reason to carry a second encoding. One +order, the specified one. A divergence is worth implementing around only when something has to be +*parsed* across it. + **[DRAFT-10] §12.3.6 omits the timestamps without which its own proof cannot be rooted.** For owner monitoring it lists "the timestamp for each log entry that causes the second algorithm of §8.3 to recurse either left or right" and, separately, "for each log entry that reaches step 5, a @@ -697,6 +762,82 @@ the entry's ladder, and a verifier that expects them consumes the proof exactly. entry reaches step 5 without recursing. Worth an editorial issue if the register is ever filed — an implementer following §12.3.6 literally builds a proof that no client can root. +**[KT-04] katie cannot serve an update at all.** `Tree.Update` and `Tree.ManagerUpdate` return a +channel of `UpdateResponse`, and every element of it is an error: `updater.next` builds its monitor +with `algorithms.NewMonitor`, which leaves `Monitor.Owner` nil, and then calls `Monitor.Update`, +whose first line refuses when `Owner` is nil. Measured at the pinned commit, on both the path that +creates new versions and the path that only reports existing ones: `label owner state has not been +initialized`, before any proof is built. No katie test exercises `Tree.Update`, which is consistent +with the path never having run. + +This blocks the forward direction for §13.5 outright — there is no `UpdateResponse` to record, so +none is claimed. §9.1's proof is still measurable, because the consumer half of the same code takes +the owner state from its caller: `update.json` drives `ProducedProofHandle`, `UpdateView`, +`NewMonitor` and `Monitor.Update` exactly as `updater.next` does, supplying the state the broken +path leaves nil, and records what comes out. The reverse direction needs nothing special, since +`StreamVerifier.Verify` sets `Owner` before calling the algorithm — so `from-kt.json` feeds +kt-built §9.1 proofs to katie's own reading of it. + +Filed as [katie#1](https://github.com/Bren2010/katie/issues/1): a blocker by the register's rule, +and not one measurement can settle. Two lines of initialization would fix it, but which state the +log should attribute to a requester is katie's decision, not this implementation's — so the report +asks rather than proposing a patch. + +**[DRAFT-11] §14's `ManagerUpdateRequest` listed every field twice.** *Fixed on 2026-07-28, two +days before it was filed here — the pin was stale, which is the lesson.* The presentation was: + +```tls-presentation +struct { + UpdateRequest request; + optional last; + + opaque label<0..2^8-1>; + optional greatest_version; + UpdateValue values<0..2^8-1>; + + uint32 signed_version; +} ManagerUpdateRequest; +``` + +`UpdateRequest` already contains `last`, `label`, `greatest_version` and `values`, so as written +every field appears twice. Tracing it upstream shows what happened: the structure was +`{ UpdateRequest request; opaque signature<0..2^16-1>; }` until a rework in July 2026 spelled the +fields out inline, and the first member was never deleted. + +That leaves the field order undetermined, and it is not a cosmetic question. The listing puts +`signed_version` last; katie — which implemented this two weeks before the rework landed — puts it +before `values`. The prose says only that the structure "is the same as `UpdateRequest`" and "also +contains a `signed_version` field", which does not choose between them. This implementation follows +the peer, since a corrupt listing is not evidence for anything and the peer's order is what +interoperates; `kt-wire::requests` pins the choice as bytes and says why. + +What survives is narrower and sharper than what was filed. With the listing well formed, +`signed_version`-after-`values` is normative and the peer is the one diverging, which makes the +remainder a peer finding (`KT-07`) rather than a draft one. This implementation follows the draft. + +Filed as [draft-protocol#50](https://github.com/ietf-wg-keytrans/draft-protocol/issues/50) against +a submodule pin that was already two days out of date. Checking upstream before filing would have +caught it. Re-scoped to the order alone, and answered: the spec is correct and the peer is out of +date, which is what this implementation had assumed. + +**[DRAFT-12] §13.5 gained `skipped_versions`, and §9.1 was not told.** As of 2026-07-28 an +`UpdateResponse` under `thirdPartyManagement` carries a `uint32 skipped_versions`: where the Service +Operator's signed version number runs ahead of the Manager's counter, the Manager fills the gap with +dummy versions whose commitment is all zeros, and those take the *lower* counters. §14 says they +"serve the function of skipping the corresponding version counters". + +§9.1 says nothing about them. Its VRF-proof set is "the set of all versions that would be contained +in a search binary ladder for the new greatest version" plus "each of these individual versions" — +and whether a skipped version is one of "these" is unstated, as is whether step 3 or 4's inclusion +proof must cover a version whose commitment is a known constant. Both readings are defensible and +they differ in the number of elements a proof carries, which is exactly the class of ambiguity +§12.3's exact-count rule turns into an interop failure. + +Not filed: unresolvable by measurement, since katie predates the field entirely, but also +unreachable — `thirdPartyManagement` is the one mode with no §9.1 vector, because the peer cannot +serve an update in any mode (`KT-04`). The wire field is implemented and the algorithm is left +alone; when a peer exists that produces one of these, this is the first thing to measure. + **[NOTE-03] `opening` sits in a different place in the two implementations.** The draft puts `opaque opening[Nc]` inside `CommitmentValue`; katie keeps it outside the struct and writes it to the HMAC first. Same bytes, different factoring — the vectors @@ -750,10 +891,19 @@ is stable. Findings are tracked here rather than filed, with one exception. The rule is that a finding gets filed when it is a **blocker** — something that cannot be resolved either by reading the specification or by measuring the peer, so that no amount of further work here will settle it. -`DRAFT-09` is the first to meet that bar and is filed as -[draft-protocol#48](https://github.com/ietf-wg-keytrans/draft-protocol/issues/48). Everything -else is resolved: the behaviour is known, a committed vector pins it, and a filing would be a -courtesy rather than a necessity. +`DRAFT-09` was the first to meet that bar, and its answer — "the appendix is wrong" — is the +clearest illustration of why the rule is drawn where it is: katie implements the parameter-free +version either way, so no amount of measurement could have settled what the text meant. +`DRAFT-11` and `KT-04` are the other two: one was a specification that contradicted itself, the +other a peer code path that cannot run. Everything else is resolved: the behaviour is known, a +committed vector pins it, and a filing would be a courtesy rather than a necessity. + +Two lessons from the 2026-07-28 draft revision, which resolved five entries here at once. **Check +the pin before filing**: `DRAFT-11` had been fixed upstream two days before it was filed, and the +issue would not have been written had the submodule been fetched first. And **a resolution can cost +more than the ambiguity did**: `DRAFT-06`'s fix is the right change to the specification and it makes +the draft and the pinned peer incompatible on the wire, which is why both readings now exist in the +code rather than one. | ID | What | Belongs to | Pinned by | Status | |---|---|---|---|---| @@ -761,17 +911,23 @@ courtesy rather than a necessity. | `KT-02` | `EvaluateBeforeAfter` treats §11.9's all-zero copath element as an opaque node | katie | `prefix-mutation.json` `remove-every-leaf` | tracked locally | | `KT-03` | `EvaluateBeforeAfter` refuses the replacement §15.2 permits | katie | `prefix-mutation.json` `replace-in-place` | tracked locally | | `DRAFT-01` | A greatest version of `2^32-1` cannot be proven at all | draft | `kt-tree::ladder` refuses it | tracked locally | -| `DRAFT-02` | §12.2 leaves `nonInclusionParent`'s `depth` and its element accounting implicit | draft | `prefix-tree.json` | tracked locally | +| `DRAFT-02` | §12.2 leaves `nonInclusionParent`'s `depth` and its element accounting implicit | draft | `prefix-tree.json` | **half resolved**: §12.2 now defines `depth` as the missing child's, the reading used here; the element accounting is still implicit | | `DRAFT-03` | §12.2's `uint8 depth` cannot express depth 256 | draft | `kt-tree::prefix` `DepthOverflow` | tracked locally | | `DRAFT-04` | §15.2 step 7 cannot determine the root when a removal's sibling is uncovered | draft | `prefix-mutation.json`, `auditor-update.json` | tracked locally | | `DRAFT-05` | §15.2 cannot audit a log entry that changes nothing | draft | `auditor-update.json` `change-nothing` | tracked locally | -| `DRAFT-06` | §4.2 can send a user no timestamps at all while the log has grown | draft | `update-view.json`; `ibst::leaves_right_edge_unchecked` | tracked locally | -| `DRAFT-07` | §11.2's grouped `select` reads two ways; the two Go implementations took one each, and no signature cross-verifies in contactMonitoring | draft | `tree-head.json`, including a negative case carrying a signature valid under the other reading | tracked locally | -| `DRAFT-08` | §6.3 cannot verify the response that means "this label does not exist" | draft | `search.json` `label-does-not-exist` | tracked locally | +| `DRAFT-06` | §4.2 can send a user no timestamps at all while the log has grown | draft | `update-view.json`; `ibst::ancestors_only_leaves_right_edge_unchecked` | **resolved**: the 2026-07-28 restart clause; the peer still lags, so both readings are implemented (`KT-05`) | +| `DRAFT-07` | §11.2's grouped `select` reads two ways; the two Go implementations took one each, and no signature cross-verifies in contactMonitoring | draft | `tree-head.json`, including a negative case carrying a signature valid under the other reading | **resolved**: `case contactMonitoring:` deleted on 2026-07-28, which is the reading implemented here | +| `DRAFT-08` | §6.3 cannot verify the response that means "this label does not exist" | draft | `search.json` `label-does-not-exist` | **resolved**: §13.1 now says no negative result is encodable and clients MUST treat one as failed validation; the peer sends them anyway | | `NOTE-01` | katie's search ladder is target-indexed, Appendix B's greatest-indexed — equivalent | neither | generator's 131×131 grid; Rust tests | no action | | `NOTE-04` | a per-entry ladder's results are a *prefix* of the verifier's ladder, because the log stops on the local greatest version | neither | `search.json`, all five greatest-version cases | no action | | `DRAFT-09` | Appendix B's `monitoring_binary_ladder` keeps a `left_inclusion` parameter whose prose was removed; the two readings fail against each other | draft | `monitor.json`, §8.2 replays under the empty-set reading | **resolved**: the appendix is wrong ([#48](https://github.com/ietf-wg-keytrans/draft-protocol/issues/48)); fix sent as [#49](https://github.com/ietf-wg-keytrans/draft-protocol/pull/49) | | `DRAFT-10` | §12.3.6 omits the timestamp of an entry that reaches step 5 without recursing, so its own proof cannot be rooted | draft | `monitor.json` `owner-monitor-reaches-step-5` | tracked locally; resolved by measurement | +| `KT-04` | `Tree.Update` cannot answer any request: the owner state is checked but never initialized | katie | `update.json` drives the algorithm directly, supplying the state | **filed**: [katie#1](https://github.com/Bren2010/katie/issues/1) | +| `DRAFT-11` | §14's `ManagerUpdateRequest` listed every field twice | draft | `kt-wire::requests` | **resolved**: duplication removed 2026-07-28, before it was filed; the order half is now `KT-07` | +| `KT-05` | §4.2 without the 2026-07-28 frontier restart, so 15 advertised sizes get nothing back | katie | `update-view.json`, both readings; `search.json`'s advertised-size replay | tracked locally | +| `KT-06` | §9.1 step 2.1 skipped unconditionally, where the current text requires a previous version to have existed | katie | `kt-tree::combined` test `a_first_version_skips_nothing_in_the_previous_tree` | tracked locally | +| `KT-07` | `ManagerUpdateRequest.signed_version` ordered before `values`, where §14 puts it after | katie | `kt-wire::requests` round-trip pins the draft's order as bytes | **confirmed** by the draft's editor: the spec is right, the peer is out of date ([#50](https://github.com/ietf-wg-keytrans/draft-protocol/issues/50)) | +| `DRAFT-12` | §13.5 gained `skipped_versions`, but §9.1 does not say how its ladder set or additional-proof set treats a skipped version | draft | `kt-wire::responses` decodes the field; the algorithm is untouched | tracked locally | | `NOTE-03` | `opening` sits inside `CommitmentValue` in the draft, outside it in katie | neither | `commitment.json` records both | no action | Two ground rules for anything added here. A finding is only a finding once a committed diff --git a/interop/README.md b/interop/README.md index 2d84f12..cc2d9fd 100644 --- a/interop/README.md +++ b/interop/README.md @@ -150,7 +150,8 @@ dropped. `binary-ladder.json` stops at version `2^31-2` for exactly that reason. | `commitment.json` | commitment | §11.6 | 6 positive, 1 negative | | `ibst.json` | implicit binary search tree | §4.1, Appendix A | 38 log sizes | | `binary-ladder.json` | binary ladder | §5, Appendix B | 76 across base, search, monitoring | -| `vrf.json` | VRF | §11.7 | 10 positive, 1 negative | +| `vrf.json` | VRF, Ed25519 suite | §11.7 | 10 positive, 1 negative | +| `vrf-p256.json` | VRF, P-256 suite | §11.7, §17.1 | 10 positive, 1 negative | | `log-math.json` | log tree structure, in node indices | §3.2, §4.2, §12.1 | 1679 decompositions | | `log-tree.json` | log tree | §3.2, §11.8, §12.1 | 19 sizes, 297 batch proofs | | `prefix-tree.json` | prefix tree | §3.3, §11.9, §12.2 | 11 trees | @@ -159,13 +160,15 @@ dropped. `binary-ladder.json` stops at version `2^31-2` for exactly that reason. | `auditor-update.json` | `AuditorUpdate` bytes, the auditor's verdict, and the root it would sign | §15.2, §11.3 | 14, 8 negative | | `search.json` | responses served by a real in-memory log | §6.3, §7.1–§7.2, §12.3, §13.1 | 13 requests, 1 refused | | `monitor.json` | monitoring responses from the same log | §13.2–§13.4 | 5 operations | +| `update.json` | §9.1 update proofs from the same log | §9.1, §13.5 | 8 owner/window/version shapes | | `ladder-interpretation.json` | search ladder interpretation | §6.2 | 211 target/greatest pairs | | `update-view.json` | updating a view | §4.2 | 190 size/advertised pairs | | `distinguished.json` | distinguished log entries | §6.1 | 42 size/window/timestamp shapes | | `tree-head.json` | configuration, signatures, and `FullTreeHead` bytes | §11.2–§11.4 | 9, all three modes | +| `tree-head-p256.json` | the same under ECDSA/P-256 | §11.2–§11.4, §17.1 | 9, all three modes | | `requests.json` | §13 requests and building blocks | §11.5, §13.1–§13.5 | 22 structures | | `tampered.json` | **must reject** | §11.2, §11.6, §11.7, §12.1, §12.2 | 22, all negative | -| `from-kt.json` | must accept / must reject, in reverse | as above | 218, roughly half negative | +| `from-kt.json` | must accept / must reject, in reverse | as above | 225, roughly half negative | ## Two kinds of agreement @@ -189,11 +192,18 @@ subtree over `[start, start+len)` is node `2*start + len - 1`, and because a §1 only ever carries balanced subtree heads, that translation is total on exactly the values that matter. A range that failed to map would itself be the bug. -## The two files that cannot be reproducible +## The four files that cannot be reproducible Every vector file here regenerates to the same bytes, and CI diffs them so that an upstream -bump changing behaviour fails loudly instead of passing quietly. `search.json` and -`monitor.json` are the exceptions, and it is not fixable: both are served by a live log, and +bump changing behaviour fails loudly instead of passing quietly. Four are exceptions. + +`tree-head-p256.json` is the simple case: it carries ECDSA signatures, and Go's `crypto/ecdsa` +draws a nonce per signature. §17.1 fixes how `r` and `s` are encoded, not how they are produced. +Here the non-reproducibility is a feature — CI regenerates and re-checks, so every run verifies +signatures nobody has seen, and a verifier that happened to work for one nonce would fail. + +`search.json`, `monitor.json` and `update.json` are the harder case, and it is not fixable: all +three come from a live log, and katie stamps each entry with `time.Now()` and generates a fresh random opening for every commitment, with no injection point for either. Every commitment, prefix root, log root and signature in a response therefore differs run to run. @@ -208,11 +218,21 @@ where everything or nothing expired would pass while testing nothing. The alternative to giving up the property was to weaken it — compare the regenerated file structurally, ignoring the values that move. That would have checked less than what is done instead: CI regenerates the vectors and runs **the whole Rust check suite against the -regenerated tree**, so both files' checks execute against bytes nobody has ever seen. A change +regenerated tree**, so all four files' checks execute against bytes nobody has ever seen. A change in how the peer shapes a response fails there. The committed files remain as pinned artifacts for `cargo test` and for the page, and are excluded from the reproducibility diff. -Net effect: the deterministic files are pinned *and* reproducible; the two nondeterministic ones +`update.json` is the odd one out in a second way, and it is worth being precise about what it +claims. It carries `CombinedTreeProof` structures and no response envelope, because katie's +`tree.Update` cannot serve an update at all: it builds a monitor that leaves the owner state +nil and then calls the algorithm that requires it, so every request comes back "label owner +state has not been initialized" (`KT-04`). The vectors therefore drive katie's §9.1 +implementation directly — the same calls `tree.Update` makes, with the owner state supplied — +and record what it produces. What is measured is §9.1's element ordering, which is the part no +hand-built example can pin. The `UpdateResponse` around it was never measured and is not +claimed; its members are pinned by `tree-head.json` and `requests.json` instead. + +Net effect: the deterministic files are pinned *and* reproducible; the four nondeterministic ones are pinned and re-derived. Nothing is checked only against itself, which is the property that actually matters. diff --git a/interop/go/cmd/gen/heads.go b/interop/go/cmd/gen/heads.go index d64e8f1..2ae3ed6 100644 --- a/interop/go/cmd/gen/heads.go +++ b/interop/go/cmd/gen/heads.go @@ -37,19 +37,33 @@ import ( "fmt" "github.com/Bren2010/katie/crypto/suites" - "github.com/Bren2010/katie/crypto/vrf/edwards25519" "github.com/Bren2010/katie/tree/transparency/structs" ) // headVectors covers draft §11.2, §11.3, and §11.4. func headVectors(sha string) (*File, error) { - cs := suites.KTSha256Ed25519{} + return headVectorsFor(sha, suites.KTSha256Ed25519{}) +} + +// headVectorsP256 is the same set under KT_128_SHA256_P256, where the signature is +// ECDSA over SHA-256 rather than Ed25519 and the suite code in every Configuration is +// 0x0001. Unlike its Ed25519 counterpart this file is *not* reproducible: Go's +// crypto/ecdsa draws a fresh nonce per signature, and §17.1 fixes the encoding of r +// and s but not how they are produced. +// +// That is a feature here rather than a nuisance. CI regenerates the vectors and runs +// the Rust checks against them, so every run verifies signatures nobody has seen — a +// verifier that happened to work for one nonce would fail. +func headVectorsP256(sha string) (*File, error) { + return headVectorsFor(sha, suites.KTSha256P256{}) +} +func headVectorsFor(sha string, cs suites.CipherSuite) (*File, error) { f := &File{ Primitive: "tree-head", Draft: draftRev + " §11.2, §11.3, §11.4", Generator: Generator{Impl: "katie", SHA: sha}, - CipherSuite: 0x0002, // KT_128_SHA256_Ed25519 + CipherSuite: cs.Id(), Notes: "Signed tree heads and the configuration every signature covers. " + "`configuration` is the encoded Configuration, `tree_head_tbs` is what the " + "signature is computed over, and `tree_head` is the wire TreeHead. Note the " + @@ -74,7 +88,7 @@ func headVectors(sha string) (*File, error) { if err != nil { return nil, fmt.Errorf("parsing the leaf signing key: %w", err) } - vrfKey, err := edwards25519.NewPrivateKey(repeat(0x74, 32)) + vrfKey, err := cs.ParseVRFPrivateKey(repeat(0x74, 32)) if err != nil { return nil, fmt.Errorf("parsing the VRF key: %w", err) } diff --git a/interop/go/cmd/gen/main.go b/interop/go/cmd/gen/main.go index 045d953..b7b68f3 100644 --- a/interop/go/cmd/gen/main.go +++ b/interop/go/cmd/gen/main.go @@ -78,6 +78,7 @@ func main() { {"ibst.json", ibstVectors}, {"binary-ladder.json", ladderVectors}, {"vrf.json", vrfVectors}, + {"vrf-p256.json", vrfVectorsP256}, {"update-view.json", updateViewVectors}, {"log-math.json", logMathVectors}, {"log-tree.json", logTreeVectors}, @@ -87,7 +88,9 @@ func main() { {"auditor-update.json", auditorVectors}, {"search.json", searchVectors}, {"monitor.json", monitorVectors}, + {"update.json", updateVectors}, {"tree-head.json", headVectors}, + {"tree-head-p256.json", headVectorsP256}, {"requests.json", requestVectors}, {"ladder-interpretation.json", ladderInterpretationVectors}, {"distinguished.json", distinguishedVectors}, diff --git a/interop/go/cmd/gen/update.go b/interop/go/cmd/gen/update.go new file mode 100644 index 0000000..5d8cafd --- /dev/null +++ b/interop/go/cmd/gen/update.go @@ -0,0 +1,567 @@ +// Vectors for a label owner verifying an update (draft §9.1, §13.5). +// +// §9.1 is the protocol's only two-tree algorithm: it checks a claim about the boundary between +// the log as it was before the new versions were added and the log as it is now. That makes the +// element ordering inside its `CombinedTreeProof` unlike any other operation's — two descents +// for distinguished entries, a greatest-version search over the *previous* tree's frontier, then +// one or two proofs at the entry holding the new versions — and ordering is exactly what a +// hand-built example cannot pin. +// +// # What is missing here, and why +// +// Every other response in this directory comes from katie's public entry point: `tree.Search`, +// `tree.ContactMonitor`, `tree.OwnerMonitor`. The equivalent for §13.5 would be `tree.Update`, +// and it cannot be used: it fails for every request with "label owner state has not been +// initialized". Its `updater.next` builds a monitor with `algorithms.NewMonitor`, which leaves +// `Monitor.Owner` nil, and then calls `Monitor.Update`, which refuses when `Owner` is nil. No +// katie test exercises `tree.Update`, which is consistent with the path never having run. +// Measured at the pinned commit; recorded as `KT-04`, filed as Bren2010/katie#1. +// +// So these vectors drive the algorithm the way `updater.next` intends to — a +// `ProducedProofHandle` over the log's own store, `algorithms.UpdateView`, a monitor whose +// `Owner` this harness supplies, then `Monitor.Update` — and record the `CombinedTreeProof` that +// comes out. That is the part §12.3 makes hard and the part a verifier can get wrong. What it +// does not record is the `UpdateResponse` envelope around it: `position`, `info` and +// `binary_ladder` are assembled by the unreachable code path, and its `FullTreeHead`, +// `UpdateInfo` and `BinaryLadderStep` members are already pinned by tree-head.json and +// requests.json. Claiming an `UpdateResponse` was checked against the peer would be claiming +// more than was measured. +// +// # Not reproducible +// +// Like search.json and monitor.json: katie stamps each entry with time.Now() and draws a fresh +// random commitment opening per version, so every timestamp, commitment and prefix root below +// differs run to run. CI runs the Rust checks against a freshly generated copy rather than +// diffing this file. See ../../README.md. +package main + +import ( + "bytes" + "encoding/hex" + "fmt" + "slices" + + "github.com/Bren2010/katie/crypto/suites" + "github.com/Bren2010/katie/crypto/vrf/edwards25519" + "github.com/Bren2010/katie/db" + "github.com/Bren2010/katie/db/memory" + "github.com/Bren2010/katie/tree/transparency" + "github.com/Bren2010/katie/tree/transparency/algorithms" + ktmath "github.com/Bren2010/katie/tree/transparency/math" + "github.com/Bren2010/katie/tree/transparency/structs" +) + +// updateCase is one owner checking one update. +type updateCase struct { + name string + // mutations is one log entry per element, each adding the label-value pairs given. + mutations [][]labelValue + label string + // window overrides the Reasonable Monitoring Window. A week leaves only the root + // distinguished, which is what puts §9.1 on its step 4 branch; zero makes every entry + // distinguished, which is the step 3 branch. + window uint64 + // position is the log entry the new versions were added to, and versions how many. + position uint64 + versions int + // The owner's state before the update: the reference point it verified, the greatest version + // that existed there — nil for a label that did not exist yet — and where each version it + // already knows about was inserted. + starting uint64 + verAtStarting *uint32 + upcoming []uint64 + // last is the tree size the owner advertised, if any. §12.3 then omits the timestamps it is + // expected to have retained, so the same query produces a differently shaped proof. + last *uint64 + // note explains what the case is for, and rides along into the vector file. + note string +} + +func updateCases() []updateCase { + lv := func(label, value string) labelValue { + return labelValue{label: []byte(label), value: []byte(value)} + } + alice := "alice@example.com" + bob := "bob@example.com" + carol := "carol@example.com" + dave := "dave@example.com" + erin := "erin@example.com" + + // Seven entries. alice gains a version in every one; bob gains version 0 in the first and + // version 1 in the last, so nothing happens to it in between; carol gains three versions at + // once in the last; dave appears for the first time in the last. Four labels, four different + // shapes of the same algorithm over one tree. + log := [][]labelValue{ + {lv(alice, "alice-1"), lv(bob, "bob-1"), lv(erin, "erin-1")}, + {lv(alice, "alice-2")}, + {lv(alice, "alice-3")}, + {lv(alice, "alice-4")}, + {lv(alice, "alice-5")}, + {lv(alice, "alice-6")}, + { + lv(alice, "alice-7"), lv(bob, "bob-2"), + lv(carol, "carol-1"), lv(carol, "carol-2"), lv(carol, "carol-3"), + lv(dave, "dave-1"), + lv(erin, "erin-2"), lv(erin, "erin-3"), lv(erin, "erin-4"), + }, + } + week := uint64(604800000) + + return []updateCase{ + { + // The owner's last known version is in entry 5, which is the previous tree's + // rightmost entry, so step 2.1 skips it and phase one reads nothing. Step 4 runs at + // entry 6, with the omissions phase one's seeding produced. + name: "single-version-previous-frontier-skipped", + mutations: log, + label: alice, + window: week, + position: 6, + versions: 1, + starting: 3, + verAtStarting: ptr(uint32(3)), + upcoming: []uint64{4, 5}, + note: "§9.1 step 2.1: the ladder for the previous tree's rightmost entry arrived " + + "with the update that created the owner's last version, so this response " + + "carries none.", + }, + { + // Nothing happened to bob between the owner's reference point and the update, so + // step 2.2 has a real ladder to check: entry 5 must still show version 0 as the + // greatest. + name: "single-version-previous-frontier-inspected", + mutations: log, + label: bob, + window: week, + position: 6, + versions: 1, + starting: 3, + verAtStarting: ptr(uint32(0)), + upcoming: nil, + note: "§9.1 step 2.2: a greatest-version search over the previous tree's frontier, " + + "which is the phase that stops a log from creating a version and hiding it.", + }, + { + // Three versions in one entry, and a search ladder for the new greatest version + // happens to cover all three: versions 0, 1 and 2 are all rungs of the ladder for + // version 2. So §9.1's additional proof is empty and no second proof is sent. + name: "multi-version-covered-by-the-ladder", + mutations: log, + label: carol, + window: week, + position: 6, + versions: 3, + starting: 3, + verAtStarting: nil, + upcoming: nil, + note: "Three versions at once, all of them rungs of the ladder for the new greatest " + + "version, so §9.1 asks for no additional inclusion proof.", + }, + { + // The same shape one version along, where it stops being free: versions 1, 2 and 3 + // on top of version 0, and the ladder for version 3 does not look up version 2. So + // step 4 sends a second proof from the same log entry, which is where §12.3.4's rule + // that two proofs for one entry must agree about its prefix tree root does work. + name: "multi-version-with-additional-proof", + mutations: log, + label: erin, + window: week, + position: 6, + versions: 3, + starting: 3, + verAtStarting: ptr(uint32(0)), + upcoming: nil, + note: "Versions 1 to 3 created at once. A ladder for version 3 does not look up " + + "version 2, so §9.1 sends a second prefix proof from the same log entry.", + }, + { + // A label the owner is creating for the first time. The previous greatest version is + // absent rather than zero, and step 2.2's ladder proves version 0 *absent* in the + // previous tree instead of proving a version present. + name: "first-version-of-a-new-label", + mutations: log, + label: dave, + window: week, + position: 6, + versions: 1, + starting: 3, + verAtStarting: nil, + upcoming: nil, + note: "The label did not exist at the owner's reference point, so §9.1 step 2.3 " + + "asks for a ladder consistent with no version existing at all.", + }, + { + // Every entry distinguished. Step 1 finds no non-distinguished entry, so phase one + // is skipped entirely and step 3 asks only for the versions a ladder would miss. + // Recorded deliberately as the degenerate shape: it exercises almost nothing. + name: "distinguished-entry-asks-for-nothing", + mutations: log, + label: carol, + window: 0, + position: 6, + versions: 3, + starting: 3, + verAtStarting: nil, + upcoming: nil, + note: "A window of zero makes every entry distinguished, so §9.1 takes its step 3 " + + "branch — and with every new version covered by the ladder there is nothing " + + "left to ask for. The whole proof is the view update.", + }, + { + // Distinguished, but with a version the ladder misses, so step 3 does have something + // to ask for: an inclusion proof and nothing else. + name: "distinguished-entry-additional-proof", + mutations: log, + label: erin, + window: 0, + position: 6, + versions: 3, + starting: 3, + verAtStarting: ptr(uint32(0)), + upcoming: nil, + note: "§9.1 step 3 on its own: the entry is distinguished so no ladder is sent, but " + + "version 2 is not a rung of the ladder for version 3 and still needs proving.", + }, + { + // The same query as the first case, but the owner advertises a tree size it has + // already seen. §12.3 then omits the timestamps it retained, so the proof is a + // different shape for the same algorithm. + name: "single-version-with-advertised-size", + mutations: log, + label: alice, + window: week, + position: 6, + versions: 1, + starting: 3, + verAtStarting: ptr(uint32(3)), + upcoming: []uint64{4, 5}, + last: ptr(uint64(5)), + note: "The owner advertises a tree size it has seen, so the view update sends §4.2's " + + "list rather than the whole frontier and the retained timestamps are omitted.", + }, + } +} + +// updateVectors covers draft §9.1 and §13.5. +func updateVectors(sha string) (*File, error) { + cs := suites.KTSha256Ed25519{} + + f := &File{ + Primitive: "update", + Draft: draftRev + " §9.1, §13.5", + Generator: Generator{Impl: "katie", SHA: sha}, + CipherSuite: 0x0002, // KT_128_SHA256_Ed25519 + Notes: "CombinedTreeProof structures for §9.1, produced by katie's own algorithm " + + "implementation. `mutations` describes how the log was built, `owner` is the label " + + "owner's state before the update, and `ladder` gives every search key the proof's " + + "lookups need — the union of what the owner already held and what the response " + + "would add. These carry no UpdateResponse envelope: katie's tree.Update cannot " + + "answer any request (see KT-04), so the envelope was never measured and is not " + + "claimed. The proof's elements carry no indication of which log entry they belong " + + "to; they are in the order §9.1 asks for them.", + } + + for _, spec := range updateCases() { + tree, store, config, timestamps, layout, err := buildUpdateLog(cs, spec) + if err != nil { + return nil, fmt.Errorf("case %q: building the log: %w", spec.name, err) + } + index, ok := layout[spec.label] + if !ok { + return nil, fmt.Errorf("case %q: label %q has no versions", spec.name, spec.label) + } + size := tree.TreeHead().TreeSize + if spec.position >= size { + return nil, fmt.Errorf( + "case %q: position %d is outside a log of %d", spec.name, spec.position, size) + } + + // The versions the case says were created must really be the ones in `position`, or the + // case would be checking a claim the log does not make. + created := 0 + for _, at := range index { + if at == spec.position { + created++ + } + } + if created != spec.versions { + return nil, fmt.Errorf( + "case %q: %d versions were created in entry %d, not %d", + spec.name, created, spec.position, spec.versions) + } + + handle := algorithms.NewProducedProofHandle(cs, store, index) + provider := algorithms.NewDataProvider(cs, handle) + if spec.last != nil { + retained, err := retainedEntries(cs, store, *spec.last) + if err != nil { + return nil, fmt.Errorf("case %q: retained entries: %w", spec.name, err) + } + if err := provider.AddRetained(nil, retained); err != nil { + return nil, fmt.Errorf("case %q: adding retained entries: %w", spec.name, err) + } + } + if err := algorithms.UpdateView(config, size, spec.last, provider); err != nil { + return nil, fmt.Errorf("case %q: updating the view: %w", spec.name, err) + } + monitor, err := algorithms.NewMonitor(config, size, provider) + if err != nil { + return nil, fmt.Errorf("case %q: creating the monitor: %w", spec.name, err) + } + // The state tree.Update would have loaded from the client's store, and the one thing its + // own code path leaves nil. + monitor.Owner = &algorithms.OwnerState{ + Starting: spec.starting, + VerAtStarting: verAtStarting(spec.verAtStarting), + UpcomingVers: slices.Clone(spec.upcoming), + } + if err := monitor.Update(spec.position, spec.versions); err != nil { + return nil, fmt.Errorf("case %q: §9.1: %w", spec.name, err) + } + + // Output needs a VRF output for every version the proof looks up, which is only known + // once the algorithm has run. The commitments come back the same way, populated from the + // prefix tree searches the output performs. + required := maps(handle.RequiredVersions()) + slices.Sort(required) + for _, ver := range required { + input, err := structs.Marshal(&structs.VrfInput{ + Label: []byte(spec.label), + Version: ver, + }) + if err != nil { + return nil, fmt.Errorf("case %q: marshalling the VRF input: %w", spec.name, err) + } + output, _ := privateVrfKey().Prove(input) + if err := handle.AddVersion(ver, output); err != nil { + return nil, fmt.Errorf("case %q: version %d: %w", spec.name, ver, err) + } + } + proof, err := provider.Output(size, nil, spec.last) + if err != nil { + return nil, fmt.Errorf("case %q: producing the proof: %w", spec.name, err) + } + + var buf bytes.Buffer + if err := proof.Marshal(&buf); err != nil { + return nil, fmt.Errorf("case %q: marshalling the proof: %w", spec.name, err) + } + reread := bytes.NewBuffer(buf.Bytes()) + if _, err := structs.NewCombinedTreeProof(cs, reread); err != nil { + return nil, fmt.Errorf("case %q: katie cannot parse its own proof: %w", spec.name, err) + } else if reread.Len() != 0 { + return nil, fmt.Errorf("case %q: %d bytes left after the proof", spec.name, reread.Len()) + } + + ladder := make([]map[string]any, 0, len(required)) + for _, ver := range required { + input, err := structs.Marshal(&structs.VrfInput{ + Label: []byte(spec.label), + Version: ver, + }) + if err != nil { + return nil, fmt.Errorf("case %q: marshalling the VRF input: %w", spec.name, err) + } + output, _ := privateVrfKey().Prove(input) + entry := map[string]any{ + "version": ver, + "vrf_output": hex.EncodeToString(output), + } + if commitment := handle.GetCommitment(ver); commitment != nil { + entry["commitment"] = hex.EncodeToString(commitment) + } + ladder = append(ladder, entry) + } + + owner := map[string]any{ + "starting": spec.starting, + "upcoming": indices(spec.upcoming), + } + if spec.verAtStarting != nil { + owner["version_at_starting"] = *spec.verAtStarting + } + + input := map[string]any{ + "mutations": updateMutationsJSON(spec.mutations), + "label": hex.EncodeToString([]byte(spec.label)), + "mode": uint8(config.Mode), + "vrf_public_key": hex.EncodeToString(config.VrfKey.Bytes()), + "monitoring_window": spec.window, + "tree_size": size, + "position": spec.position, + "versions": spec.versions, + "owner": owner, + "entry_timestamps": indices(timestamps), + "ladder": ladder, + "note": spec.note, + "signature_public_key": hex.EncodeToString(config.SignatureKey.Bytes()), + } + if spec.last != nil { + input["last"] = *spec.last + } + + expect := map[string]any{ + "proof": hex.EncodeToString(buf.Bytes()), + "timestamps": indices(proof.Timestamps), + "prefix_proofs": prefixProofsJSON(proof.PrefixProofs), + "prefix_roots": hexAll(proof.PrefixRoots), + "inclusion": hexAll(proof.Inclusion.Elements), + // Whether the entry holding the new versions is distinguished decides which branch + // §9.1 takes, and katie reports it by whether it wrote a contact monitoring entry: + // step 4 adds one, step 3 does not. + "distinguished": monitor.Contact == nil || len(monitor.Contact.Ptrs) == 0, + } + if monitor.Contact != nil { + if version, ok := monitor.Contact.Ptrs[spec.position]; ok { + expect["contact"] = map[string]any{ + "position": spec.position, + "version": version, + } + } + } + + f.Cases = append(f.Cases, Case{ + Name: spec.name, + Input: input, + Expect: expect, + }) + } + + return f, nil +} + +// verAtStarting converts the owner's greatest version at its reference point to katie's +// representation, where -1 means the label did not exist. +func verAtStarting(version *uint32) int { + if version == nil { + return -1 + } + return int(*version) +} + +// maps returns the keys of a set as a slice, since RequiredVersions hands back a map and Go +// randomizes map iteration — a vector file has to come out the same way every run. +func maps[K comparable](set map[K]struct{}) []K { + out := make([]K, 0, len(set)) + for key := range set { + out = append(out, key) + } + return out +} + +func updateMutationsJSON(mutations [][]labelValue) []map[string]any { + out := make([]map[string]any, 0, len(mutations)) + for _, mutation := range mutations { + labels := make([]map[string]any, 0, len(mutation)) + for _, entry := range mutation { + labels = append(labels, map[string]any{ + "label": hex.EncodeToString(entry.label), + "value": hex.EncodeToString(entry.value), + }) + } + out = append(out, map[string]any{"add": labels}) + } + return out +} + +// privateVrfKey is the VRF key every generated log uses, so that a verifier can be handed the +// public half and check each search key against it. +func privateVrfKey() *edwards25519.PrivateKey { + key, err := edwards25519.NewPrivateKey(repeat(0x74, 32)) + if err != nil { + // The key material is a constant; failing to parse it is a programming error, not + // something a caller could handle. + panic(fmt.Sprintf("parsing the VRF key: %v", err)) + } + return key +} + +// retainedEntries loads the log entries a user who advertised tree size `last` would have kept: +// its frontier, with each entry's timestamp and prefix tree root. +func retainedEntries( + cs suites.CipherSuite, store db.TransparencyStore, last uint64, +) (map[uint64]structs.LogEntry, error) { + frontier := ktmath.Frontier(last) + raw, err := store.BatchGet(frontier) + if err != nil { + return nil, err + } + out := make(map[uint64]structs.LogEntry, len(frontier)) + for _, position := range frontier { + encoded, ok := raw[position] + if !ok { + return nil, fmt.Errorf("no log entry at %d", position) + } + buf := bytes.NewBuffer(encoded) + entry, err := structs.NewLogEntry(cs, buf) + if err != nil { + return nil, fmt.Errorf("log entry %d: %w", position, err) + } else if buf.Len() != 0 { + return nil, fmt.Errorf("log entry %d has %d trailing bytes", position, buf.Len()) + } + out[position] = *entry + } + return out, nil +} + +// buildUpdateLog runs a log through the case's mutations, returning it along with the store the +// proof is produced from, the timestamp of each entry, and each label's index — the log entry +// where each of its versions was created. +// +// The index is recorded as the log is built rather than read back out of the store, because the +// stored form is a delta-encoded series of uvarints that only katie's unexported reader decodes. +// Building it here means this harness never has to reimplement a storage format. +func buildUpdateLog( + cs suites.CipherSuite, spec updateCase, +) ( + *transparency.Tree, + db.TransparencyStore, + *structs.PublicConfig, + []uint64, + map[string][]uint64, + error, +) { + logKey, err := cs.ParseSigningPrivateKey(repeat(0x71, 32)) + if err != nil { + return nil, nil, nil, nil, nil, fmt.Errorf("parsing the log signing key: %w", err) + } + private := structs.PrivateConfig{ + SignatureKey: logKey, + VrfKey: privateVrfKey(), + Config: structs.Config{ + Suite: cs, + Mode: structs.ContactMonitoring, + MaxAhead: 10000, + MaxBehind: 10000, + ReasonableMonitoringWindow: spec.window, + }, + } + store := memory.NewTransparencyStore() + tree, err := transparency.NewTree(private, store, nil) + if err != nil { + return nil, nil, nil, nil, nil, fmt.Errorf("creating the tree: %w", err) + } + + timestamps := make([]uint64, 0, len(spec.mutations)) + layout := make(map[string][]uint64) + for i, mutation := range spec.mutations { + position := uint64(0) + if head := tree.TreeHead(); head != nil { + position = head.TreeSize + } + add := make([]transparency.LabelValue, 0, len(mutation)) + for _, entry := range mutation { + add = append(add, transparency.LabelValue{ + Label: entry.label, + Value: structs.UpdateValue{Value: entry.value}, + }) + layout[string(entry.label)] = append(layout[string(entry.label)], position) + } + update, err := tree.Mutate(add, nil) + if err != nil { + return nil, nil, nil, nil, nil, fmt.Errorf("mutation %d: %w", i, err) + } + timestamps = append(timestamps, update.Timestamp) + } + return tree, store, private.Public(), timestamps, layout, nil +} diff --git a/interop/go/cmd/gen/vrf.go b/interop/go/cmd/gen/vrf.go index f5c2904..43c5d24 100644 --- a/interop/go/cmd/gen/vrf.go +++ b/interop/go/cmd/gen/vrf.go @@ -21,7 +21,25 @@ import ( // vrfVectors covers draft §11.7 for KT_128_SHA256_Ed25519. func vrfVectors(sha string) (*File, error) { - cs := suites.KTSha256Ed25519{} + return vrfVectorsFor(sha, suites.KTSha256Ed25519{}, + "ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381) over the presentation-language "+ + "encoding of a VrfInput, with the output truncated to VRF.Nh = 32 bytes per "+ + "§17.1. ") +} + +// vrfVectorsP256 covers the same section for KT_128_SHA256_P256, where the ECVRF +// ciphersuite, the hash, the integer byte order and every encoded length differ. The +// cases are the same inputs, so a diff between the two files is entirely attributable +// to the suite. +func vrfVectorsP256(sha string) (*File, error) { + return vrfVectorsFor(sha, suites.KTSha256P256{}, + "ECVRF-P256-SHA256-TAI (RFC 9381) over the presentation-language encoding of a "+ + "VrfInput. Unlike the Ed25519 suite there is no truncation — beta_string is "+ + "already VRF.Nh = 32 bytes — the integers are big-endian, an encoded point is "+ + "33 bytes SEC1 compressed, and VRF.Np is 81. ") +} + +func vrfVectorsFor(sha string, cs suites.CipherSuite, notes string) (*File, error) { // A fixed key, so regeneration is a no-op diff. Deliberately not the RFC 9381 // key: those vectors are already run directly, and reusing one here would @@ -57,12 +75,11 @@ func vrfVectors(sha string) (*File, error) { Primitive: "vrf", Draft: draftRev + " §11.7", Generator: Generator{Impl: "katie", SHA: sha}, - CipherSuite: 0x0002, // KT_128_SHA256_Ed25519 - Notes: "ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381) over the presentation-language " + - "encoding of a VrfInput, with the output truncated to VRF.Nh = 32 bytes per " + - "§17.1. `vrf_input` is that encoding, which is what alpha_string must be; " + + CipherSuite: cs.Id(), + Notes: notes + + "`vrf_input` is that encoding, which is what alpha_string must be; " + "`output` is the prefix tree search key for the label-version pair; `proof` is " + - "VRF.Np = 80 bytes. The RFC's own Appendix B vectors pin the ECVRF core and are " + + "VRF.Np bytes. The RFC's own Appendix B vectors pin the ECVRF core and are " + "run directly by the Rust side; these pin the KT wrapping around it. The " + "negative case is a proof for one label-version pair checked against another, " + "which must not verify.", diff --git a/interop/go/cmd/verify/main.go b/interop/go/cmd/verify/main.go index 7203093..21e9811 100644 --- a/interop/go/cmd/verify/main.go +++ b/interop/go/cmd/verify/main.go @@ -78,6 +78,12 @@ type caseEnvelope struct { Versions []versionKey `json:"versions"` Map []mapEntry `json:"map"` + // owner-update — `size`, `timestamps` and `proof` are shared with the kinds above. + Position uint64 `json:"position"` + NewVersions int `json:"new_versions"` + Owner *ownerState `json:"owner"` + Keys []versionKey `json:"keys"` + // auditor-update Encoding string `json:"encoding"` @@ -89,6 +95,14 @@ type mapEntry struct { Version uint32 `json:"version"` } +// ownerState is a label owner's state as §9.1 reads it. VersionAtStarting is -1 where the label +// did not exist at the owner's reference point, which is how katie spells "no version". +type ownerState struct { + Starting uint64 `json:"starting"` + VersionAtStarting int `json:"version_at_starting"` + Upcoming []uint64 `json:"upcoming"` +} + type versionKey struct { Version uint32 `json:"version"` VrfOutput string `json:"vrf_output"` @@ -148,6 +162,8 @@ func main() { err = checkPrefixMutation(cs, c) case "combined-search": err = checkCombinedSearch(cs, c) + case "owner-update": + err = checkOwnerUpdate(cs, c) case "auditor-update": err = checkAuditorUpdate(cs, c) default: @@ -507,3 +523,74 @@ func fatalf(format string, args ...any) { fmt.Fprintf(os.Stderr, "verify: "+format+"\n", args...) os.Exit(1) } + +// checkOwnerUpdate feeds a §9.1 proof built by the Rust side to katie's own reading of the same +// algorithm. +// +// This is the only direction §9.1 can be checked in. katie's `tree.Update` cannot serve an update +// at all — it builds a monitor with NewMonitor, which leaves Owner nil, then calls Monitor.Update, +// which refuses when Owner is nil, so every request comes back "label owner state has not been +// initialized" (KT-04). The consumer half takes the owner state from its caller and works, and it +// is the more valuable half anyway: it is what catches a proof this side would accept and the peer +// would not. +func checkOwnerUpdate(cs suites.CipherSuite, c caseEnvelope) error { + if c.Owner == nil { + return errors.New("no owner state") + } + raw, err := hex.DecodeString(c.Proof) + if err != nil { + return fmt.Errorf("proof: %w", err) + } + buf := bytes.NewBuffer(raw) + proof, err := structs.NewCombinedTreeProof(cs, buf) + if err != nil { + return fmt.Errorf("parsing the proof: %w", err) + } else if buf.Len() != 0 { + return fmt.Errorf("%d bytes left after the proof", buf.Len()) + } + + handle := algorithms.NewReceivedProofHandle(cs, *proof) + for _, k := range c.Keys { + vrfOutput, err := hex.DecodeString(k.VrfOutput) + if err != nil { + return fmt.Errorf("version %d vrf_output: %w", k.Version, err) + } + var commitment []byte + if k.Commitment != nil { + commitment, err = hex.DecodeString(*k.Commitment) + if err != nil { + return fmt.Errorf("version %d commitment: %w", k.Version, err) + } + } + if err := handle.AddVersion(k.Version, vrfOutput, commitment); err != nil { + return fmt.Errorf("version %d: %w", k.Version, err) + } + } + + config, err := searchConfig(cs) + if err != nil { + return err + } + provider := algorithms.NewDataProvider(cs, handle) + // §12.3.1 first, as a client does: the view update consumes the frontier's timestamps before + // §9.1 asks for anything. + if err := algorithms.UpdateView(config, c.Size, nil, provider); err != nil { + return fmt.Errorf("updating the view: %w", err) + } + monitor, err := algorithms.NewMonitor(config, c.Size, provider) + if err != nil { + return fmt.Errorf("creating the monitor: %w", err) + } + monitor.Owner = &algorithms.OwnerState{ + Starting: c.Owner.Starting, + VerAtStarting: c.Owner.VersionAtStarting, + UpcomingVers: c.Owner.Upcoming, + } + if err := monitor.Update(c.Position, c.NewVersions); err != nil { + return fmt.Errorf("§9.1: %w", err) + } + if _, err := provider.Finish(c.Size, nil, nil); err != nil { + return fmt.Errorf("finishing: %w", err) + } + return nil +} diff --git a/interop/report/src/bin/emit.rs b/interop/report/src/bin/emit.rs index 718bdb7..97b7126 100644 --- a/interop/report/src/bin/emit.rs +++ b/interop/report/src/bin/emit.rs @@ -116,6 +116,34 @@ enum Case { /// The wire-encoded `CombinedTreeProof`, hex. proof: String, }, + /// A `CombinedTreeProof` for §9.1, which the peer reads with its own `Monitor.Update`. + /// + /// Separate from [`Case::CombinedSearch`] because §9.1 is checked against three things rather + /// than one: the tree, the response's claims, and the *owner's own state*. That state has no + /// counterpart in a search, and passing it as optional fields on a search case would suggest a + /// search could have one. + OwnerUpdate { + name: String, + expect: &'static str, + /// The log's size. + size: u64, + /// The log entry the new versions were inserted into. + position: u64, + /// How many new versions were created there. + /// + /// Named `new_versions` on the wire because a search case's `versions` is a list of search + /// keys, and one JSON name cannot be both a count and a list. + #[serde(rename = "new_versions")] + versions: usize, + /// The owner's state before the update. + owner: OwnerOut, + /// Every log entry's timestamp, by position. + timestamps: Vec, + /// The search keys the peer needs for the lookups §9.1 makes. + keys: Vec, + /// The wire-encoded `CombinedTreeProof`, hex. + proof: String, + }, /// An `AuditorUpdate` for the peer's decoder (§15.2). AuditorUpdate { name: String, @@ -143,6 +171,19 @@ struct MapEntryOut { version: u32, } +/// A label owner's state, in the peer's representation. +/// +/// `version_at_starting` is `-1` where the label did not exist at the reference point, which is how +/// the peer spells "no version" — it keeps the field an integer and lets the count arithmetic work +/// out. This side models it as an absent version; the conversion happens here rather than in the +/// library, so that the peer's encoding does not leak into a type the protocol defines otherwise. +#[derive(Clone, Serialize)] +struct OwnerOut { + starting: u64, + version_at_starting: i64, + upcoming: Vec, +} + /// A version's search key and commitment, as the peer's proof handle wants them. #[derive(Clone, Serialize)] struct VersionKey { @@ -250,6 +291,7 @@ const fn expect_of(case: &Case) -> &'static str { | Case::PrefixTree { expect, .. } | Case::PrefixMutation { expect, .. } | Case::CombinedSearch { expect, .. } + | Case::OwnerUpdate { expect, .. } | Case::AuditorUpdate { expect, .. } => expect, } } @@ -259,6 +301,7 @@ fn build_cases() -> Result, String> { cases.extend(prefix_cases()?); cases.extend(mutation_cases()?); cases.extend(search_cases()?); + cases.extend(owner_update_cases()?); Ok(cases) } @@ -1081,3 +1124,453 @@ fn prefix_cases() -> Result, String> { Ok(cases) } + +/// §9.1 proofs built by this implementation for the peer's own `Monitor.Update` to consume. +/// +/// This is the only direction §9.1 can be checked in. The forward direction needs katie to *serve* +/// an update, and it cannot: `tree.Update` fails for every request because its own code path leaves +/// the owner state uninitialized before checking it (`KT-04`). The consumer half is sound, and it +/// takes the owner state from the caller — so a proof this side builds can be fed to the peer's +/// reading of the same algorithm, which is the direction that catches over-acceptance anyway. +/// +/// The log model is the same as the search cases': version `v` was added at entry `v`, up to a cap. +/// The cap is what makes an honest step 2.2 possible — a label that gains a version in every entry +/// has nothing for the previous tree's frontier to confirm, since the owner's previous greatest +/// version was never the greatest anywhere except where it was created. +fn owner_update_cases() -> Result, String> { + const SIZE: u64 = 7; + let timestamps: Vec = (0..SIZE) + .map(|i| 1_700_000_000_000_u64.saturating_add(i)) + .collect(); + let window = 604_800_000_u64; + + let key_for = |version: u32| { + let mut bytes = [0_u8; HashValue::SIZE]; + bytes[0] = u8::try_from(version.wrapping_mul(37) % 256).unwrap_or(0); + bytes[HashValue::SIZE - 1] = u8::try_from(version % 256).unwrap_or(0); + HashValue::from_bytes(bytes) + }; + let commitment_for = |version: u32| { + HashValue::from_bytes([u8::try_from(version % 256).unwrap_or(0) ^ 0xa5; HashValue::SIZE]) + }; + // The prefix tree at `position` for a label that gained version `v` in entry `v` until it + // stopped at `cap`, then gained one more in the last entry. + let tree_at = |position: u64, cap: u64| -> Result { + let greatest = if position == SIZE - 1 { + cap.saturating_add(1) + } else { + position.min(cap) + }; + let mut tree = prefix::PrefixTree::new(); + for version in 0..=u32::try_from(greatest).unwrap_or(0) { + tree.insert(PrefixLeaf { + vrf_output: key_for(version), + commitment: commitment_for(version), + }) + .map_err(|err| format!("building the tree at {position}: {err}"))?; + } + Ok(tree) + }; + let greatest_at = |position: u64, cap: u64| -> u32 { + let greatest = if position == SIZE - 1 { + cap.saturating_add(1) + } else { + position.min(cap) + }; + u32::try_from(greatest).unwrap_or(0) + }; + let stamp_at = |position: u64| -> Result { + usize::try_from(position) + .ok() + .and_then(|index| timestamps.get(index)) + .copied() + .ok_or_else(|| format!("no timestamp for entry {position}")) + }; + + // §6.1's descent, over the timestamps directly: which entries a verifier reads on the way to + // `target`, and the first one it finds not distinguished. + let descend = |target: u64| -> Result<(Vec, Option), String> { + let last = SIZE - 1; + let mut reads = vec![last]; + let mut current = ibst::root(SIZE).map_err(|err| format!("root: {err}"))?; + let mut left = (0_u64, 0_u64); + let mut right = (last, stamp_at(last)?); + loop { + let gap = right.1.saturating_sub(left.1); + if gap < window { + return Ok((reads, Some(current))); + } + if current == target { + return Ok((reads, None)); + } + reads.push(current); + let timestamp = stamp_at(current)?; + if current < target { + let next = ibst::right(current, SIZE) + .map_err(|err| format!("right of {current}: {err}"))?; + left = (current, timestamp); + current = next; + } else { + let next = + ibst::left(current).map_err(|err| format!("left of {current}: {err}"))?; + right = (current, timestamp); + current = next; + } + } + }; + + // One honest §9.1 proof, built by walking the algorithm's own steps. + let build = |cap: u64, + position: u64, + owner_starting: u64, + owner_greatest: u32, + owner_upcoming: &[u64]| + -> Result { + let mut stamps: Vec = Vec::new(); + let mut seen: Vec = Vec::new(); + let mut proofs = Vec::new(); + let mut proved: Vec = Vec::new(); + let mut established: Vec<(u64, Vec, Vec)> = Vec::new(); + let sets_for = + |established: &[(u64, Vec, Vec)], position: u64| -> (Vec, Vec) { + let (mut left, mut right) = (Vec::new(), Vec::new()); + for (at, included, absent) in established { + if *at < position { + for version in included { + if !left.contains(version) { + left.push(*version); + } + } + } + if *at > position { + for version in absent { + if !right.contains(version) { + right.push(*version); + } + } + } + } + (left, right) + }; + let record = + |position: u64, stamps: &mut Vec, seen: &mut Vec| -> Result<(), String> { + if !seen.contains(&position) { + stamps.push(stamp_at(position)?); + seen.push(position); + } + Ok(()) + }; + + // §12.3.1's view update: a first-time owner gets the whole frontier. + for entry in ibst::frontier(SIZE).map_err(|err| format!("frontier: {err}"))? { + record(entry, &mut stamps, &mut seen)?; + } + + let last_update = owner_upcoming.last().copied().unwrap_or(owner_starting); + let rightmost = position.saturating_sub(1); + + // Phase one: steps 1 and 2 over the previous tree. + let (reads, first) = descend(rightmost)?; + for entry in reads { + record(entry, &mut stamps, &mut seen)?; + } + if let Some(first) = first { + let mut current = first; + while current > rightmost { + current = ibst::left(current).map_err(|err| format!("left of {current}: {err}"))?; + } + // Step 2.2's omissions, seeded as if the skipped entries had been inspected. + let ladder = ladder::search_binary_ladder(owner_greatest, owner_greatest, &[], &[]) + .map_err(|err| format!("seed ladder: {err}"))?; + let assume = |established: &mut Vec<(u64, Vec, Vec)>, + at: u64, + bound: u32, + versions: &[u32]| { + let (mut included, mut absent) = (Vec::new(), Vec::new()); + for version in versions { + if *version <= bound { + included.push(*version); + } else { + absent.push(*version); + } + } + established.push((at, included, absent)); + }; + let previous_root = ibst::root(position).map_err(|err| format!("root: {err}"))?; + if current != previous_root { + let parent = ibst::direct_path(current, position) + .map_err(|err| format!("direct path: {err}"))? + .first() + .copied() + .ok_or_else(|| format!("entry {current} has no parent"))?; + assume(&mut established, parent, greatest_at(parent, cap), &ladder); + } + assume(&mut established, current, owner_greatest, &ladder); + + loop { + if current > last_update { + let (left_inclusion, right_non_inclusion) = sets_for(&established, current); + let versions = ladder::search_binary_ladder( + owner_greatest, + greatest_at(current, cap), + &left_inclusion, + &right_non_inclusion, + ) + .map_err(|err| format!("ladder at {current}: {err}"))?; + let searches: Vec = + versions.iter().map(|version| key_for(*version)).collect(); + let proof = tree_at(current, cap)? + .prove(SUITE, &searches) + .map_err(|err| format!("proving at {current}: {err}"))?; + let (mut included, mut absent) = (Vec::new(), Vec::new()); + for (version, result) in versions.iter().zip(proof.results.iter()) { + if result.is_inclusion() { + included.push(*version); + } else { + absent.push(*version); + } + } + established.push((current, included, absent)); + record(current, &mut stamps, &mut seen)?; + proofs.push(proof); + proved.push(current); + } + if current == rightmost { + break; + } + current = ibst::right(current, position) + .map_err(|err| format!("right of {current}: {err}"))?; + } + } + + // Phase two: step 3 or step 4 at the entry holding the new versions. + let (reads, first) = descend(position)?; + for entry in reads { + record(entry, &mut stamps, &mut seen)?; + } + let end_ver = greatest_at(position, cap); + if first.is_some() { + let (left_inclusion, right_non_inclusion) = sets_for(&established, position); + let versions = ladder::search_binary_ladder( + end_ver, + greatest_at(position, cap), + &left_inclusion, + &right_non_inclusion, + ) + .map_err(|err| format!("ladder at {position}: {err}"))?; + let searches: Vec = + versions.iter().map(|version| key_for(*version)).collect(); + let proof = tree_at(position, cap)? + .prove(SUITE, &searches) + .map_err(|err| format!("proving at {position}: {err}"))?; + record(position, &mut stamps, &mut seen)?; + proofs.push(proof); + proved.push(position); + } + + // The new versions a ladder for the new greatest version would not look up. + let covered = ladder::search_binary_ladder(end_ver, end_ver, &[], &[]) + .map_err(|err| format!("covered ladder: {err}"))?; + let additional: Vec = (owner_greatest.saturating_add(1)..=end_ver) + .filter(|version| !covered.contains(version)) + .collect(); + if !additional.is_empty() { + let searches: Vec = + additional.iter().map(|version| key_for(*version)).collect(); + record(position, &mut stamps, &mut seen)?; + proofs.push( + tree_at(position, cap)? + .prove(SUITE, &searches) + .map_err(|err| format!("proving at {position}: {err}"))?, + ); + proved.push(position); + } + + // §12.3.2: a prefix root for every entry with a timestamp and no proof, ascending. + let mut owed: Vec = seen + .iter() + .copied() + .filter(|entry| !proved.contains(entry)) + .collect(); + owed.sort_unstable(); + let mut prefix_roots = Vec::new(); + for entry in &owed { + prefix_roots.push(tree_at(*entry, cap)?.root(SUITE)); + } + + let mut leaves = Vec::new(); + for entry in 0..SIZE { + let log_entry = LogEntry { + timestamp: stamp_at(entry)?, + prefix_tree: tree_at(entry, cap)?.root(SUITE), + }; + leaves.push( + log::leaf_value(SUITE, &log_entry).map_err(|err| format!("leaf {entry}: {err}"))?, + ); + } + let mut inspected = seen.clone(); + inspected.sort_unstable(); + let inclusion = log::prove(SUITE, &leaves, &inspected, None) + .map_err(|err| format!("log proof: {err}"))?; + + Ok(CombinedTreeProof { + timestamps: stamps, + prefix_proofs: proofs, + prefix_roots, + inclusion, + }) + }; + + let encoded = |proof: &CombinedTreeProof| -> Result { + codec::encode(proof) + .map(hex::encode) + .map_err(|err| format!("encoding: {err}")) + }; + let keys_through = |greatest: u32| -> Vec { + (0..=greatest.saturating_add(4)) + .map(|version| VersionKey { + version, + vrf_output: hex::encode(key_for(version).as_bytes()), + commitment: (version <= greatest) + .then(|| hex::encode(commitment_for(version).as_bytes())), + }) + .collect() + }; + + // The owner's last version went into the previous tree's rightmost entry, so step 2.1 skips it + // and phase one reads nothing. This is the common case for an owner updating a label it updates + // often. + let skipped = build(5, 6, 3, 5, &[4, 5])?; + let mut cases = vec![Case::OwnerUpdate { + name: "owner-update-previous-frontier-skipped".to_owned(), + expect: ACCEPT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: vec![4, 5], + }, + timestamps: timestamps.clone(), + keys: keys_through(6), + proof: encoded(&skipped)?, + }]; + + // The label stopped gaining versions at entry 3, so the previous tree's frontier has something + // to confirm and step 2.2 sends a real ladder. This is the case that exercises the phase §9.1 + // exists for. + let inspected = build(3, 6, 3, 3, &[])?; + cases.push(Case::OwnerUpdate { + name: "owner-update-previous-frontier-inspected".to_owned(), + expect: ACCEPT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: Vec::new(), + }, + timestamps: timestamps.clone(), + keys: keys_through(4), + proof: encoded(&inspected)?, + }); + + // The negatives. Each is a proof this side builds deliberately wrong; the peer must refuse it. + let mut extra = inspected.clone(); + extra.timestamps.push(1_700_000_000_099); + cases.push(Case::OwnerUpdate { + name: "owner-update-extra-timestamp".to_owned(), + expect: REJECT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: Vec::new(), + }, + timestamps: timestamps.clone(), + keys: keys_through(4), + proof: encoded(&extra)?, + }); + + let mut short = inspected.clone(); + short.prefix_proofs.pop(); + cases.push(Case::OwnerUpdate { + name: "owner-update-missing-prefix-proof".to_owned(), + expect: REJECT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: Vec::new(), + }, + timestamps: timestamps.clone(), + keys: keys_through(4), + proof: encoded(&short)?, + }); + + let mut swapped = inspected.clone(); + swapped.prefix_proofs.reverse(); + cases.push(Case::OwnerUpdate { + name: "owner-update-proofs-out-of-order".to_owned(), + expect: REJECT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: Vec::new(), + }, + timestamps: timestamps.clone(), + keys: keys_through(4), + proof: encoded(&swapped)?, + }); + + // §13.5 step 1, checked on the peer: an honest proof presented with an owner state that has + // already passed the claimed position. Nothing about the bytes is wrong, and the peer must + // still refuse — a new version cannot appear to the left of one the owner already holds. + cases.push(Case::OwnerUpdate { + name: "owner-update-position-not-advancing".to_owned(), + expect: REJECT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 3, + upcoming: vec![4, 5, 6], + }, + timestamps: timestamps.clone(), + keys: keys_through(6), + proof: encoded(&skipped)?, + }); + + // A label with exactly one version before the update, which makes every ladder in the proof as + // short as it can be: the seeding ladder for version 0 is two rungs, and step 2.2's is one. + // Short ladders are where an off-by-one in the omission bookkeeping shows up, because there is + // no slack left in the sequence. + let fresh = build(0, 6, 3, 0, &[])?; + cases.push(Case::OwnerUpdate { + name: "owner-update-shortest-possible-ladders".to_owned(), + expect: ACCEPT, + size: SIZE, + position: 6, + versions: 1, + owner: OwnerOut { + starting: 3, + version_at_starting: 0, + upcoming: Vec::new(), + }, + timestamps: timestamps.clone(), + keys: keys_through(1), + proof: encoded(&fresh)?, + }); + + Ok(cases) +} diff --git a/interop/report/src/check.rs b/interop/report/src/check.rs index 68db222..40b54bb 100644 --- a/interop/report/src/check.rs +++ b/interop/report/src/check.rs @@ -15,12 +15,12 @@ use kt_crypto::suite::CipherSuite; use kt_crypto::{signature, vrf}; use kt_tree::{audit, combined, ibst, ladder, log, prefix}; use kt_wire::audit::AuditorUpdate; -use kt_wire::codec::Decoder; +use kt_wire::codec::{Decode as _, Decoder}; use kt_wire::heads::{ AuditorConfig, AuditorTreeHead, AuditorTreeHeadTBS, Configuration, FullTreeHead, TreeHead, TreeHeadTBS, }; -use kt_wire::proofs::{InclusionProof, PrefixLeaf, PrefixProof}; +use kt_wire::proofs::{CombinedTreeProof, InclusionProof, PrefixLeaf, PrefixProof}; use kt_wire::requests::{ BinaryLadderStep, ContactMonitorRequest, LabelValue, MonitorMapEntry, OwnerInitRequest, OwnerMonitorRequest, SearchRequest, UpdateInfo, UpdateRequest, UpdateTBS, @@ -38,13 +38,13 @@ use crate::vectors::{ DistinguishedExpect, DistinguishedInput, HeadExpect, HeadInput, IbstExpect, IbstInput, InterpretationExpect, InterpretationInput, LadderExpect, LadderInput, LogMathExpect, LogMathInput, LogTreeExpect, LogTreeInput, MonitorExpect, MonitorInput, MutationExpect, - MutationInput, PrefixTreeExpect, PrefixTreeInput, RequestExpect, RequestInput, SearchExpect, - SearchInput, TamperedExpect, TamperedInput, UpdateViewExpect, UpdateViewInput, VectorFile, - VrfCaseInput, VrfExpect, + MutationInput, OwnerUpdateExpect, OwnerUpdateInput, PrefixTreeExpect, PrefixTreeInput, + RequestExpect, RequestInput, SearchExpect, SearchInput, TamperedExpect, TamperedInput, + UpdateViewExpect, UpdateViewInput, VectorFile, VrfCaseInput, VrfExpect, }; /// The vector files this crate knows how to check, in dependency order. -pub const FILES: [&str; 18] = [ +pub const FILES: [&str; 21] = [ "commitment.json", "ibst.json", "binary-ladder.json", @@ -52,6 +52,7 @@ pub const FILES: [&str; 18] = [ "update-view.json", "distinguished.json", "vrf.json", + "vrf-p256.json", "log-math.json", "log-tree.json", "log-append.json", @@ -60,7 +61,9 @@ pub const FILES: [&str; 18] = [ "auditor-update.json", "search.json", "monitor.json", + "update.json", "tree-head.json", + "tree-head-p256.json", "requests.json", "tampered.json", ]; @@ -176,6 +179,7 @@ pub fn run(dir: &Path) -> Result, Error> { update_view_suite(dir)?, distinguished_suite(dir)?, vrf_suite(dir)?, + vrf_p256_suite(dir)?, log_math_suite(dir)?, log_tree_suite(dir)?, append_suite(dir)?, @@ -184,7 +188,9 @@ pub fn run(dir: &Path) -> Result, Error> { auditor_suite(dir)?, search_suite(dir)?, monitor_suite(dir)?, + owner_update_suite(dir)?, head_suite(dir)?, + head_p256_suite(dir)?, request_suite(dir)?, tampered_suite(dir)?, ]) @@ -1084,7 +1090,8 @@ fn search_suite(dir: &Path) -> Result { checks.push(Check::new( "replaying §6.3 consumes the proof exactly (§12.3)", if case.name == "label-does-not-exist" { - "every element read, none left over (the label has no versions)" + "every element read, none left over \ + (a negative result, which §13.1 requires rejecting)" } else { "every element read, none left over" }, @@ -1147,7 +1154,9 @@ fn ladder_keys( let label = hex::decode(&case.input.label).map_err(|err| format!("label: {err}"))?; let vrf_key = <[u8; 32]>::try_from(vrf_key.as_slice()) .map_err(|_| "the VRF public key is not 32 bytes".to_owned()) - .and_then(|bytes| vrf::PublicKey::from_bytes(bytes).map_err(|err| err.to_string()))?; + .and_then(|bytes| { + vrf::edwards25519::PublicKey::from_bytes(bytes).map_err(|err| err.to_string()) + })?; // The response's ladder can be shorter than §5's full sequence, for the same reason a // per-entry ladder can: the sequence stops once it has placed the greatest version. So the @@ -1182,7 +1191,7 @@ fn ladder_keys( label: label.clone(), version: *version, }; - let proof = vrf::Proof::from_slice(&step.proof) + let proof = vrf::edwards25519::Proof::from_slice(&step.proof) .map_err(|err| format!("version {version}: {err}"))?; let output = vrf_key .verify(suite, &input, &proof) @@ -1249,7 +1258,10 @@ fn replay_greatest_version( // when the user advertised a size. let view = match case.input.last { None => ibst::frontier(size).map_err(|err| format!("frontier: {err}"))?, - Some(advertised) => ibst::update_view(size, Some(advertised)) + // The peer's procedure, not the current text's: a proof's elements are ordered by + // the algorithm that *built* it (§12.3), and the peer runs §4.2 as it read before + // 2026-07-28. See `ibst::update_view_ancestors_only`. + Some(advertised) => ibst::update_view_ancestors_only(size, Some(advertised)) .map_err(|err| format!("update view: {err}"))?, }; for position in &view { @@ -1270,9 +1282,10 @@ fn replay_greatest_version( let (inspected_pairs, note) = match &outcome { combined::Outcome::Found(search) => (&search.inspected, ""), // The label has no versions. §6.3 does not describe this response; see DRAFT-08. - combined::Outcome::NoVersions { inspected, .. } => { - (inspected, " (the label has no versions)") - } + combined::Outcome::NegativeResult { inspected, .. } => ( + inspected, + " (a negative result, which §13.1 requires rejecting)", + ), }; // Any entry that got a timestamp but no proof needs its prefix root, which is what @@ -1325,7 +1338,10 @@ fn replay_fixed_version( // §12.3.1's view update comes first, exactly as for a greatest-version search. let view = match case.input.last { None => ibst::frontier(size).map_err(|err| format!("frontier: {err}"))?, - Some(advertised) => ibst::update_view(size, Some(advertised)) + // The peer's procedure, not the current text's: a proof's elements are ordered by + // the algorithm that *built* it (§12.3), and the peer runs §4.2 as it read before + // 2026-07-28. See `ibst::update_view_ancestors_only`. + Some(advertised) => ibst::update_view_ancestors_only(size, Some(advertised)) .map_err(|err| format!("update view: {err}"))?, }; for position in &view { @@ -1531,7 +1547,9 @@ fn replay_contact_monitor( hex::decode(&case.input.vrf_public_key).map_err(|err| format!("vrf key: {err}"))?; let vrf_key = <[u8; 32]>::try_from(vrf_key.as_slice()) .map_err(|_| "the VRF public key is not 32 bytes".to_owned()) - .and_then(|value| vrf::PublicKey::from_bytes(value).map_err(|err| err.to_string()))?; + .and_then(|value| { + vrf::edwards25519::PublicKey::from_bytes(value).map_err(|err| err.to_string()) + })?; // What the client already holds. A monitoring response carries no ladder and no // commitments, so these come from the user's own state — recorded by the generator as a @@ -1590,7 +1608,10 @@ fn replay_contact_monitor( // §12.3.4's view update comes first, as for every other operation. let view = match case.input.last { None => ibst::frontier(size).map_err(|err| format!("frontier: {err}"))?, - Some(advertised) => ibst::update_view(size, Some(advertised)) + // The peer's procedure, not the current text's: a proof's elements are ordered by + // the algorithm that *built* it (§12.3), and the peer runs §4.2 as it read before + // 2026-07-28. See `ibst::update_view_ancestors_only`. + Some(advertised) => ibst::update_view_ancestors_only(size, Some(advertised)) .map_err(|err| format!("update view: {err}"))?, }; for position in &view { @@ -1644,7 +1665,9 @@ fn replay_owner_init( hex::decode(&case.input.vrf_public_key).map_err(|err| format!("vrf key: {err}"))?; let vrf_key = <[u8; 32]>::try_from(vrf_key.as_slice()) .map_err(|_| "the VRF public key is not 32 bytes".to_owned()) - .and_then(|value| vrf::PublicKey::from_bytes(value).map_err(|err| err.to_string()))?; + .and_then(|value| { + vrf::edwards25519::PublicKey::from_bytes(value).map_err(|err| err.to_string()) + })?; // §8.3 step 3: the ladder covers version zero and every version a search ladder for any of // the greatest versions would look up. Recovering which version each step is for means @@ -1674,7 +1697,7 @@ fn replay_owner_init( label: label.clone(), version: *version, }; - let proof = vrf::Proof::from_slice(&step.proof) + let proof = vrf::edwards25519::Proof::from_slice(&step.proof) .map_err(|err| format!("version {version}: {err}"))?; let output = vrf_key .verify(suite, &input, &proof) @@ -2392,7 +2415,7 @@ fn vrf_suite(dir: &Path) -> Result { let mut cases = Vec::new(); for case in &file.cases { let name = case.name.as_str(); - let seed: [u8; vrf::SECRET_KEY_SIZE] = + let seed: [u8; vrf::edwards25519::SECRET_KEY_SIZE] = unhex(FILE, name, "private_key", &case.input.private_key)? .try_into() .map_err(|_| Error::Hex { @@ -2400,7 +2423,7 @@ fn vrf_suite(dir: &Path) -> Result { case: name.to_owned(), field: "private_key".to_owned(), })?; - let secret = vrf::SecretKey::from_seed(seed); + let secret = vrf::edwards25519::SecretKey::from_seed(seed); let label = unhex(FILE, name, "label", &case.input.label)?; let input = VrfInput::new(label, case.input.version); @@ -2423,7 +2446,7 @@ fn vrf_suite(dir: &Path) -> Result { field: "input.proof".to_owned(), })?; let bytes = unhex(FILE, name, "input.proof", raw)?; - let got = match vrf::Proof::from_slice(&bytes) { + let got = match vrf::edwards25519::Proof::from_slice(&bytes) { Err(err) => format!("unusable proof: {err}"), Ok(proof) => match secret.public_key().verify(suite, &input, &proof) { Err(_) => "rejected".to_owned(), @@ -2494,13 +2517,13 @@ fn vrf_suite(dir: &Path) -> Result { // And the peer's own proof must verify and yield the peer's output — // the direction a client actually runs. let bytes = unhex(FILE, name, "expect.proof", expected_proof)?; - let public = vrf::PublicKey::from_slice(&unhex( + let public = vrf::edwards25519::PublicKey::from_slice(&unhex( FILE, name, "public_key", &case.input.public_key, )?); - let verified = match (public, vrf::Proof::from_slice(&bytes)) { + let verified = match (public, vrf::edwards25519::Proof::from_slice(&bytes)) { (Err(err), _) => format!("unusable public key: {err}"), (_, Err(err)) => format!("unusable proof: {err}"), (Ok(public), Ok(proof)) => { @@ -2654,8 +2677,8 @@ fn tampered_suite(dir: &Path) -> Result { let bytes = unhex(FILE, name, "proof", proof)?; let input = VrfInput::new(unhex(FILE, name, "label", label)?, *version); let got = match ( - vrf::PublicKey::from_slice(&key), - vrf::Proof::from_slice(&bytes), + vrf::edwards25519::PublicKey::from_slice(&key), + vrf::edwards25519::Proof::from_slice(&bytes), ) { (Err(err), _) => format!("rejected with the key: {err}"), (_, Err(err)) => format!("rejected with the proof: {err}"), @@ -2768,12 +2791,24 @@ fn tampered_suite(dir: &Path) -> Result { /// §11.2, §11.3, §11.4: the configuration and the signatures over it. fn head_suite(dir: &Path) -> Result { - const FILE: &str = "tree-head.json"; - let file: VectorFile = load(dir, FILE)?; + head_suite_file(dir, "tree-head.json", "Signed tree heads") +} + +/// The same set under `KT_128_SHA256_P256`: ECDSA over SHA-256, and `0x0001` in every +/// `Configuration`. Not reproducible, because ECDSA signing draws a nonce — which makes CI's +/// regeneration run the stronger check, since each one verifies signatures nobody has seen. +fn head_p256_suite(dir: &Path) -> Result { + head_suite_file(dir, "tree-head-p256.json", "Signed tree heads, ECDSA/P-256") +} + +fn head_suite_file(dir: &Path, name: &str, title: &str) -> Result { + // The two suites' files are the same shape, so only the name and the title differ. + let file_name = name.to_owned(); + let file: VectorFile = load(dir, name)?; let code = file.cipher_suite.unwrap_or_default(); let suite = CipherSuite::from_code(code).map_err(|_| Error::CipherSuite { - file: FILE.to_owned(), + file: file_name.clone(), value: code, })?; @@ -2781,7 +2816,7 @@ fn head_suite(dir: &Path) -> Result { for case in &file.cases { let name = case.name.as_str(); let mode = DeploymentMode::from_u8(case.input.mode).map_err(|_| Error::Computation { - file: FILE.to_owned(), + file: file_name.clone(), case: name.to_owned(), detail: format!("unknown deployment mode {}", case.input.mode), })?; @@ -2790,22 +2825,22 @@ fn head_suite(dir: &Path) -> Result { cipher_suite: code, mode, signature_public_key: unhex( - FILE, + name, name, "signature_public_key", &case.input.signature_public_key, )?, - vrf_public_key: unhex(FILE, name, "vrf_public_key", &case.input.vrf_public_key)?, + vrf_public_key: unhex(name, name, "vrf_public_key", &case.input.vrf_public_key)?, leaf_public_key: match &case.input.leaf_public_key { None => None, - Some(key) => Some(unhex(FILE, name, "leaf_public_key", key)?), + Some(key) => Some(unhex(name, name, "leaf_public_key", key)?), }, auditor: match &case.input.auditor_public_key { None => None, Some(key) => Some(AuditorConfig { max_auditor_lag: case.input.max_auditor_lag.unwrap_or_default(), auditor_start_pos: case.input.auditor_start_pos.unwrap_or_default(), - auditor_public_key: unhex(FILE, name, "auditor_public_key", key)?, + auditor_public_key: unhex(name, name, "auditor_public_key", key)?, }), }, max_ahead: case.input.max_ahead, @@ -2813,7 +2848,7 @@ fn head_suite(dir: &Path) -> Result { reasonable_monitoring_window: case.input.reasonable_monitoring_window, maximum_lifetime: None, }; - let root = hash_field(FILE, name, "root", &case.input.root)?; + let root = hash_field(name, name, "root", &case.input.root)?; // The configuration's encoding, which every signature depends on. This is // the check that pins §11.2's grouped-case ambiguity: under contact @@ -2827,7 +2862,7 @@ fn head_suite(dir: &Path) -> Result { // And the peer's own bytes must decode back to the same configuration. let peer_config = unhex( - FILE, + name, name, "expect.configuration", &case.expect.configuration, @@ -2855,7 +2890,7 @@ fn head_suite(dir: &Path) -> Result { let head = TreeHead { tree_size: case.input.tree_size, - signature: unhex(FILE, name, "expect.signature", &case.expect.signature)?, + signature: unhex(name, name, "expect.signature", &case.expect.signature)?, }; checks.push(Check::new( "TreeHead encoding (§11.2)", @@ -2883,7 +2918,7 @@ fn head_suite(dir: &Path) -> Result { ("same", &case.expect.full_tree_head_same), ("updated", &case.expect.full_tree_head_updated), ] { - let bytes = unhex(FILE, name, "expect.full_tree_head", expected)?; + let bytes = unhex(name, name, "expect.full_tree_head", expected)?; let mut dec = Decoder::new(&bytes); let parsed = FullTreeHead::decode_with_mode(&mut dec, mode); checks.push(Check::new( @@ -2932,7 +2967,7 @@ fn head_suite(dir: &Path) -> Result { render_result(kt_wire::codec::encode(&auditor_tbs), hex::encode), )); - let bytes = unhex(FILE, name, "expect.auditor_tree_head", expected_head)?; + let bytes = unhex(name, name, "expect.auditor_tree_head", expected_head)?; let parsed = kt_wire::codec::decode::(&bytes); checks.push(Check::new( "the auditor's signature verifies (§11.3)", @@ -3006,9 +3041,9 @@ fn head_suite(dir: &Path) -> Result { Ok(Suite { primitive: file.primitive, - title: "Tree head signatures".to_owned(), + title: title.to_owned(), draft_section: section_of(&file.draft), - file: FILE.to_owned(), + file: file_name.clone(), generator: Generator { implementation: file.generator.implementation, sha: file.generator.sha, @@ -3028,14 +3063,44 @@ fn update_view_suite(dir: &Path) -> Result { let size = case.input.size; let advertised = case.input.advertised; + // The peer implements §4.2 as it read before 2026-07-28, so that is what its + // answers are compared against. The current text's procedure is checked below, + // against the property the amendment added rather than against the peer. let mut checks = vec![Check::new( - "update_view(size, advertised) (§4.2)", + "update_view as the peer reads §4.2 (before 2026-07-28)", render_list(&case.expect.entries), - render_result(ibst::update_view(size, advertised), |entries| { - render_list(&entries) - }), + render_result( + ibst::update_view_ancestors_only(size, advertised), + |entries| render_list(&entries), + ), )]; + // What the amendment guarantees: the list ends at the new rightmost entry, so a + // user always learns the timestamp their clock bounds are checked against. There is + // no peer answer to compare this to — the peer predates the clause — so the check + // is against the draft's own guarantee, and it is the only check here that is not a + // cross-implementation comparison. + let current = ibst::update_view(size, advertised); + let up_to_date = advertised == Some(size); + checks.push(Check::new( + "update_view under the current §4.2 ends at the rightmost entry", + if up_to_date { + "nothing to send".to_owned() + } else { + format!("ends at entry {}", size.saturating_sub(1)) + }, + render_result(current, |entries| { + if up_to_date && entries.is_empty() { + "nothing to send".to_owned() + } else { + entries.last().map_or_else( + || "nothing at all".to_owned(), + |last| format!("ends at entry {last}"), + ) + } + }), + )); + if let Some(expected) = &case.expect.frontier { checks.push(Check::new( "frontier(size) (§4.1)", @@ -3051,7 +3116,7 @@ fn update_view_suite(dir: &Path) -> Result { "whether the rightmost entry is left unchecked (§4.2)", expected.to_string(), render_result( - ibst::leaves_right_edge_unchecked(size, advertised), + ibst::ancestors_only_leaves_right_edge_unchecked(size, advertised), |flag| flag.to_string(), ), )); @@ -3517,3 +3582,409 @@ fn log_math_suite(dir: &Path) -> Result { fn alloc_display(err: &impl core::fmt::Display) -> String { format!("{err}") } + +/// §9.1 and §13.5: a label owner verifying that new versions were inserted correctly. +/// +/// These vectors carry a `CombinedTreeProof` and no response envelope, and the reason is recorded +/// in the file itself: katie's `tree.Update` cannot answer any request, so no `UpdateResponse` was +/// ever measured. What is here is the part that matters for interoperability — §9.1's element +/// ordering, which no hand-built example can pin — and the claim is scoped to it. +fn owner_update_suite(dir: &Path) -> Result { + const FILE: &str = "update.json"; + let file: VectorFile = load(dir, FILE)?; + let suite = CipherSuite::Kt128Sha256Ed25519; + + let mut cases = Vec::new(); + for case in &file.cases { + let name = case.name.as_str(); + let bytes = unhex(FILE, name, "expect.proof", &case.expect.proof)?; + + let mut dec = Decoder::new(&bytes); + let parsed = CombinedTreeProof::decode(&mut dec).and_then(|proof| { + if dec.is_empty() { + Ok(proof) + } else { + Err(kt_wire::codec::Error::TrailingBytes { + remaining: dec.remaining(), + }) + } + }); + + let mut checks = vec![Check::new( + "CombinedTreeProof round-trips (§12.3)", + case.expect.proof.clone(), + match &parsed { + Err(err) => format!("decode failed: {err}"), + Ok(proof) => render_result(kt_wire::codec::encode(proof), hex::encode), + }, + )]; + checks.push(Check::new( + "timestamps, in the order §9.1 asks for them (§12.3.5)", + render_list( + &case + .expect + .timestamps + .iter() + .map(u64::to_string) + .collect::>(), + ), + match &parsed { + Err(_) => "decode failed".to_owned(), + Ok(proof) => render_list( + &proof + .timestamps + .iter() + .map(u64::to_string) + .collect::>(), + ), + }, + )); + checks.push(Check::new( + "prefix proofs: step 2.2's ladders, then step 3 or 4's lookups (§9.1)", + render_list( + &case + .expect + .prefix_proofs + .iter() + .map(|proof| proof.encoding.clone()) + .collect::>(), + ), + match &parsed { + Err(_) => "decode failed".to_owned(), + Ok(proof) => render_list( + &proof + .prefix_proofs + .iter() + .map(|element| render_result(kt_wire::codec::encode(element), hex::encode)) + .collect::>(), + ), + }, + )); + checks.push(Check::new( + "prefix roots — the entries with a timestamp but no proof (§12.3.2)", + render_list(&case.expect.prefix_roots), + match &parsed { + Err(_) => "decode failed".to_owned(), + Ok(proof) => render_list( + &proof + .prefix_roots + .iter() + .map(|root| hex::encode(root.as_bytes())) + .collect::>(), + ), + }, + )); + checks.push(Check::new( + "log tree inclusion elements (§12.3)", + render_list(&case.expect.inclusion), + match &parsed { + Err(_) => "decode failed".to_owned(), + Ok(proof) => render_list( + &proof + .inclusion + .elements + .iter() + .map(|element| hex::encode(element.as_bytes())) + .collect::>(), + ), + }, + )); + + // The replay. §12.3's exact-count rule is what turns this into a test of the *reading*: + // an implementation that orders §9.1's requests differently does not compute something + // subtly wrong, it finishes holding elements it never used. + let expected_branch = format!( + "every element read, none left over · entry {} {} · {}", + case.input.position, + if case.expect.distinguished { + "distinguished (step 3)" + } else { + "not distinguished (step 4)" + }, + case.expect.contact.as_ref().map_or_else( + || "no contact monitoring entry".to_owned(), + |entry| format!( + "contact monitoring entry {} → version {}", + entry.position, entry.version + ), + ), + ); + checks.push(Check::new( + "replaying §9.1 consumes the proof exactly (§12.3)", + expected_branch, + match &parsed { + Err(err) => format!("decode failed: {err}"), + Ok(proof) => { + replay_owner_update(case, proof, suite).unwrap_or_else(|detail| detail) + } + }, + )); + + cases.push(Case { + name: name.to_owned(), + negative: false, + input: format!( + "{}-entry log · {} new version{} in entry {} · {} timestamps, {} proofs, {} roots", + case.input.mutations.len(), + case.input.versions, + if case.input.versions == 1 { "" } else { "s" }, + case.input.position, + case.expect.timestamps.len(), + case.expect.prefix_proofs.len(), + case.expect.prefix_roots.len(), + ), + checks, + }); + } + + Ok(Suite { + primitive: file.primitive, + title: "Update proofs for a label owner".to_owned(), + draft_section: section_of(&file.draft), + file: FILE.to_owned(), + generator: Generator { + implementation: file.generator.implementation, + sha: file.generator.sha, + }, + cipher_suite: Some(format!("0x{:04x} {}", suite.code(), suite.name())), + cases, + }) +} + +/// Replays §9.1 over a recorded proof and reports what it established. +fn replay_owner_update( + case: &crate::vectors::Case, + proof: &CombinedTreeProof, + suite: CipherSuite, +) -> Result { + let mut keys = BTreeMap::new(); + for known in &case.input.ladder { + let vrf_output = hex::decode(&known.vrf_output) + .map_err(|err| format!("version {}: vrf_output: {err}", known.version)) + .and_then(|bytes| { + HashValue::from_slice(&bytes).map_err(|err| format!("vrf_output: {err}")) + })?; + let commitment = match &known.commitment { + None => None, + Some(value) => Some( + hex::decode(value) + .map_err(|err| format!("version {}: commitment: {err}", known.version)) + .and_then(|bytes| { + HashValue::from_slice(&bytes).map_err(|err| format!("commitment: {err}")) + })?, + ), + }; + keys.insert( + known.version, + combined::LadderKey { + vrf_output, + commitment, + }, + ); + } + + let size = case.input.tree_size; + let mut retained = combined::Retained::none(); + if let Some(advertised) = case.input.last { + for position in ibst::frontier(advertised).map_err(|err| format!("frontier: {err}"))? { + let timestamp = usize::try_from(position) + .ok() + .and_then(|index| case.input.entry_timestamps.get(index)) + .copied() + .ok_or_else(|| format!("no recorded timestamp for log entry {position}"))?; + retained.timestamps.insert(position, timestamp); + } + } + let mut reader = combined::Reader::new(proof, &retained); + + // §12.3.1's view update first, as for every other operation. + let view = match case.input.last { + None => ibst::frontier(size).map_err(|err| format!("frontier: {err}"))?, + // The peer's procedure, not the current text's: a proof's elements are ordered by + // the algorithm that *built* it (§12.3), and the peer runs §4.2 as it read before + // 2026-07-28. See `ibst::update_view_ancestors_only`. + Some(advertised) => ibst::update_view_ancestors_only(size, Some(advertised)) + .map_err(|err| format!("update view: {err}"))?, + }; + for position in &view { + reader + .timestamp(*position) + .map_err(|err| format!("view update at entry {position}: {err}"))?; + } + + let owner = combined::OwnerState { + starting: case.input.owner.starting, + version_at_starting: case.input.owner.version_at_starting, + upcoming: case.input.owner.upcoming.clone(), + }; + let updated = combined::owner_update( + suite, + size, + case.input.monitoring_window, + case.input.position, + case.input.versions, + &owner, + &keys, + &mut reader, + ) + .map_err(|err| format!("§9.1: {err}"))?; + + for position in reader.entries_owed_roots() { + reader + .prefix_root(position) + .map_err(|err| format!("prefix root for entry {position}: {err}"))?; + } + reader.finish().map_err(|err| format!("§12.3: {err}"))?; + + Ok(format!( + "every element read, none left over · entry {} {} · {}", + case.input.position, + if updated.distinguished { + "distinguished (step 3)" + } else { + "not distinguished (step 4)" + }, + updated.contact.map_or_else( + || "no contact monitoring entry".to_owned(), + |(position, version)| format!( + "contact monitoring entry {position} → version {version}" + ), + ), + )) +} + +/// §11.7 for `KT_128_SHA256_P256`, where the peer proves and this side verifies. +/// +/// The Ed25519 suite's checks include reproducing the peer's proof byte for byte, because that +/// module implements proving. This one cannot: RFC 9381 §5.4.2.1 derives P-256's nonce with +/// RFC 6979, which is a signing concern a verifier has no use for. So what is checked here is +/// what a client actually does — take the peer's 81-byte proof and recover the search key it +/// commits to — plus the two things RFC 9381 leaves to §11.7: that `alpha_string` is the encoded +/// `VrfInput`, and that a proof for one label-version pair does not verify for another. +/// +/// RFC 9381's own Appendix B.1 vectors are run in `kt-crypto`'s unit tests, and they are the +/// oracle for the ECVRF core; these pin the KT wrapping around it. +fn vrf_p256_suite(dir: &Path) -> Result { + const FILE: &str = "vrf-p256.json"; + let file: VectorFile = load(dir, FILE)?; + + let code = file.cipher_suite.unwrap_or_default(); + let suite = CipherSuite::from_code(code).map_err(|_| Error::CipherSuite { + file: FILE.to_owned(), + value: code, + })?; + + let mut cases = Vec::new(); + for case in &file.cases { + let name = case.name.as_str(); + let label = unhex(FILE, name, "label", &case.input.label)?; + let input = VrfInput::new(label, case.input.version); + let key_bytes = unhex(FILE, name, "public_key", &case.input.public_key)?; + let key = vrf::p256::PublicKey::from_slice(&key_bytes); + + let mut checks = Vec::new(); + if case.expect.error { + let raw = case + .input + .proof + .as_deref() + .ok_or_else(|| Error::MissingField { + file: FILE.to_owned(), + case: name.to_owned(), + field: "input.proof".to_owned(), + })?; + let bytes = unhex(FILE, name, "input.proof", raw)?; + let got = match (key, vrf::p256::Proof::from_slice(&bytes)) { + (Err(err), _) => format!("unusable public key: {err}"), + (_, Err(err)) => format!("unusable proof: {err}"), + (Ok(key), Ok(proof)) => match key.verify(&input, &proof) { + Err(_) => "rejected".to_owned(), + Ok(_) => "accepted".to_owned(), + }, + }; + checks.push(Check::new( + "verify() rejects a proof for another label-version pair (§11.7)", + "rejected", + got, + )); + } else { + let expected_input = + case.expect + .vrf_input + .as_deref() + .ok_or_else(|| Error::MissingField { + file: FILE.to_owned(), + case: name.to_owned(), + field: "expect.vrf_input".to_owned(), + })?; + let expected_output = + case.expect + .output + .as_deref() + .ok_or_else(|| Error::MissingField { + file: FILE.to_owned(), + case: name.to_owned(), + field: "expect.output".to_owned(), + })?; + let expected_proof = + case.expect + .proof + .as_deref() + .ok_or_else(|| Error::MissingField { + file: FILE.to_owned(), + case: name.to_owned(), + field: "expect.proof".to_owned(), + })?; + + checks.push(Check::new( + "VrfInput encoding is alpha_string (§2.1, §11.7)", + expected_input, + render_result(kt_wire::codec::encode(&input), hex::encode), + )); + // The length is a claim in its own right: an 81-byte proof read as 80 shifts every + // field of every `BinaryLadderStep` after it. + checks.push(Check::new( + "VRF.Np = 81 bytes (§17.1)", + (expected_proof.len() / 2).to_string(), + vrf::p256::PROOF_SIZE.to_string(), + )); + let bytes = unhex(FILE, name, "expect.proof", expected_proof)?; + let verified = match (key, vrf::p256::Proof::from_slice(&bytes)) { + (Err(err), _) => format!("unusable public key: {err}"), + (_, Err(err)) => format!("unusable proof: {err}"), + (Ok(key), Ok(proof)) => render_result(key.verify(&input, &proof), |output| { + hex::encode(output.as_bytes()) + }), + }; + checks.push(Check::new( + "verifying the peer's proof yields the peer's search key (§11.7)", + expected_output, + verified, + )); + } + + cases.push(Case { + name: name.to_owned(), + negative: case.expect.error, + input: format!( + "label {} bytes, version {}", + case.input.label.len() / 2, + case.input.version + ), + checks, + }); + } + + Ok(Suite { + primitive: file.primitive, + title: "VRF: ECVRF-P256-SHA256-TAI, verified against the peer".to_owned(), + draft_section: section_of(&file.draft), + file: FILE.to_owned(), + generator: Generator { + implementation: file.generator.implementation, + sha: file.generator.sha, + }, + cipher_suite: Some(format!("0x{:04x} {}", suite.code(), suite.name())), + cases, + }) +} diff --git a/interop/report/src/report.rs b/interop/report/src/report.rs index 994936e..0f20df8 100644 --- a/interop/report/src/report.rs +++ b/interop/report/src/report.rs @@ -462,14 +462,18 @@ pub fn coverage_table() -> Vec { // right after the suite code, and what follows it differs per mode. &["tree-head.json"], ), - // Not a gap: the Ed25519 suite is the target, and both Go peers support it. - Area { - section: "§11.7, §17.1".to_owned(), - name: "VRF: ECVRF-P256-SHA256-TAI (KT_128_SHA256_P256)".to_owned(), - module: None, - coverage: Coverage::OutOfScope, - evidence: Vec::new(), - }, + verified( + "§11.7, §17.1", + "VRF: ECVRF-P256-SHA256-TAI, verified — proving is a log's operation and is not", + "kt-crypto::vrf::p256", + &["vrf-p256.json"], + ), + verified( + "§11.3, §11.4, §17.1", + "Signatures: ECDSA/P-256 over SHA-256, r||s as 64 fixed-width bytes", + "kt-crypto::signature", + &["tree-head-p256.json"], + ), verified( "§3.4", "Combined tree: log entries committing to prefix tree roots", @@ -530,7 +534,27 @@ pub fn coverage_table() -> Vec { "kt-tree::combined", &["monitor.json"], ), - todo("§9, §13.5", "Updating a label"), + verified( + "§9.1", + "Updating a label: the two-tree check a label owner makes on new versions", + "kt-tree::combined", + &["update.json"], + ), + Area { + section: "§13.5".to_owned(), + name: "UpdateRequest, UpdateResponse, and §14's ManagerUpdateRequest".to_owned(), + module: Some("kt-wire::requests, kt-wire::responses".to_owned()), + // The requests are pinned as bytes by requests.json. The *response* is not, and + // cannot be: katie's tree.Update cannot serve one at all (KT-04), so no + // UpdateResponse has ever been measured. Its members are pinned individually — + // FullTreeHead by tree-head.json, UpdateInfo and BinaryLadderStep by requests.json, + // the CombinedTreeProof by update.json — but the envelope itself is checked only by + // this side's round-trips, and §14's ManagerUpdateRequest follows the peer's field + // order because §14's own listing is corrupt (DRAFT-11). Saying "verified" would + // overstate both. + coverage: Coverage::ImplementedUnverified, + evidence: Vec::new(), + }, todo( "§13.6", "Requesting distinguished heads, and fork detection (§10.2)", diff --git a/interop/report/src/vectors.rs b/interop/report/src/vectors.rs index 313b63a..f4004a9 100644 --- a/interop/report/src/vectors.rs +++ b/interop/report/src/vectors.rs @@ -501,6 +501,93 @@ pub struct MonitorExpect { pub binary_ladder: Option>, } +/// `update.json` input (§9.1, §13.5). +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct OwnerUpdateInput { + /// One log entry per element, each adding the label-value pairs listed. + pub mutations: Vec, + /// The label being updated, hex. + pub label: String, + /// The deployment mode. + pub mode: u8, + /// The log's signature key, hex. + pub signature_public_key: String, + /// The log's VRF key, hex. + pub vrf_public_key: String, + /// The Reasonable Monitoring Window, which decides which entries are distinguished and + /// therefore which branch §9.1 takes. + pub monitoring_window: u64, + /// The log's tree size when the proof was produced. + pub tree_size: u64, + /// The log entry the new versions were inserted into. + pub position: u64, + /// How many new versions were created there. + pub versions: usize, + /// The owner's state before the update. + pub owner: OwnerStateInput, + /// The tree size the owner advertised, if any. + #[serde(default)] + pub last: Option, + /// Every log entry's timestamp, by position. + pub entry_timestamps: Vec, + /// Every search key the proof's lookups need: the union of what the owner already held and + /// what §13.5's `binary_ladder` would add. + /// + /// §9.1 sends VRF proofs only for the versions the owner cannot already know, so a verifier + /// replaying one of these proofs needs both halves. Splitting them would be more faithful to + /// the wire and would test nothing extra, since which half a key came from does not change + /// any lookup. + pub ladder: Vec, + /// What the case is for, recorded by the generator. + pub note: String, +} + +/// A label owner's state, as §9.1 reads it. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct OwnerStateInput { + /// The reference point the owner verified with §8.3's first algorithm. + pub starting: u64, + /// The greatest version that existed there, absent if the label did not exist. + #[serde(default)] + pub version_at_starting: Option, + /// The entry each version created since then was inserted into, ascending, one per version. + pub upcoming: Vec, +} + +/// `update.json` expectations. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct OwnerUpdateExpect { + /// The whole encoded `CombinedTreeProof`, hex. + pub proof: String, + /// Its timestamps, in the algorithm's request order. + pub timestamps: Vec, + /// Its prefix proofs, in request order. + pub prefix_proofs: Vec, + /// Its prefix roots, hex. + pub prefix_roots: Vec, + /// Its log tree inclusion elements, hex. + pub inclusion: Vec, + /// Whether the entry holding the new versions is distinguished, which decides whether §9.1 + /// takes its step 3 or step 4 branch. + pub distinguished: bool, + /// The contact monitoring entry step 4 adds, absent when step 3 applied. + #[serde(default)] + pub contact: Option, +} + +/// The entry §9.1 step 4 adds to the owner's contact monitoring map. +#[derive(Debug, Deserialize)] +#[serde(deny_unknown_fields)] +pub struct ContactEntryExpect { + /// The log entry holding the new versions. + pub position: u64, + /// The new greatest version. + pub version: u32, +} + /// `distinguished.json` input (§6.1). #[derive(Debug, Deserialize)] #[serde(deny_unknown_fields)] diff --git a/interop/vectors/from-kt.json b/interop/vectors/from-kt.json index e10e9f2..40cfdbb 100644 --- a/interop/vectors/from-kt.json +++ b/interop/vectors/from-kt.json @@ -4615,6 +4615,480 @@ } ], "proof": "040000018bcfe568030000018bcfe568050000018bcfe568060000018bcfe5680101030100020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a500020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a500000003c515de8ebec5b3ed339986572c7dea20011eb8477cbb1d00d4c9e9dc512e894deb2ff8cc28cfb2e11acd02957aa93bc4546f9c02b6ba0ac3bb0b5327b7fb1731e831781713a039d65b77ea87de990a12558dac3541df03fd65be15ae4d1804c0000388c80386321d2d246f6ca37ea4ee0b01f3b082501c468a9f24c6fbaed3256d4723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cdfe79a7e1cd86892050957209fc5ca2abb69e568fad4be2520b6e5a79b68e3b0c" + }, + { + "kind": "owner-update", + "name": "owner-update-previous-frontier-skipped", + "expect": "accept", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [ + 4, + 5 + ] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005", + "commitment": "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006", + "commitment": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + }, + { + "version": 9, + "vrf_output": "4d00000000000000000000000000000000000000000000000000000000000009" + }, + { + "version": 10, + "vrf_output": "720000000000000000000000000000000000000000000000000000000000000a" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060102020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a503010200033e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf2e10a7f76117890ab9c36587502bc234ac98347b889c59a54b770e8fac41ec4c0209deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc3eb2ff8cc28cfb2e11acd02957aa93bc4546f9c02b6ba0ac3bb0b5327b7fb173100037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cdfe79a7e1cd86892050957209fc5ca2abb69e568fad4be2520b6e5a79b68e3b0c" + }, + { + "kind": "owner-update", + "name": "owner-update-previous-frontier-inspected", + "expect": "accept", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060203020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5030301030100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf03020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a503029400000000000000000000000000000000000000000000000000000000000004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a101010100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf0109deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc300037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cd223da5aae9ed70218c5f6a5c435df2b529877536252de7e05b279d9fc42def98" + }, + { + "kind": "owner-update", + "name": "owner-update-extra-timestamp", + "expect": "reject", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + } + ], + "proof": "040000018bcfe568030000018bcfe568050000018bcfe568060000018bcfe568630203020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5030301030100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf03020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a503029400000000000000000000000000000000000000000000000000000000000004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a101010100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf0109deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc300037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cd223da5aae9ed70218c5f6a5c435df2b529877536252de7e05b279d9fc42def98" + }, + { + "kind": "owner-update", + "name": "owner-update-missing-prefix-proof", + "expect": "reject", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060103020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5030301030100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf0109deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc300037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cd223da5aae9ed70218c5f6a5c435df2b529877536252de7e05b279d9fc42def98" + }, + { + "kind": "owner-update", + "name": "owner-update-proofs-out-of-order", + "expect": "reject", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060203020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a503029400000000000000000000000000000000000000000000000000000000000004a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a101010100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf03020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5030301030100023e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf0109deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc300037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cd223da5aae9ed70218c5f6a5c435df2b529877536252de7e05b279d9fc42def98" + }, + { + "kind": "owner-update", + "name": "owner-update-position-not-advancing", + "expect": "reject", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 3, + "upcoming": [ + 4, + 5, + 6 + ] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002", + "commitment": "a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7a7" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003", + "commitment": "a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6a6" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004", + "commitment": "a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1a1" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005", + "commitment": "a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0" + }, + { + "version": 6, + "vrf_output": "de00000000000000000000000000000000000000000000000000000000000006", + "commitment": "a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3a3" + }, + { + "version": 7, + "vrf_output": "0300000000000000000000000000000000000000000000000000000000000007" + }, + { + "version": 8, + "vrf_output": "2800000000000000000000000000000000000000000000000000000000000008" + }, + { + "version": 9, + "vrf_output": "4d00000000000000000000000000000000000000000000000000000000000009" + }, + { + "version": 10, + "vrf_output": "720000000000000000000000000000000000000000000000000000000000000a" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060102020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a503010200033e4f9e27b59215bd00ce84ed70a46cf6b417d7b2d0cab3f76eba1d0ebfe12cc100af668b3a2983fb5077d0af145f31e2acbe21d282dcb9ea2ab71f51aa2b5abf2e10a7f76117890ab9c36587502bc234ac98347b889c59a54b770e8fac41ec4c0209deeacffe7897c0989a9d408a3741ae332c219de3f248c34b964303825eccc3eb2ff8cc28cfb2e11acd02957aa93bc4546f9c02b6ba0ac3bb0b5327b7fb173100037da5f7d93287729c851f7fb055b57892bb730ed0b2b3e9f7b1a6c0b4f080884723c605c7f7340812b8d103e8f8324631fee97bcdb8936ad073d79f83a088e9cdfe79a7e1cd86892050957209fc5ca2abb69e568fad4be2520b6e5a79b68e3b0c" + }, + { + "kind": "owner-update", + "name": "owner-update-shortest-possible-ladders", + "expect": "accept", + "size": 7, + "position": 6, + "new_versions": 1, + "owner": { + "starting": 3, + "version_at_starting": 0, + "upcoming": [] + }, + "timestamps": [ + 1700000000000, + 1700000000001, + 1700000000002, + 1700000000003, + 1700000000004, + 1700000000005, + 1700000000006 + ], + "keys": [ + { + "version": 0, + "vrf_output": "0000000000000000000000000000000000000000000000000000000000000000", + "commitment": "a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5" + }, + { + "version": 1, + "vrf_output": "2500000000000000000000000000000000000000000000000000000000000001", + "commitment": "a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4a4" + }, + { + "version": 2, + "vrf_output": "4a00000000000000000000000000000000000000000000000000000000000002" + }, + { + "version": 3, + "vrf_output": "6f00000000000000000000000000000000000000000000000000000000000003" + }, + { + "version": 4, + "vrf_output": "9400000000000000000000000000000000000000000000000000000000000004" + }, + { + "version": 5, + "vrf_output": "b900000000000000000000000000000000000000000000000000000000000005" + } + ], + "proof": "030000018bcfe568030000018bcfe568050000018bcfe568060201020000000000000000000000000000000000000000000000000000000000000000a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a500000003010303020302000214e1606bcb679d0c9b5786822a582821e3dfa8e0fb291c73f7aa381a0b0c478300000000000000000000000000000000000000000000000000000000000000000114e1606bcb679d0c9b5786822a582821e3dfa8e0fb291c73f7aa381a0b0c47830003109e4263bae6f7c96d79c2b5baf81bdab6dc61b560ea28e5cbb7bac840a92a155e23476d51114523786a0a140ab2addf3c73ea93d7f39f9887d89666bbf6c598df2dff30bbbae0f676deaf3cea20d7824bb15f811089420854afce057e7ee432" } ] } diff --git a/interop/vectors/monitor.json b/interop/vectors/monitor.json index e7d880b..ecd1a43 100644 --- a/interop/vectors/monitor.json +++ b/interop/vectors/monitor.json @@ -18,42 +18,42 @@ } ], "entry_timestamps": [ - 1785340634579, - 1785340634580, - 1785340634580, - 1785340634581, - 1785340634581, - 1785340634582, - 1785340634582 + 1785421501777, + 1785421501777, + 1785421501778, + 1785421501778, + 1785421501778, + 1785421501779, + 1785421501779 ], "known_versions": [ { - "commitment": "449255b8c67d5df30eb78b7be416e8f01bb2e1c525de2f8fb89edac345b1a4f9", + "commitment": "a7d8b3121a61bcbeefe50c5a596b37acf36d9723fba58ae04018336f3de84ad3", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "8289fd49b7903c8019cb4cb6a789e0beb6d16513cc826c4b2ff95653c53ae0a7", + "commitment": "300b1095b6a7bb9b5827371043f679c5bb6fd5e5f28b322a5ea294c59dbdb1f3", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "87513a27fc159ca2fb5c6aafeec6249ecf62939ee40c5d6451848e60c1033d0d", + "commitment": "8d1bfd6bda81f46c5a9606e1202a0668f1669f15042c00fbf04d52f650fbd6c6", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "db30521c01a77530ea45a8af2fee6aa6293b98739dfe901d3dec9603cc5ec5d8", + "commitment": "7ced5a20473f84c859c5eb9e21cd04f87d694d4dd1e8ad93c84f5725d61d0197", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "060ef4f0009146c2c369aa728fa21887d6923095545b8bf1e89ebcc1a3b7d78c", + "commitment": "99fdb6803c6f39cbf6d90a1290a87e68843f8666531d4c97a1faee9225bcaa68", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "6ca5ae2845a5c8d9b9dd2a414acb10354b377a7c76ac1d0465080c1594be6320", + "commitment": "fb8162bd0359aa21cae9635f4d8db9fc4e1a551af92b1bd431fe77b3e1d8920f", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, @@ -134,23 +134,23 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "0200000000000000070040c9c3deb97798c1a89d1060f7dc7b9d11241232e5942508b43ecf60e4af350f88774287803e213461ac94cf815d64ea41d50d31e1738e5e22fe20cfb246f36301", + "full_tree_head": "02000000000000000700408ba59d3414e4861921ab74ef384b10cc5a2ad31fc8f6310b8f153b44fd7c0e8eb4def22c52ce1b5a028101d6dd8b4e03aa25bbd57950962b800a7e2621ce680a", "inclusion": [ - "76dcca241fe88a97fbcec83b9c5fd52139055eb075cb178489f3647f87cc4595", - "ae9954a130f8372d12011840e85717ecf720700f1b553e5aa0ac3708f80397bd", - "4824510a0c3a3006da71f5780156423f9b4fe766350b3ea9d25e40de622db157" + "673e7ad229d6640995fb366cd2949d08840f7e54fc0a4e49745c13070de45d14", + "f6aaf2030b85c499bf633b7ebd50d691285666155eefab6520ce5e3c5715b637", + "be09b1a39ce289bf57f10397a3123d68ee10533becb7cc21929d2c5918106e4b" ], "prefix_proofs": [], "prefix_roots": [ - "f384a311d9e1b0328b95282f865b4e51ecc512caddf21eda19e03fe5d2b7a3f7", - "90927c3422a97428ccf640eabcb5b8adf5835342e9d33285545d3cebc1be7728", - "23161f76dfd4c49312985d710c21d3dd8cfa42281196c607fa060cb65bf74489" + "0d8b0d4dd023a9bd9eb387dd874bb35c5fae3ec6972eaf64ea2098b3d0c3667c", + "e18f0e6e660752bd375336536906b37bd9a3b53fb96d7644da24dc4841236a31", + "10487a3d72f58a409ee4093ccca920991a69b222b1a09e77498dc92a996ef89e" ], - "response": "0200000000000000070040c9c3deb97798c1a89d1060f7dc7b9d11241232e5942508b43ecf60e4af350f88774287803e213461ac94cf815d64ea41d50d31e1738e5e22fe20cfb246f36301030000019fae9825d50000019fae9825d60000019fae9825d60003f384a311d9e1b0328b95282f865b4e51ecc512caddf21eda19e03fe5d2b7a3f790927c3422a97428ccf640eabcb5b8adf5835342e9d33285545d3cebc1be772823161f76dfd4c49312985d710c21d3dd8cfa42281196c607fa060cb65bf74489000376dcca241fe88a97fbcec83b9c5fd52139055eb075cb178489f3647f87cc4595ae9954a130f8372d12011840e85717ecf720700f1b553e5aa0ac3708f80397bd4824510a0c3a3006da71f5780156423f9b4fe766350b3ea9d25e40de622db157", + "response": "02000000000000000700408ba59d3414e4861921ab74ef384b10cc5a2ad31fc8f6310b8f153b44fd7c0e8eb4def22c52ce1b5a028101d6dd8b4e03aa25bbd57950962b800a7e2621ce680a030000019fb36a15520000019fb36a15530000019fb36a155300030d8b0d4dd023a9bd9eb387dd874bb35c5fae3ec6972eaf64ea2098b3d0c3667ce18f0e6e660752bd375336536906b37bd9a3b53fb96d7644da24dc4841236a3110487a3d72f58a409ee4093ccca920991a69b222b1a09e77498dc92a996ef89e0003673e7ad229d6640995fb366cd2949d08840f7e54fc0a4e49745c13070de45d14f6aaf2030b85c499bf633b7ebd50d691285666155eefab6520ce5e3c5715b637be09b1a39ce289bf57f10397a3123d68ee10533becb7cc21929d2c5918106e4b", "timestamps": [ - 1785340634581, - 1785340634582, - 1785340634582 + 1785421501778, + 1785421501779, + 1785421501779 ], "tree_size": 7 } @@ -165,27 +165,27 @@ } ], "entry_timestamps": [ - 1785340634595, - 1785340634595, - 1785340634595, - 1785340634596, - 1785340634596, - 1785340634596, - 1785340634597 + 1785421501792, + 1785421501792, + 1785421501792, + 1785421501793, + 1785421501793, + 1785421501793, + 1785421501794 ], "known_versions": [ { - "commitment": "51eccdd5bab285820be4d953c590cbcb1cfe7226c7df2e49ae5ee0dcd8513016", + "commitment": "1522a8277f9fd59d7555fc183cae8a85d8d44310447a68cbbb620a660d959b10", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "700bb7ab4b02cf67af5776d39827daedbbeed19a1687bc0a216cb5dfa5901f78", + "commitment": "a1b95e7b8eaba58c48acbd3964845602495b3517df748fa8656777fabf423eff", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "3133479aa12fbf7261c6cd9dd81535e11a2105a7d013b4036df93dc58235023e", + "commitment": "c4388c6173340837b3a2358a18160655a26c0bd853db6e3bcb152e4a19c20dc0", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, @@ -262,19 +262,19 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "02000000000000000700403d1d331a10a8782003013c0270e5ddd1e90a85427854f12d2892a775c2bb96e695ee7b62b8552cf6d263077dcef4f54f97e9b8e74a920a7e0d09bfffc380fa02", + "full_tree_head": "0200000000000000070040f474c0d7439e81692050c58092429b5ee6dc776ee56d6349922d3bd2194b12be32b3447f1372e24a508370af04a6a8588151eea9934e73da3d118fd33b73f50d", "inclusion": [ - "866ae7bc5d76b8192f5e0a9913690341db60d1b05337aac25a60952944968c75", - "656418f1bf5bbeaf8e3c916e412d6829a6dd3f6e9fe3ba17b719ee9f04978863", - "1af18d9fad5faec8b81a7078985e3d34d77c6fb460468811ccc0fdb0c551f7a1" + "31b1bf72b2ec2198beffd3cbf8f35f973c9e4fee08c58419fbae7c54435ed781", + "5d4eda491c05a26d8ed0470b58082e29500473a5d24bc128e7132ab42b6b868c", + "d6594cda1a28a0e04274360ba86c32edf82647fa9ccd158c49b4cb76fd2748f7" ], "prefix_proofs": [ { "elements": [ "0000000000000000000000000000000000000000000000000000000000000000", - "787f5df75796fd62321e66ace208977cf69546dcd6104dbd05bc10a00a3d5114" + "8f14dbfbccf875f6f9455f771a05b0b8ac2d743bd249074055a985a217888952" ], - "encoding": "0301040102010100020000000000000000000000000000000000000000000000000000000000000000787f5df75796fd62321e66ace208977cf69546dcd6104dbd05bc10a00a3d5114", + "encoding": "03010401020101000200000000000000000000000000000000000000000000000000000000000000008f14dbfbccf875f6f9455f771a05b0b8ac2d743bd249074055a985a217888952", "results": [ { "depth": 4, @@ -292,16 +292,16 @@ } ], "prefix_roots": [ - "b3fcc3d9d732501853b3831a40a3e30b5dd26bc06b7c6ef5693e50fc663efaee", - "ff094e927d4b849ecd158be4d10cba21bfa639dbf2c04674bfe7c444871a4dec", - "f71f1e353f44856f4b55d761e91b4c726095a28209e31b58a56683a269bd3dbd" + "8729aed5d0fd575532d30dcbf37920f9002e26f50a599a7d96dfa65406f87580", + "e773dbbc07cd73cc21acdb14b99181e36d0754514726bcf4e14f0a8876a55e48", + "70b1b00822efb92b0536e2adb2d8e2652b09d29ef43cb5cd649ee3869562da0d" ], - "response": "02000000000000000700403d1d331a10a8782003013c0270e5ddd1e90a85427854f12d2892a775c2bb96e695ee7b62b8552cf6d263077dcef4f54f97e9b8e74a920a7e0d09bfffc380fa02040000019fae9825e40000019fae9825e40000019fae9825e50000019fae9825e3010301040102010100020000000000000000000000000000000000000000000000000000000000000000787f5df75796fd62321e66ace208977cf69546dcd6104dbd05bc10a00a3d511403b3fcc3d9d732501853b3831a40a3e30b5dd26bc06b7c6ef5693e50fc663efaeeff094e927d4b849ecd158be4d10cba21bfa639dbf2c04674bfe7c444871a4decf71f1e353f44856f4b55d761e91b4c726095a28209e31b58a56683a269bd3dbd0003866ae7bc5d76b8192f5e0a9913690341db60d1b05337aac25a60952944968c75656418f1bf5bbeaf8e3c916e412d6829a6dd3f6e9fe3ba17b719ee9f049788631af18d9fad5faec8b81a7078985e3d34d77c6fb460468811ccc0fdb0c551f7a1", + "response": "0200000000000000070040f474c0d7439e81692050c58092429b5ee6dc776ee56d6349922d3bd2194b12be32b3447f1372e24a508370af04a6a8588151eea9934e73da3d118fd33b73f50d040000019fb36a15610000019fb36a15610000019fb36a15620000019fb36a15600103010401020101000200000000000000000000000000000000000000000000000000000000000000008f14dbfbccf875f6f9455f771a05b0b8ac2d743bd249074055a985a217888952038729aed5d0fd575532d30dcbf37920f9002e26f50a599a7d96dfa65406f87580e773dbbc07cd73cc21acdb14b99181e36d0754514726bcf4e14f0a8876a55e4870b1b00822efb92b0536e2adb2d8e2652b09d29ef43cb5cd649ee3869562da0d000331b1bf72b2ec2198beffd3cbf8f35f973c9e4fee08c58419fbae7c54435ed7815d4eda491c05a26d8ed0470b58082e29500473a5d24bc128e7132ab42b6b868cd6594cda1a28a0e04274360ba86c32edf82647fa9ccd158c49b4cb76fd2748f7", "timestamps": [ - 1785340634596, - 1785340634596, - 1785340634597, - 1785340634595 + 1785421501793, + 1785421501793, + 1785421501794, + 1785421501792 ], "tree_size": 7 } @@ -316,42 +316,42 @@ } ], "entry_timestamps": [ - 1785340634602, - 1785340634703, - 1785340634804, - 1785340634906, - 1785340635007, - 1785340635109, - 1785340635211 + 1785421501799, + 1785421501900, + 1785421502001, + 1785421502103, + 1785421502205, + 1785421502306, + 1785421502407 ], "known_versions": [ { - "commitment": "ed0442e8f52dd96d69f729e65db3f7df799a9976481f558121ac842c682cbcc4", + "commitment": "809f8870e3000e8cc2ae9d3656c78ab1563b327eb561f3101fdb3aab73999529", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "ad85c73fa0980b476fb30806e8032faaed64706769b96a5068789ad300a333de", + "commitment": "cf3f298c50dbecca1c1e70300be019e35703028b771e56a873955e1597ae05a3", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "80da5631a4687fdfe8c66840de9d527cdbcf70edd9de0c2d99e19524ab34c4d4", + "commitment": "23b08c8f6cc3b0a39ec664a54f342590c40114916cddbb62d1a6c5f6e26a4250", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "1995f4ade0c13d5097fb7acece47c24ce259df4c0e33d6eca2b84fd013bc25e1", + "commitment": "e39fd83b7c55d1b5dc31a005dabdc6072ab1fbd7d9f13fd1a2fea06f8141ce2f", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "d8322879d64543c73e0702a7de4940f1cf5b1fc80ba5972dba0e6537b9156898", + "commitment": "6fe141e89b0655db6031303309d68c4cefc83ef1edf3241022523bb7afbe887a", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "d229f4f9c18a75af0a5da805e383e633d19356a2ada51d225b7d1cff450de279", + "commitment": "5cd9f9aae59b7aa85476be6debb42348d4046d2f69f226a5e7df7b8400aec3e6", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, @@ -432,23 +432,23 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "020000000000000007004011c9b9b9054570a06107e6412a39eb15c4c54f487587ff9c833fd46fd0de50bdbcd26b8af329428e36ef53a79f8645c93df88107fa04ef231a79f94bebe5c000", + "full_tree_head": "020000000000000007004082bf545cbe276ca9f6a7f8e66bea09df9715f99df569f26e6ebfe3b15f33401547bea85e113d38a428a7a7c544d120fb4508d0e6450202a313d5194452567504", "inclusion": [ - "67f15be88056757515ba06cdcf67bc835fdf758d06932539b04d0e4d4b4576da", - "62879dd252e1e60775e6d57c80e04ad07c5201a62cd609a9d5cbc48f6198e2db", - "78f223cbd4da7b8d72ddd3ec3e1a1e2cbc4f4fe6c75b36127bce9c7ffb5dd7d6" + "cd6260ae103a075310569f79e48f31e2d9de01befb88d3ed823037b97fc4bce7", + "ca44040c9e54207c54cb6a08338777d27b4769977d0d9b955b6d8914865779a2", + "daddfc645ad37de87121cf480f2b5dc8fcccbf270d36e29868d3d1b44cf0e544" ], "prefix_proofs": [], "prefix_roots": [ - "9f4a50ba010f545918f5592c790786767f78219ff430a867eb538cc8444e72b3", - "0791e4af854e3e8efc43ebed690eb0a7bb9bf9d469fd808126962c0910346d7a", - "1ed3c37e3ac2ed9038800fde8b7441d9f82864c438408bf91823f8a50c281d6c" + "6fde627c31d8f7fafc86143f1327c36fbdeacbac64d1e7016a44f03674587fae", + "3fca4af571855dd0a51f47b8f7138b47a3d36c0fadfdb7aa773cfeffd6d66786", + "b85c25a3ea395ce828b271ed7a533208aa43407a2c695d56ae0921999e57e673" ], - "response": "020000000000000007004011c9b9b9054570a06107e6412a39eb15c4c54f487587ff9c833fd46fd0de50bdbcd26b8af329428e36ef53a79f8645c93df88107fa04ef231a79f94bebe5c000030000019fae98271a0000019fae9827e50000019fae98284b00039f4a50ba010f545918f5592c790786767f78219ff430a867eb538cc8444e72b30791e4af854e3e8efc43ebed690eb0a7bb9bf9d469fd808126962c0910346d7a1ed3c37e3ac2ed9038800fde8b7441d9f82864c438408bf91823f8a50c281d6c000367f15be88056757515ba06cdcf67bc835fdf758d06932539b04d0e4d4b4576da62879dd252e1e60775e6d57c80e04ad07c5201a62cd609a9d5cbc48f6198e2db78f223cbd4da7b8d72ddd3ec3e1a1e2cbc4f4fe6c75b36127bce9c7ffb5dd7d6", + "response": "020000000000000007004082bf545cbe276ca9f6a7f8e66bea09df9715f99df569f26e6ebfe3b15f33401547bea85e113d38a428a7a7c544d120fb4508d0e6450202a313d5194452567504030000019fb36a16970000019fb36a17620000019fb36a17c700036fde627c31d8f7fafc86143f1327c36fbdeacbac64d1e7016a44f03674587fae3fca4af571855dd0a51f47b8f7138b47a3d36c0fadfdb7aa773cfeffd6d66786b85c25a3ea395ce828b271ed7a533208aa43407a2c695d56ae0921999e57e6730003cd6260ae103a075310569f79e48f31e2d9de01befb88d3ed823037b97fc4bce7ca44040c9e54207c54cb6a08338777d27b4769977d0d9b955b6d8914865779a2daddfc645ad37de87121cf480f2b5dc8fcccbf270d36e29868d3d1b44cf0e544", "timestamps": [ - 1785340634906, - 1785340635109, - 1785340635211 + 1785421502103, + 1785421502306, + 1785421502407 ], "tree_size": 7 } @@ -467,47 +467,47 @@ } ], "entry_timestamps": [ - 1785340635223, - 1785340635223, - 1785340635224, - 1785340635224, - 1785340635224, - 1785340635225, - 1785340635225 + 1785421502418, + 1785421502418, + 1785421502418, + 1785421502419, + 1785421502419, + 1785421502419, + 1785421502420 ], "known_versions": [ { - "commitment": "17ce261da80f2688c8250c61e7e8cff02bba8c2523406bea874960fef67cdece", + "commitment": "9c07ec67c610086cd81b233a810305fffc5f02e0d1aea91197073eab1563d808", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "13cfb42b42aac19da0672813bae8b18c4a8a26ec353979033cf7d0b3ee24c1ae", + "commitment": "793d38ee727d008f67a517564164f386827d52a7ac713398fcb184e20ddd8247", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "588ea86685c178e9e63fac06c7dd34d0fa9f3beea7af5567bb87cfcfeb015c93", + "commitment": "f342b90ca2d878bd97d1cfcf8d55245de06c4efc24a6a385db8f0e273e1ece46", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "13781622012e69cd6256f7ecec7db7e38dadf41661486ee72ae638ffaf8dd35f", + "commitment": "870d155e560f00074013eab20cb8ed73fac81a822e8ec7fc8f109bdeebdc8460", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "33007ce5862ff74b8f30de8566230c81ad4c7aa1d41054eaaac36b00de57e246", + "commitment": "d18458e4ae3112e5206923db0ecfe39d6b9bcdd7b888121838d3d0cf20a64bce", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "65a2594c7136080303db169dcc1aaee52d33c2fc8d6178669b5bfc2b78e3a0d5", + "commitment": "79de4b0dc8b97d52cf6e7847ef095658827365ab10c2eef931477679d97a515c", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, { - "commitment": "0d9d15e964d85a70b062c9c240212e373f612a2de42eb725ae9046f1908175d4", + "commitment": "13c8f23dcf8153af216e23f2a8cb72568519172590ebbc84b552d1ec2c15de24", "version": 6, "vrf_output": "599424025a070d0e48913585265a30210ffe3416005c77c2354094f14c6358a1" }, @@ -584,21 +584,21 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "020000000000000007004075c18da8ef52537be1f92a0fb97ef6c7f010f67a5b3a6b6f66ae131cfe3d4cf90a361dc9ae14990835da215cf7d8d8a6bc444197acaf973aca25bdd33bf53406", + "full_tree_head": "0200000000000000070040d3dfaabc32196e0e31638ce181c16711ff340a8cfc5fb565fe12736a1a81816b73d947b827bae9da0b94897de82e26a8903420a5ec8a0c65c67c490a1669b001", "inclusion": [ - "0d237d66512300b573cafd684939907fb1dadcec6ca5494ddf01464662a81b54", - "dc49c2616ece7d47fec107fbe6c4143618569f362bb9c9a37200384d4ef76395", - "52c1848f3355ceb1d4788812166ae36a99c328a3efe386e3c05b1270da2d7172" + "e4c267b6afa0b4c6ae2324ce6d81ec2ce9875ed57a1b6b66e0375f319ac46420", + "893ac7f808d47a8e70386ce2d4f57601e29532ceb02ece352e334be3eeb47c81", + "d9f2ae172367420c95db9e2ad2b38025c1bf07ace4569b37096ef4578cd82b16" ], "prefix_proofs": [ { "elements": [ - "afc9400f5d50eaaa4088cc50854abf714d1d9d8ceb729a670aded19d32a8a354", - "2fa04acae4e06ea20ab760e567d3b16f830fac74b696b2b2cd3400839a6b2ae2", + "b57e36c4a3a1cfc7637dcde5080e3ebc650f2508bd2f185a73a8b553a183a10c", + "87f4099fbe78ed6bc518bbe0df53abf8ada8fc0b3f6aee7bf2829bf573c4e062", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0401060103010401060004afc9400f5d50eaaa4088cc50854abf714d1d9d8ceb729a670aded19d32a8a3542fa04acae4e06ea20ab760e567d3b16f830fac74b696b2b2cd3400839a6b2ae200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0401060103010401060004b57e36c4a3a1cfc7637dcde5080e3ebc650f2508bd2f185a73a8b553a183a10c87f4099fbe78ed6bc518bbe0df53abf8ada8fc0b3f6aee7bf2829bf573c4e06200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 6, @@ -620,14 +620,14 @@ } ], "prefix_roots": [ - "58613030462c55c932927680a09cc51ab0e24b41c9db2bbcf07992c5460a5bfd", - "22ec41fc651ad10aaad5f8c636cc397f3e9d1ee35e9c642856dcad6d71d98af7" + "976f96bbbbb01a2d0496e42ef2ff21432f3f5264e9cc47efee03d5b67a40df73", + "2eb9644a7b168ec761d420d77f2802983a5bbb3a54298c3cf54c887085f649fb" ], - "response": "020000000000000007004075c18da8ef52537be1f92a0fb97ef6c7f010f67a5b3a6b6f66ae131cfe3d4cf90a361dc9ae14990835da215cf7d8d8a6bc444197acaf973aca25bdd33bf53406030000019fae9828580000019fae9828590000019fae982859010401060103010401060004afc9400f5d50eaaa4088cc50854abf714d1d9d8ceb729a670aded19d32a8a3542fa04acae4e06ea20ab760e567d3b16f830fac74b696b2b2cd3400839a6b2ae2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000258613030462c55c932927680a09cc51ab0e24b41c9db2bbcf07992c5460a5bfd22ec41fc651ad10aaad5f8c636cc397f3e9d1ee35e9c642856dcad6d71d98af700030d237d66512300b573cafd684939907fb1dadcec6ca5494ddf01464662a81b54dc49c2616ece7d47fec107fbe6c4143618569f362bb9c9a37200384d4ef7639552c1848f3355ceb1d4788812166ae36a99c328a3efe386e3c05b1270da2d7172", + "response": "0200000000000000070040d3dfaabc32196e0e31638ce181c16711ff340a8cfc5fb565fe12736a1a81816b73d947b827bae9da0b94897de82e26a8903420a5ec8a0c65c67c490a1669b001030000019fb36a17d30000019fb36a17d30000019fb36a17d4010401060103010401060004b57e36c4a3a1cfc7637dcde5080e3ebc650f2508bd2f185a73a8b553a183a10c87f4099fbe78ed6bc518bbe0df53abf8ada8fc0b3f6aee7bf2829bf573c4e0620000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002976f96bbbbb01a2d0496e42ef2ff21432f3f5264e9cc47efee03d5b67a40df732eb9644a7b168ec761d420d77f2802983a5bbb3a54298c3cf54c887085f649fb0003e4c267b6afa0b4c6ae2324ce6d81ec2ce9875ed57a1b6b66e0375f319ac46420893ac7f808d47a8e70386ce2d4f57601e29532ceb02ece352e334be3eeb47c81d9f2ae172367420c95db9e2ad2b38025c1bf07ace4569b37096ef4578cd82b16", "timestamps": [ - 1785340635224, - 1785340635225, - 1785340635225 + 1785421502419, + 1785421502419, + 1785421502420 ], "tree_size": 7 } @@ -642,42 +642,42 @@ } ], "entry_timestamps": [ - 1785340635237, - 1785340635237, - 1785340635237, - 1785340635237, - 1785340635238, - 1785340635238, - 1785340635238 + 1785421502430, + 1785421502431, + 1785421502431, + 1785421502431, + 1785421502431, + 1785421502432, + 1785421502432 ], "known_versions": [ { - "commitment": "08ceee05f2ec9aa69e2b3e8616815b80e7e91ef7c4498e537f97024b33bbc319", + "commitment": "6d538e9700b5a6ff82361916d063e85fe640da568deae5b7aabb25f509ead715", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "02b1919e10f47599ef75ada1c0f5b37177a2e8870b082da17f56d4aac93994d4", + "commitment": "94a33f4a65c1e8506b50f89d889b0a6420554c9cc8e14a743a4e278990c8ba58", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "1fb80b73ba19d349968c7c9a5ac2a9cc45098393972bb57bca056c8c62dc863a", + "commitment": "0946ce3a09850d91f80806ac833e7f2aa80267f9ead9448635d9d14c15331ff5", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "fe8609349001d6517832f5bf5cb77183d76d57e31a27fe4994ddf23e8ad03204", + "commitment": "57d2395681e3b087e524e4def6796fdbb39adb07a0ddc77e9ff45bbeeb5935da", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "a4af9dd5c064176f42133b2ba300d1ff0fe29e33b9a26095f26cb63f84a82a58", + "commitment": "f3b0d00520d36d8fddb4332eff90cc988e0208291af1e368dbb5cc39865f4766", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "af1a20ae4b12f2f80c30167420be6a2faf38578413cce595be96ebe441574b94", + "commitment": "05a8112d0c1afc1f9ec499a931104672d4b821c8d86c8bad274794685b3709f6", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, @@ -759,15 +759,15 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "020000000000000007004033472a88df543ec17a8781265e8ee7e0f3dd4d2b08f5ed80237edd9080aca95c0576b2387e9779a331f42657a96a12dd4ee507900899795ccb942f9246a6d60f", + "full_tree_head": "02000000000000000700405ed23a0be437fae73a90b958a2775e4b3b8afb5482d97ae032ba58a0128cdeeda09285cade9d674365d9bf72ac3a58a7d2411b588e2b487556dba47be14dd70b", "inclusion": [], "prefix_proofs": [], "prefix_roots": [ - "b446b51ad0ad3c73729da7deb2b0c4e18e57351d859bdfca054d73ff3951271c" + "547ca7b15ded62e283e365b0da0d82e0b119b0986b85fb43419bd465cb7904dd" ], - "response": "020000000000000007004033472a88df543ec17a8781265e8ee7e0f3dd4d2b08f5ed80237edd9080aca95c0576b2387e9779a331f42657a96a12dd4ee507900899795ccb942f9246a6d60f010000019fae9828660001b446b51ad0ad3c73729da7deb2b0c4e18e57351d859bdfca054d73ff3951271c0000", + "response": "02000000000000000700405ed23a0be437fae73a90b958a2775e4b3b8afb5482d97ae032ba58a0128cdeeda09285cade9d674365d9bf72ac3a58a7d2411b588e2b487556dba47be14dd70b010000019fb36a17e00001547ca7b15ded62e283e365b0da0d82e0b119b0986b85fb43419bd465cb7904dd0000", "timestamps": [ - 1785340635238 + 1785421502432 ], "tree_size": 7 } @@ -777,17 +777,17 @@ "input": { "entries": [], "entry_timestamps": [ - 1785340635246, - 1785340635246, - 1785340635246, - 1785340635247, - 1785340635247, - 1785340635247, - 1785340635247 + 1785421502439, + 1785421502440, + 1785421502440, + 1785421502440, + 1785421502440, + 1785421502441, + 1785421502441 ], "known_versions": [ { - "commitment": "4899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22", + "commitment": "e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b08", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, @@ -866,15 +866,15 @@ "expect": { "binary_ladder": [ { - "commitment": "4899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22", + "commitment": "e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b08", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "c4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee897", + "commitment": "29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb696", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "ab2ffd6880fe80c6f852d4b9e7ed5516826623bce8d620beee2d123563f59722", + "commitment": "4147f08fda5c9f995445827dbabc8f5088b696ecb014d4803b5041a41e0eaa26", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { @@ -887,22 +887,22 @@ "proof": "26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e123409" } ], - "full_tree_head": "0200000000000000070040b76fc65ec19f291fc179591c8f20c2ac740c9b7647583295ef22e679bcb8dc8e04853dbc13043aabf5c9a0d96b6e29dfba9779f74817560990cc09140b99d800", + "full_tree_head": "020000000000000007004084fea47906011f0adb48330f9e17e6d15c9d01e2330089d9587b7305f189d24ef0b87526090b92070de402de9ba2e429b86a1a71dea97ace420910cbd7637903", "greatest_versions": [ 3 ], "inclusion": [ - "e998085e327400e893bb2ab7d6964e45028a4337f83bb9751a02ff956970e0d3", - "975717af978f48037cdd6670c97652731c71d4922585d8074189f95f84010f3e", - "62501f2df175ac776c0979506d0f3754732243f7634a6ed536709f859cf0c25e" + "afd78d69533cb680e74e9648078a43e8b4e464a8e4d1ac48ddc3fd27acbb9d3e", + "45e7de60d89944dbd5536a0ced005c83692d067be6b75b3c7b607e403432716b", + "7837b47eba924a5c6b25fabd9c6da6d5ea4d59e03df800a1b4250474e986e5cf" ], "prefix_proofs": [ { "elements": [ - "cdfd629b2a6faa997bafde0b6d0e9932667b014031445a7d8815806b8d205822", + "6c154bab24f868da78d02f9feaacc03420eb2388f48dd711e69d252d3edd1a48", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee8970202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee8970202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e34899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22040002cdfd629b2a6faa997bafde0b6d0e9932667b014031445a7d8815806b8d2058220000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb6960202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb6960202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b080400026c154bab24f868da78d02f9feaacc03420eb2388f48dd711e69d252d3edd1a480000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -919,7 +919,7 @@ { "depth": 2, "leaf": { - "commitment": "c4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee897", + "commitment": "29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb696", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -927,7 +927,7 @@ { "depth": 2, "leaf": { - "commitment": "c4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee897", + "commitment": "29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb696", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -935,7 +935,7 @@ { "depth": 4, "leaf": { - "commitment": "4899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22", + "commitment": "e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b08", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -944,14 +944,14 @@ } ], "prefix_roots": [ - "607b632a9aafaf731c709e41bdb9d2d76633c1fc45bd6e3bea7b5d9686d7f9f5", - "2baeda55193fc66ca6f4e698cdaf967054c0ede720f46fe19ec5b1e4919ea439" + "dbc2bf7a4c251171f94bfa58830db818c474d94d18d7e0c284b487879df02af1", + "ebaad6cd4f3572174521323f17897c624fca4836c2172c2a8bbd5b50ed0e0aa2" ], - "response": "0200000000000000070040b76fc65ec19f291fc179591c8f20c2ac740c9b7647583295ef22e679bcb8dc8e04853dbc13043aabf5c9a0d96b6e29dfba9779f74817560990cc09140b99d80001000000030006f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00014899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201c4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee897783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01ab2ffd6880fe80c6f852d4b9e7ed5516826623bce8d620beee2d123563f59722901738f65e8b0d720acdb242304a8725f8ca1acbb009fbd89c72016887f57fd6d75db48330615e002cec7257a9620a57bd2679cafe860c43c92d86e7751ff5fa38ff87f334f665233fb88fe5d60f240e00e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f6030026fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900030000019fae98286f0000019fae98286f0000019fae98286f010601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee8970202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc4fdc8aa054d763c508351e477a10d90f583cedc46bda100ac9429ae5fbee8970202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e34899033f93f321d87f49f7180b7f94f693b7e19f52dce2cf3adc79477f7bda22040002cdfd629b2a6faa997bafde0b6d0e9932667b014031445a7d8815806b8d205822000000000000000000000000000000000000000000000000000000000000000002607b632a9aafaf731c709e41bdb9d2d76633c1fc45bd6e3bea7b5d9686d7f9f52baeda55193fc66ca6f4e698cdaf967054c0ede720f46fe19ec5b1e4919ea4390003e998085e327400e893bb2ab7d6964e45028a4337f83bb9751a02ff956970e0d3975717af978f48037cdd6670c97652731c71d4922585d8074189f95f84010f3e62501f2df175ac776c0979506d0f3754732243f7634a6ed536709f859cf0c25e", + "response": "020000000000000007004084fea47906011f0adb48330f9e17e6d15c9d01e2330089d9587b7305f189d24ef0b87526090b92070de402de9ba2e429b86a1a71dea97ace420910cbd763790301000000030006f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b08414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf020129a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb696783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c014147f08fda5c9f995445827dbabc8f5088b696ecb014d4803b5041a41e0eaa26901738f65e8b0d720acdb242304a8725f8ca1acbb009fbd89c72016887f57fd6d75db48330615e002cec7257a9620a57bd2679cafe860c43c92d86e7751ff5fa38ff87f334f665233fb88fe5d60f240e00e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f6030026fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900030000019fb36a17e80000019fb36a17e90000019fb36a17e9010601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb6960202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f29a3b18a98a961f5e2fc37e245886b0a06799d4cf29d7f1d325240fed8afb6960202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3e7d65557ba97248d159059003c1f3ab66800a91f35611c35eaefc9f5b0d75b080400026c154bab24f868da78d02f9feaacc03420eb2388f48dd711e69d252d3edd1a48000000000000000000000000000000000000000000000000000000000000000002dbc2bf7a4c251171f94bfa58830db818c474d94d18d7e0c284b487879df02af1ebaad6cd4f3572174521323f17897c624fca4836c2172c2a8bbd5b50ed0e0aa20003afd78d69533cb680e74e9648078a43e8b4e464a8e4d1ac48ddc3fd27acbb9d3e45e7de60d89944dbd5536a0ced005c83692d067be6b75b3c7b607e403432716b7837b47eba924a5c6b25fabd9c6da6d5ea4d59e03df800a1b4250474e986e5cf", "timestamps": [ - 1785340635247, - 1785340635247, - 1785340635247 + 1785421502440, + 1785421502441, + 1785421502441 ], "tree_size": 7 } @@ -966,48 +966,48 @@ } ], "entry_timestamps": [ - 1785340635251, - 1785340635251, - 1785340635251, - 1785340635252, - 1785340635252, - 1785340635252, - 1785340635252 + 1785421502444, + 1785421502444, + 1785421502445, + 1785421502445, + 1785421502445, + 1785421502445, + 1785421502446 ], "greatest_version": 6, "known_versions": [ { - "commitment": "e6af5239f3705d4d502c7e40ba28af1b154a70a489bbbb743b04c38ca68b9f76", + "commitment": "4062ff1a0340594b340243ef543a5e0c021ff90738789de9e61d16c30cfa7922", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "b678a0ae8eab79260517d2af71bc5f8629e01a8265dd6b88d001090164662c9d", + "commitment": "b56ac86acbad96bde6df1138579ff594892d9c81015391f3a0be81070f758cfd", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "00a49defa1212084ce713e80c2ff1d79fc1202f1101584703607c1006f1d0961", + "commitment": "54ff665589bf78482cd0bb204ff6b1c5296bf0089cb48089a48573b82f3af873", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "64341059877483f2096b1f3342d536ceb827f3f72afae5ec1c27e9bf2cfd724a", + "commitment": "04c52f929e9f0dc559671ab833ad0703819593e5bc0284a563c65218a63bcd56", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "531775bf58d7b451acf77617062aebd8b6e7bf54136e62838dbda1b6f11a1256", + "commitment": "04703e63ddbeee4e74d0656b4210f460b3907e2dd80ee1f1a573d510b4c32e51", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "e47f76e65e5774e2383116eab8e68ddf0fbbee15b40d1a5132a1565f3d68c9d9", + "commitment": "6eaa49973206f3dfe3c45655df4051748bbb42d590147b1890c602edb3dd689a", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, { - "commitment": "cb5bd4b2e9ef4c7e24e6c1d444337c902b086de48239231c76ff6674684cc7f4", + "commitment": "4f91ea965b050bc1c91350854a8706402076c46aa459eeefd7a640f48dfee870", "version": 6, "vrf_output": "599424025a070d0e48913585265a30210ffe3416005c77c2354094f14c6358a1" }, @@ -1084,23 +1084,23 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "02000000000000000700402177d7969c9375530fc534de87fd21ba986dcc6fdbb226f8fd5713cb6e6356d88b32e6dc35d82cca50cb4421c745ace8db9eb499b681ea041eca00f14653a40f", + "full_tree_head": "0200000000000000070040021e01844a70c81affffedf47e9b2fdac67bb6f149c754b7401e0fb5a7eadb363509635a78a7d0bf5e9526154f72e16fa1d56766e3e6b3c3fc6fc7b7e5d9b90f", "inclusion": [ - "2021fda0d57154e9cc7d62562d181d7ae7bec45f75e9e59df58dcb9dd26df952", - "97ca06ebe06487ab3fed631d5fca56ebcad91463bdccb5c5b0545a8ad34c77a1", - "1b1e0c65278544da2320347d50472accee17f3fa9026f7165cc018e062b728fc" + "0ca232dc0e3e7415e5a6bd25e6c2822b8281a4122a38c0956568e01438ae4c0f", + "3a1e55fc0d8794348d1ed94b88680c36f94b17f9049ab11510b0916a8bd58188", + "743229894757a673568d7e1bc85a2072ffb1686b18b4ea61b0099d0eb31605d1" ], "prefix_proofs": [], "prefix_roots": [ - "5261d9024b49fa472cbe1f1448e1ba21ed4c6b7a9dcad89c90819e1889bcc700", - "252d5fa5591ef3a9a437be66efcfb8f71fe6c58f8aafb478a8fbfa9d73bda94c", - "a73b6b31601e749f7c5e2e49f6c0c5615f9f9ef55919d7433e05caa08929ac00" + "e61f4bcfb5753119028bfd4bc4d0ce384f80282436e102343f859723fdc4f8c5", + "76143c8ce0bc1016e32bc19f892f8c46a0d9b2299f550ab45176cb93635046c7", + "0696753cf49f1e278649f655c56262b8515df47cfecbbab69aeefd423049bd42" ], - "response": "02000000000000000700402177d7969c9375530fc534de87fd21ba986dcc6fdbb226f8fd5713cb6e6356d88b32e6dc35d82cca50cb4421c745ace8db9eb499b681ea041eca00f14653a40f030000019fae9828740000019fae9828740000019fae98287400035261d9024b49fa472cbe1f1448e1ba21ed4c6b7a9dcad89c90819e1889bcc700252d5fa5591ef3a9a437be66efcfb8f71fe6c58f8aafb478a8fbfa9d73bda94ca73b6b31601e749f7c5e2e49f6c0c5615f9f9ef55919d7433e05caa08929ac0000032021fda0d57154e9cc7d62562d181d7ae7bec45f75e9e59df58dcb9dd26df95297ca06ebe06487ab3fed631d5fca56ebcad91463bdccb5c5b0545a8ad34c77a11b1e0c65278544da2320347d50472accee17f3fa9026f7165cc018e062b728fc", + "response": "0200000000000000070040021e01844a70c81affffedf47e9b2fdac67bb6f149c754b7401e0fb5a7eadb363509635a78a7d0bf5e9526154f72e16fa1d56766e3e6b3c3fc6fc7b7e5d9b90f030000019fb36a17ed0000019fb36a17ed0000019fb36a17ee0003e61f4bcfb5753119028bfd4bc4d0ce384f80282436e102343f859723fdc4f8c576143c8ce0bc1016e32bc19f892f8c46a0d9b2299f550ab45176cb93635046c70696753cf49f1e278649f655c56262b8515df47cfecbbab69aeefd423049bd4200030ca232dc0e3e7415e5a6bd25e6c2822b8281a4122a38c0956568e01438ae4c0f3a1e55fc0d8794348d1ed94b88680c36f94b17f9049ab11510b0916a8bd58188743229894757a673568d7e1bc85a2072ffb1686b18b4ea61b0099d0eb31605d1", "timestamps": [ - 1785340635252, - 1785340635252, - 1785340635252 + 1785421502445, + 1785421502445, + 1785421502446 ], "tree_size": 7 } @@ -1115,48 +1115,48 @@ } ], "entry_timestamps": [ - 1785340635260, - 1785340635360, - 1785340635462, - 1785340635564, - 1785340635666, - 1785340635767, - 1785340635869 + 1785421502453, + 1785421502555, + 1785421502656, + 1785421502757, + 1785421502859, + 1785421502960, + 1785421503061 ], "greatest_version": 6, "known_versions": [ { - "commitment": "ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b5", + "commitment": "fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f4", "version": 0, "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, { - "commitment": "c251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a", + "commitment": "b9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad", "version": 1, "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, { - "commitment": "5965795a8345cfdd990b9dd991eb27e7641129544e1e8c5469aa57cfa6673ab5", + "commitment": "671f2652baad13e26fddfbfda27d30d44c0cdb53e534fed6aa0fd677006bda3f", "version": 2, "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, { - "commitment": "366e715075704d3c68ec2e5f67615b42aee4c10a6c2953727056d68c3f95efc1", + "commitment": "d6e27d89ec5f301427bc7a64ef55aeee35ab16607046f376939eaa3a9b63bc25", "version": 3, "vrf_output": "ef0582ede742f1d47079dad60b655a3e4bab73d2bf1da0b3d6db7a833f132fee" }, { - "commitment": "82dd11271483571eeccd38c72de1690f6d7a0817629c909c570a0c07735e251b", + "commitment": "c59b964bde518e97f43cedd8f522c277f92e99fe1c27e36d77a2068d7f2f345c", "version": 4, "vrf_output": "f9177f084968b05e8b34978d8f50108baad4083600b51f944dc54460e0d850ae" }, { - "commitment": "b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f", + "commitment": "f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b75", "version": 5, "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, { - "commitment": "6eb941d6fe37b895d0cd86d7ae7b485d6f6bebe2894e5903f5b16094d5d4d018", + "commitment": "7fd3cc4acafdbd62fbe277db45d7ae8d8ff45380959b0b2a2f0f69c5ab541e5c", "version": 6, "vrf_output": "599424025a070d0e48913585265a30210ffe3416005c77c2354094f14c6358a1" }, @@ -1233,14 +1233,14 @@ "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" }, "expect": { - "full_tree_head": "0200000000000000070040fe2b41964ab691ba832da19f394a0f3ac01db44114f8ddcd3e27d8d5ca98f8d4860b1ada1bfbedafab3807bba6963ad94475674e7dce5385438836a6dad0b309", + "full_tree_head": "02000000000000000700405cd6beefe7235a5d813551fc0df543a4747e5b41f45f971557039aa6e6a2ed29eb76c2d2e4f4a4dddb1c9e5b09b3c5552ba9a0a0d26ffeb626be31965185740e", "inclusion": [ - "2fe0f356f4e85febc2d0e5b2b6cc9cbefcd106b271aaad0e4bf0b78adce995b6" + "4677bcc8145bd7b21ee92e5dd8dd0b8be5bfc34314eca256462415eeb1efeb3f" ], "prefix_proofs": [ { "elements": [], - "encoding": "040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b50201010000", + "encoding": "040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f40201010000", "results": [ { "depth": 2, @@ -1253,7 +1253,7 @@ { "depth": 2, "leaf": { - "commitment": "ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b5", + "commitment": "fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f4", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -1266,10 +1266,10 @@ }, { "elements": [ - "22da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a9", + "e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e868", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b504000222da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a90000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f4040002e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e8680000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -1286,7 +1286,7 @@ { "depth": 2, "leaf": { - "commitment": "c251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a", + "commitment": "b9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1294,7 +1294,7 @@ { "depth": 2, "leaf": { - "commitment": "c251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a", + "commitment": "b9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1302,7 +1302,7 @@ { "depth": 4, "leaf": { - "commitment": "ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b5", + "commitment": "fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f4", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -1311,11 +1311,11 @@ }, { "elements": [ - "22da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a9", + "e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e868", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0601060102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a020106000322da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0601060102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0201060003e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e86800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 6, @@ -1332,7 +1332,7 @@ { "depth": 2, "leaf": { - "commitment": "c251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a", + "commitment": "b9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1340,7 +1340,7 @@ { "depth": 2, "leaf": { - "commitment": "c251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a", + "commitment": "b9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1355,9 +1355,9 @@ "elements": [ "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", - "af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea61" + "d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b13" ], - "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e455965795a8345cfdd990b9dd991eb27e7641129544e1e8c5469aa57cfa6673ab501000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea61", + "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b750301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45671f2652baad13e26fddfbfda27d30d44c0cdb53e534fed6aa0fd677006bda3f01000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b13", "results": [ { "depth": 6, @@ -1374,7 +1374,7 @@ { "depth": 3, "leaf": { - "commitment": "b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f", + "commitment": "f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b75", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1386,7 +1386,7 @@ { "depth": 1, "leaf": { - "commitment": "5965795a8345cfdd990b9dd991eb27e7641129544e1e8c5469aa57cfa6673ab5", + "commitment": "671f2652baad13e26fddfbfda27d30d44c0cdb53e534fed6aa0fd677006bda3f", "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, "result_type": 2 @@ -1396,13 +1396,13 @@ { "elements": [ "0000000000000000000000000000000000000000000000000000000000000000", - "22da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a9", + "e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e868", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", - "af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea61" + "d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b13" ], - "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f03010301040006000000000000000000000000000000000000000000000000000000000000000022da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea61", + "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b75030103010400060000000000000000000000000000000000000000000000000000000000000000e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e868000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b13", "results": [ { "depth": 6, @@ -1419,7 +1419,7 @@ { "depth": 3, "leaf": { - "commitment": "b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f", + "commitment": "f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b75", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1436,16 +1436,16 @@ } ], "prefix_roots": [ - "48a2928ab1eb4f1fd049065234b41996440eda6a9a3d22db1c29df02bffb53f0" + "3a40998755bd658960244d0d42c0863d1e303a03b12612ed22f5f43f36b1b448" ], - "response": "0200000000000000070040fe2b41964ab691ba832da19f394a0f3ac01db44114f8ddcd3e27d8d5ca98f8d4860b1ada1bfbedafab3807bba6963ad94475674e7dce5385438836a6dad0b309060000019fae9829ac0000019fae982a770000019fae982add0000019fae9828e00000019fae9829460000019fae982a1205040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b502010100000601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3ded52cab47906fc745f16dd244fd8e3c2744acea333b36097f5a23a1e47d07b504000222da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a900000000000000000000000000000000000000000000000000000000000000000601060102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fc251e7b73aa4ee628a8837b513a41dad7b467bd94d1d45112e27327dd6ee769a020106000322da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a90000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e455965795a8345cfdd990b9dd991eb27e7641129544e1e8c5469aa57cfa6673ab501000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea6106010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b2dcee71e4ce697f43980816087607974cc6839a7559dcdc1852f57f93a3cd1f03010301040006000000000000000000000000000000000000000000000000000000000000000022da6b106f6ee71baa2f26989f1613a2668b5cae8451dce07995fdd5ab0629a9000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000af032ef7eda961fb40119d3c124df42235546c9249e9ca04a7b239e3f841ea610148a2928ab1eb4f1fd049065234b41996440eda6a9a3d22db1c29df02bffb53f000012fe0f356f4e85febc2d0e5b2b6cc9cbefcd106b271aaad0e4bf0b78adce995b6", + "response": "02000000000000000700405cd6beefe7235a5d813551fc0df543a4747e5b41f45f971557039aa6e6a2ed29eb76c2d2e4f4a4dddb1c9e5b09b3c5552ba9a0a0d26ffeb626be31965185740e060000019fb36a19250000019fb36a19f00000019fb36a1a550000019fb36a185b0000019fb36a18c00000019fb36a198b05040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f402010100000601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3fdffdd3f251f370e52e8eec29eff46bf39a3f0259f8d7e739aadf8a784aee4f4040002e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e86800000000000000000000000000000000000000000000000000000000000000000601060102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb9c8b261cf6cc63b2d16069ef7636803a2f0209cabcc17b977dfee36d4707dad0201060003e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e8680000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b750301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45671f2652baad13e26fddfbfda27d30d44c0cdb53e534fed6aa0fd677006bda3f01000300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b1306010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f1e44b991c661cbf52c645d129ebb185f4b9af6d6819999079f3b75d828f0b75030103010400060000000000000000000000000000000000000000000000000000000000000000e5d7fb97ded57f04c96d1b2b79aaa8959a9b7ec8960a28de9e9c53d1d9f1e868000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d6d9eb4adca2ab1520141eb70d3126d24632a781fae47ca3cc969414708a5b13013a40998755bd658960244d0d42c0863d1e303a03b12612ed22f5f43f36b1b44800014677bcc8145bd7b21ee92e5dd8dd0b8be5bfc34314eca256462415eeb1efeb3f", "timestamps": [ - 1785340635564, - 1785340635767, - 1785340635869, - 1785340635360, - 1785340635462, - 1785340635666 + 1785421502757, + 1785421502960, + 1785421503061, + 1785421502555, + 1785421502656, + 1785421502859 ], "tree_size": 7 } diff --git a/interop/vectors/search.json b/interop/vectors/search.json index fba9dea..3b932fb 100644 --- a/interop/vectors/search.json +++ b/interop/vectors/search.json @@ -12,13 +12,13 @@ "name": "greatest-version-first-search", "input": { "entry_timestamps": [ - 1785340632706, - 1785340632706, - 1785340632706, - 1785340632707, - 1785340632707, - 1785340632707, - 1785340632707 + 1785421499917, + 1785421499917, + 1785421499917, + 1785421499917, + 1785421499918, + 1785421499918, + 1785421499918 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -94,42 +94,42 @@ "expect": { "binary_ladder": [ { - "commitment": "5019302d96fae104e70ca046fab8736b65bebee2f7243b5a6253ebf1a36c43b2", + "commitment": "ccc8aa9cf612e92467163ca7e98163df04cf028503de1d8e96d30b495c03a837", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "b1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f", + "commitment": "67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "c1ecf7fe174b997e1d0f4dbef2fe40106e67113fdc2b4d73457c960b15841b11", + "commitment": "4e296d2ebea44d686271c18b10539d793d1535833364568f6dbec62e75ade096", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { "proof": "26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e123409" }, { - "commitment": "52270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e5", + "commitment": "906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c6088", "proof": "e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f603" }, { "proof": "79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a" } ], - "full_tree_head": "0200000000000000070040a9fb5baab632c9e28f9c3644b7e7231fdef2e53a35e691935832ef62ceb15af2f1d566cb8dd579c682be4dbc388f98fb0ab193fe3012d7e367958dcd2b50a60a", + "full_tree_head": "020000000000000007004012557040894c647c2d681bb53795c06fd809f71b7b609bb34d13fe0ff1cd8f253bbbaf70c555983df06c5160e0a89e6470e16d1796e643ef2771278496943303", "inclusion": [ - "fe5d5ec2e5a002b6830031697af2dff269824b770d6c33586f5cb4e08b22a78f", - "0ba141695440b038f5300cee49c24e3992ff902d59e7dbd4558909e7529d9002", - "6612d1d4dba32cf7e112aae4cf31b119a7b79ffeb67513b0a04bd4e6823a358a" + "ca2293a2b70c3619dd78770057685c6efb1e94d65f20ced112d4ca5e6e040dc8", + "266bbbed13171a443b31b72ba378cfed04c3fe9355ec558b6d22d29a29f275b8", + "ad2fafa40cd5b68e50b57a0256a3f13e75932b60dc03a8efc68361e35a96409c" ], - "opening": "37beded31b05b5bee0596d84c5b0432f", + "opening": "c6178742ce6ca5a16703900f8efeb55a", "prefix_proofs": [ { "elements": [ - "dc477e2bb44b533cce9c26c9eca4d758480b6f5aaf08c39f69bde2bfce9ff9f7", + "d5e7edfb0f546ee49cf22cda4f6d586c14e141f056ea2e4b85a67c940581590e", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f020002dc477e2bb44b533cce9c26c9eca4d758480b6f5aaf08c39f69bde2bfce9ff9f70000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a390202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39020002d5e7edfb0f546ee49cf22cda4f6d586c14e141f056ea2e4b85a67c940581590e0000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -146,7 +146,7 @@ { "depth": 2, "leaf": { - "commitment": "b1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f", + "commitment": "67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -154,7 +154,7 @@ { "depth": 2, "leaf": { - "commitment": "b1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f", + "commitment": "67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -163,16 +163,16 @@ }, { "elements": [ - "13f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e45", - "8a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e", - "11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c8931154" + "a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb17", + "74a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f", + "5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c909" ], - "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba852270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e50301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45257131fba847ad4edd67447637017ebcdcf28daf11cfbf27214120edd0d3182202000313f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e458a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c8931154", + "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c60880301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45307e0ea426878887b06451682ac0c065b892b232342505aaab4dfab85609f01a020003a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb1774a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c909", "results": [ { "depth": 3, "leaf": { - "commitment": "52270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e5", + "commitment": "906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c6088", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -184,7 +184,7 @@ { "depth": 2, "leaf": { - "commitment": "257131fba847ad4edd67447637017ebcdcf28daf11cfbf27214120edd0d31822", + "commitment": "307e0ea426878887b06451682ac0c065b892b232342505aaab4dfab85609f01a", "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, "result_type": 2 @@ -193,18 +193,18 @@ }, { "elements": [ - "13f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e45", - "f8a967951f65082e676ec968e2323328d2d24d3b70bc1175c436865ba2de0abb", + "a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb17", + "10e8f78586c67c96ad7651e7b3bef7c2029703b108660e23cead91d65743041e", "0000000000000000000000000000000000000000000000000000000000000000", - "8a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e", - "11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c8931154" + "74a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f", + "5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c909" ], - "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba852270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e5030104000513f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e45f8a967951f65082e676ec968e2323328d2d24d3b70bc1175c436865ba2de0abb00000000000000000000000000000000000000000000000000000000000000008a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c8931154", + "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c60880301040005a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb1710e8f78586c67c96ad7651e7b3bef7c2029703b108660e23cead91d65743041e000000000000000000000000000000000000000000000000000000000000000074a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c909", "results": [ { "depth": 3, "leaf": { - "commitment": "52270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e5", + "commitment": "906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c6088", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -217,11 +217,11 @@ } ], "prefix_roots": [], - "response": "0200000000000000070040a9fb5baab632c9e28f9c3644b7e7231fdef2e53a35e691935832ef62ceb15af2f1d566cb8dd579c682be4dbc388f98fb0ab193fe3012d7e367958dcd2b50a60a0000000637beded31b05b5bee0596d84c5b0432f00000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00015019302d96fae104e70ca046fab8736b65bebee2f7243b5a6253ebf1a36c43b2414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201b1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01c1ecf7fe174b997e1d0f4dbef2fe40106e67113fdc2b4d73457c960b15841b1126fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f6030152270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e579f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fae981e830000019fae981e830000019fae981e83030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb1783bee1e90eb4c5c1c47e2b2435c0533ce8a2e99ddd0d246c8cf0f1d9aa95f020002dc477e2bb44b533cce9c26c9eca4d758480b6f5aaf08c39f69bde2bfce9ff9f7000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba852270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e50301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45257131fba847ad4edd67447637017ebcdcf28daf11cfbf27214120edd0d3182202000313f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e458a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c893115402028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba852270e23b27643569fa405662985c103996ad953255d4fccfd529fca916600e5030104000513f9281e656b5af9c3265360a7327cf526e60c6ea515553062f0bc16d0348e45f8a967951f65082e676ec968e2323328d2d24d3b70bc1175c436865ba2de0abb00000000000000000000000000000000000000000000000000000000000000008a93bfc064557eb188fa700dd0a7a456d79bf5f6c906c7e3d3cbb14e79fe2d4e11224370bc6af2190c6c4a289423a5b62ba502abe87c919e52983a45c8931154000003fe5d5ec2e5a002b6830031697af2dff269824b770d6c33586f5cb4e08b22a78f0ba141695440b038f5300cee49c24e3992ff902d59e7dbd4558909e7529d90026612d1d4dba32cf7e112aae4cf31b119a7b79ffeb67513b0a04bd4e6823a358a", + "response": "020000000000000007004012557040894c647c2d681bb53795c06fd809f71b7b609bb34d13fe0ff1cd8f253bbbaf70c555983df06c5160e0a89e6470e16d1796e643ef277127849694330300000006c6178742ce6ca5a16703900f8efeb55a00000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001ccc8aa9cf612e92467163ca7e98163df04cf028503de1d8e96d30b495c03a837414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf020167cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c014e296d2ebea44d686271c18b10539d793d1535833364568f6dbec62e75ade09626fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c608879f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fb36a0e0d0000019fb36a0e0e0000019fb36a0e0e030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a390202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f67cdce42dd5916777296f60cb7d733249244cedec6d70d15505e782027362a39020002d5e7edfb0f546ee49cf22cda4f6d586c14e141f056ea2e4b85a67c940581590e000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c60880301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45307e0ea426878887b06451682ac0c065b892b232342505aaab4dfab85609f01a020003a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb1774a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c90902028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8906fb123fae09b3cea173811b7598866ef0902aced3d11507300a2e3425c60880301040005a9c29696ddaf62ab64e00bd667cc1ffee0a1b4f467c706a27344583f4059eb1710e8f78586c67c96ad7651e7b3bef7c2029703b108660e23cead91d65743041e000000000000000000000000000000000000000000000000000000000000000074a8445f9d8c43c2366434fc9639964a856b3e63616e34d770dd9613078e331f5482ba6bbd6cf464986784aae48eeee0c5fd9349681b4f9dfe4bee359934c909000003ca2293a2b70c3619dd78770057685c6efb1e94d65f20ced112d4ca5e6e040dc8266bbbed13171a443b31b72ba378cfed04c3fe9355ec558b6d22d29a29f275b8ad2fafa40cd5b68e50b57a0256a3f13e75932b60dc03a8efc68361e35a96409c", "timestamps": [ - 1785340632707, - 1785340632707, - 1785340632707 + 1785421499917, + 1785421499918, + 1785421499918 ], "tree_size": 7, "version": 6 @@ -231,13 +231,13 @@ "name": "greatest-version-with-advertised-size", "input": { "entry_timestamps": [ - 1785340632709, - 1785340632709, - 1785340632709, - 1785340632710, - 1785340632710, - 1785340632710, - 1785340632710 + 1785421499919, + 1785421499920, + 1785421499920, + 1785421499920, + 1785421499920, + 1785421499920, + 1785421499921 ], "label": "616c696365406578616d706c652e636f6d", "last": 4, @@ -314,40 +314,40 @@ "expect": { "binary_ladder": [ { - "commitment": "e940036e55895f2b17eca16c8a5f69f830a1390e3c5b03014d969b6c8501f3f0", + "commitment": "633f09d01acb11ed1c701d983cdbb6e174a9d6b9bc4df51c869ea7809c8d44d1", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe261", + "commitment": "5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "219ca3c75f046284d87ec40df4fc4d692256f08012e6ec865dc7f241b39a8713", + "commitment": "2d96e1b42e9687bcdfac66bd4edf9b72c1018b9837b6b309ecbbada8737cc853", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { "proof": "26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e123409" }, { - "commitment": "546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d2776", + "commitment": "c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c997", "proof": "e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f603" }, { "proof": "79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a" } ], - "full_tree_head": "0200000000000000070040448dab3282e10369e05330e5d3bb0652e70e084daa82f7062cbd9b0851d4145c05dca2e40438d2b8d947aa77639ee720ec6cb028260e0c09dc2648588aa36503", + "full_tree_head": "020000000000000007004085dffe878359ebfce6c2095d7caa40bcae8cd49d86bd305b33fee27a4a8f31beaac255558c3c120f73c825bc67711bbc1c0b7bf4295f9535fadbd958e0ca6f0f", "inclusion": [ - "4d66c92ff1a24e5ebb9678634c8c641de9649b9b4dce891b563653bbaf1589d7" + "39de8f5247ab26a3a03a0f82e5886621f716e617eaaaea990375ba9213d74ca5" ], - "opening": "3485da070777b330a8bfc2899ae36ed1", + "opening": "a112d9c2c89f19251862a5f84451fb0e", "prefix_proofs": [ { "elements": [ - "3f3de394ab1d9abc814aa2940cf430376f8a087ab37e8898018236dd39fd8a4c", + "bbff561fd67894598b9e33cfac4a74450cf31471f50d415a59154d48b1b50ba6", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe2610202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe2610200023f3de394ab1d9abc814aa2940cf430376f8a087ab37e8898018236dd39fd8a4c0000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb90202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9020002bbff561fd67894598b9e33cfac4a74450cf31471f50d415a59154d48b1b50ba60000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -364,7 +364,7 @@ { "depth": 2, "leaf": { - "commitment": "4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe261", + "commitment": "5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -372,7 +372,7 @@ { "depth": 2, "leaf": { - "commitment": "4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe261", + "commitment": "5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -381,16 +381,16 @@ }, { "elements": [ - "636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e3", - "f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad0", - "8ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e5" + "b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af", + "59348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e480174", + "3c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be15071" ], - "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d27760301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45a69121856a5151e1d77e32f5c0e247067b0573a1381026a85bbb5db05f609952020003636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e3f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad08ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e5", + "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c9970301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45b193cd1501231b783dca3053cb842200e3869d6ad8ac77f6500bd6d5fd06dcdf020003b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af59348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e4801743c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be15071", "results": [ { "depth": 3, "leaf": { - "commitment": "546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d2776", + "commitment": "c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c997", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -402,7 +402,7 @@ { "depth": 2, "leaf": { - "commitment": "a69121856a5151e1d77e32f5c0e247067b0573a1381026a85bbb5db05f609952", + "commitment": "b193cd1501231b783dca3053cb842200e3869d6ad8ac77f6500bd6d5fd06dcdf", "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, "result_type": 2 @@ -411,18 +411,18 @@ }, { "elements": [ - "636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e3", - "5ef59df4f12ffd0f15d469e92c2d6064397296d699a713455dd3029501463db4", + "b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af", + "0f648799d42867669ac4f20753a64a0dd69b8eaf76d4eec939e9ee14c26cb0f0", "0000000000000000000000000000000000000000000000000000000000000000", - "f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad0", - "8ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e5" + "59348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e480174", + "3c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be15071" ], - "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d27760301040005636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e35ef59df4f12ffd0f15d469e92c2d6064397296d699a713455dd3029501463db40000000000000000000000000000000000000000000000000000000000000000f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad08ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e5", + "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c9970301040005b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af0f648799d42867669ac4f20753a64a0dd69b8eaf76d4eec939e9ee14c26cb0f0000000000000000000000000000000000000000000000000000000000000000059348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e4801743c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be15071", "results": [ { "depth": 3, "leaf": { - "commitment": "546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d2776", + "commitment": "c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c997", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -435,10 +435,10 @@ } ], "prefix_roots": [], - "response": "0200000000000000070040448dab3282e10369e05330e5d3bb0652e70e084daa82f7062cbd9b0851d4145c05dca2e40438d2b8d947aa77639ee720ec6cb028260e0c09dc2648588aa36503000000063485da070777b330a8bfc2899ae36ed100000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001e940036e55895f2b17eca16c8a5f69f830a1390e3c5b03014d969b6c8501f3f0414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02014e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe261783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01219ca3c75f046284d87ec40df4fc4d692256f08012e6ec865dc7f241b39a871326fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d277679f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00020000019fae981e860000019fae981e86030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe2610202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4e1fa58a25714e4b0225b5b28ea2faac9ae238891bef18fe3981f44a2c8fe2610200023f3de394ab1d9abc814aa2940cf430376f8a087ab37e8898018236dd39fd8a4c000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d27760301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45a69121856a5151e1d77e32f5c0e247067b0573a1381026a85bbb5db05f609952020003636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e3f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad08ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e502028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8546da0f117364ec5a0e555b26a86d1e6d0ca0df041a00ddc88fb7b67673d27760301040005636ab9f164afce6e31cb87e78113f23bc97e01900e8197eddf1fdd1b436853e35ef59df4f12ffd0f15d469e92c2d6064397296d699a713455dd3029501463db40000000000000000000000000000000000000000000000000000000000000000f4cf3e32875ca4b30eb89921049fe554231c2b99a57cb525e6de0cb21e23bad08ea63c2832d91df49387b49834b802cf7357923f3ec08ab3c0cf7b4edaf117e50000014d66c92ff1a24e5ebb9678634c8c641de9649b9b4dce891b563653bbaf1589d7", + "response": "020000000000000007004085dffe878359ebfce6c2095d7caa40bcae8cd49d86bd305b33fee27a4a8f31beaac255558c3c120f73c825bc67711bbc1c0b7bf4295f9535fadbd958e0ca6f0f00000006a112d9c2c89f19251862a5f84451fb0e00000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001633f09d01acb11ed1c701d983cdbb6e174a9d6b9bc4df51c869ea7809c8d44d1414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02015c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c012d96e1b42e9687bcdfac66bd4edf9b72c1018b9837b6b309ecbbada8737cc85326fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c99779f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00020000019fb36a0e110000019fb36a0e10030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb90202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f5c11c10b31a4ddb706e0596c8032dbe5db70a47844850678e6879a8416bdcbb9020002bbff561fd67894598b9e33cfac4a74450cf31471f50d415a59154d48b1b50ba6000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c9970301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45b193cd1501231b783dca3053cb842200e3869d6ad8ac77f6500bd6d5fd06dcdf020003b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af59348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e4801743c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be1507102028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8c6c871614ea59b89caca8ab0ab92f8dfbc5ba704c4e7fa135de8652d0a17c9970301040005b97d25dec88d36b394d6b4cccbe57003c40c9a917c9233c889c378e9ce85c2af0f648799d42867669ac4f20753a64a0dd69b8eaf76d4eec939e9ee14c26cb0f0000000000000000000000000000000000000000000000000000000000000000059348b87da8c37a78d1508350c3b9af1caae907376403d60320225be6e4801743c3dbba103cfabe640831de5011bf6981b4b558c99615ed51f6d0ee98be1507100000139de8f5247ab26a3a03a0f82e5886621f716e617eaaaea990375ba9213d74ca5", "timestamps": [ - 1785340632710, - 1785340632710 + 1785421499921, + 1785421499920 ], "tree_size": 7, "version": 6 @@ -448,13 +448,13 @@ "name": "greatest-version-single-version-label", "input": { "entry_timestamps": [ - 1785340632712, - 1785340632712, - 1785340632712, - 1785340632712, - 1785340632713, - 1785340632713, - 1785340632713 + 1785421499922, + 1785421499922, + 1785421499922, + 1785421499923, + 1785421499923, + 1785421499923, + 1785421499923 ], "label": "626f62406578616d706c652e636f6d", "max_ahead": 10000, @@ -536,20 +536,20 @@ "proof": "f4f1731a9622a7d2e073010a0f633b7a7480ed6ec7d1b7b139f070ded0837c6d37ad8fe5463defe0cf800861a66f5ac1d413a5bcfd5e1de2f3c5234a90a68e35f6ee818bd00ddeb7307b3a1718f5e003" } ], - "full_tree_head": "0200000000000000070040a6cd0be7d0d145ac6703e81d67c949f50607ea7ac07775192bb69c1c24f6b7edc233829a27f595f0bcbec19c911f0756d5adb299e2864b384b44db553cd93e0b", + "full_tree_head": "02000000000000000700408dcb91f242a0052477e5733626a8de07d1481912f12a2625e1a1a0b19170f1cae2ddd4dd98a41715458927dfa25da5ddb1c0f4ce4f4be16f450fc06d391c2d0a", "inclusion": [ - "558519d43c734ce4ede3bd4a3a4f6eb585d94cab26ab9912abd58b22e6cf25f7", - "53f6d08a95efe1df164d718ff099801dc03f49a2340d1dd3461779083d67a6dd", - "1103cce6b3427c443cb52fcd24aa853f003e4c140c62b5c082791a005999387d" + "7124d9c08136196c87e10bef688e57cac3ee16aeee339bc8afd7dfe19b071665", + "0c520d8efe571b4f4a606ef06e6fb0e3fdf5034ada7c6306515dd13867163260", + "4554a802828fef4e1acf863320c7c245e498b09abfbc77573977169524f138ff" ], - "opening": "816587d0ed00715e22e1147db9a3f452", + "opening": "4d4e49e33f5e1f6c024f457eb0a10517", "prefix_proofs": [ { "elements": [ - "50384e3cc532f1917a2a14468ceef8efe65fc08983a07f7fda1659201134d5b5", - "0268507a5d8ddbc017383ef49e7dcf3d0b4c449a2b079f69f54c8b5a6d0c9648" + "b9837eb3daad78ac21fe0efc2f250c776701833053372cf63040a71ba38b31f5", + "ce52f2fc73352702cb468590f857a43ab8ed70b4e830b507fbb2c13c4a80048d" ], - "encoding": "02010202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab02000250384e3cc532f1917a2a14468ceef8efe65fc08983a07f7fda1659201134d5b50268507a5d8ddbc017383ef49e7dcf3d0b4c449a2b079f69f54c8b5a6d0c9648", + "encoding": "02010202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d020002b9837eb3daad78ac21fe0efc2f250c776701833053372cf63040a71ba38b31f5ce52f2fc73352702cb468590f857a43ab8ed70b4e830b507fbb2c13c4a80048d", "results": [ { "depth": 2, @@ -558,7 +558,7 @@ { "depth": 2, "leaf": { - "commitment": "e6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab", + "commitment": "2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -567,16 +567,16 @@ }, { "elements": [ - "8725c70ae0d823817b5dca2679395aeddcf1805aa5e7e68e83fc1a76dde8637c", - "aee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9", - "e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd69" + "b704e1ea9fc60a232e074795114047c1dddcc1e42bca8c8e61461e8b3614d0bf", + "47533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dc", + "d1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f" ], - "encoding": "0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab0300038725c70ae0d823817b5dca2679395aeddcf1805aa5e7e68e83fc1a76dde8637caee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd69", + "encoding": "0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d030003b704e1ea9fc60a232e074795114047c1dddcc1e42bca8c8e61461e8b3614d0bf47533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dcd1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f", "results": [ { "depth": 3, "leaf": { - "commitment": "e6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab", + "commitment": "2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -585,16 +585,16 @@ }, { "elements": [ - "907547d42b213adc369025ea69ee3c61a3a388d04c5f36ee1e8f35950b58d71a", - "aee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9", - "e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd69" + "a0535beef1cc2473acd7ef41c29b03c3dead7169b471ee90c9f62c6c999b5f73", + "47533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dc", + "d1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f" ], - "encoding": "0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab030003907547d42b213adc369025ea69ee3c61a3a388d04c5f36ee1e8f35950b58d71aaee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd69", + "encoding": "0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d030003a0535beef1cc2473acd7ef41c29b03c3dead7169b471ee90c9f62c6c999b5f7347533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dcd1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f", "results": [ { "depth": 3, "leaf": { - "commitment": "e6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab", + "commitment": "2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -603,11 +603,11 @@ } ], "prefix_roots": [], - "response": "0200000000000000070040a6cd0be7d0d145ac6703e81d67c949f50607ea7ac07775192bb69c1c24f6b7edc233829a27f595f0bcbec19c911f0756d5adb299e2864b384b44db553cd93e0b00000000816587d0ed00715e22e1147db9a3f45200000005626f622d31021cfe4094661cd115c04be0c885e8f5d1a223195bd4eb8c012cebdfb8d039c825470e056ca834618373a9c92c2a716e75eee517652f2d0580dd44750bb47ecb8c1e906187bdb596c146d4d7abed149a0a00f4f1731a9622a7d2e073010a0f633b7a7480ed6ec7d1b7b139f070ded0837c6d37ad8fe5463defe0cf800861a66f5ac1d413a5bcfd5e1de2f3c5234a90a68e35f6ee818bd00ddeb7307b3a1718f5e00300030000019fae981e880000019fae981e890000019fae981e890302010202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab02000250384e3cc532f1917a2a14468ceef8efe65fc08983a07f7fda1659201134d5b50268507a5d8ddbc017383ef49e7dcf3d0b4c449a2b079f69f54c8b5a6d0c96480102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab0300038725c70ae0d823817b5dca2679395aeddcf1805aa5e7e68e83fc1a76dde8637caee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd690102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fe6c5c17ae48e3ea9e6e8f511f5c28d8f21323f29753ae798a84e0e54894b79ab030003907547d42b213adc369025ea69ee3c61a3a388d04c5f36ee1e8f35950b58d71aaee98a365856b5cf5ecda58b7220f203fd67156088f696b832a5948ae641f1d9e3939702e0e69223cfac390de01437af712dc6a0a9850d9c27e9f3c867d7cd69000003558519d43c734ce4ede3bd4a3a4f6eb585d94cab26ab9912abd58b22e6cf25f753f6d08a95efe1df164d718ff099801dc03f49a2340d1dd3461779083d67a6dd1103cce6b3427c443cb52fcd24aa853f003e4c140c62b5c082791a005999387d", + "response": "02000000000000000700408dcb91f242a0052477e5733626a8de07d1481912f12a2625e1a1a0b19170f1cae2ddd4dd98a41715458927dfa25da5ddb1c0f4ce4f4be16f450fc06d391c2d0a000000004d4e49e33f5e1f6c024f457eb0a1051700000005626f622d31021cfe4094661cd115c04be0c885e8f5d1a223195bd4eb8c012cebdfb8d039c825470e056ca834618373a9c92c2a716e75eee517652f2d0580dd44750bb47ecb8c1e906187bdb596c146d4d7abed149a0a00f4f1731a9622a7d2e073010a0f633b7a7480ed6ec7d1b7b139f070ded0837c6d37ad8fe5463defe0cf800861a66f5ac1d413a5bcfd5e1de2f3c5234a90a68e35f6ee818bd00ddeb7307b3a1718f5e00300030000019fb36a0e130000019fb36a0e130000019fb36a0e130302010202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d020002b9837eb3daad78ac21fe0efc2f250c776701833053372cf63040a71ba38b31f5ce52f2fc73352702cb468590f857a43ab8ed70b4e830b507fbb2c13c4a80048d0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d030003b704e1ea9fc60a232e074795114047c1dddcc1e42bca8c8e61461e8b3614d0bf47533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dcd1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f2d52863c0d2fef772e2dd2db975fa00986047865cef82f5ced76c907ef2edb8d030003a0535beef1cc2473acd7ef41c29b03c3dead7169b471ee90c9f62c6c999b5f7347533b91470b6d0b2b3ffeda08f4498aee53b62fe204364bd25f40da6b0755dcd1b5e17110cbd21d84a0a21d5439af2ce3a1456edc6d1be2cb67d2c385f53a9f0000037124d9c08136196c87e10bef688e57cac3ee16aeee339bc8afd7dfe19b0716650c520d8efe571b4f4a606ef06e6fb0e3fdf5034ada7c6306515dd138671632604554a802828fef4e1acf863320c7c245e498b09abfbc77573977169524f138ff", "timestamps": [ - 1785340632712, - 1785340632713, - 1785340632713 + 1785421499923, + 1785421499923, + 1785421499923 ], "tree_size": 7, "version": 0 @@ -617,13 +617,13 @@ "name": "fixed-version-first", "input": { "entry_timestamps": [ - 1785340632714, - 1785340632714, - 1785340632714, - 1785340632715, - 1785340632715, - 1785340632715, - 1785340632715 + 1785421499924, + 1785421499924, + 1785421499924, + 1785421499925, + 1785421499925, + 1785421499925, + 1785421499925 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -703,24 +703,24 @@ "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "13e44c320985aa35f6c3418694b90b375f5e3cb635daa7eaf121488ad3704351", + "commitment": "6bbb48d38185afac5091f9562388bac093721769b07525e92914d89e0b78ed1b", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" } ], - "full_tree_head": "020000000000000007004024db298706f32a083a0cf8661d37031dba0e29facc36f22f6fb92af567b6266d835cd5b42fd2bbceb7fd32fe42e106c9ca0b3e53425c2bfe978ce15978d9ae08", + "full_tree_head": "0200000000000000070040650555bcf8111fa6bac1d3c9e957b49b647dc8602ca4a8e05e4c331025360edc64e4c697cc2a6eba4ba947db445f4973104d9e2f65eeb11a18d5e8332a091b03", "inclusion": [ - "9e9de5df2a753436897bd4979a65e48626a238e217d8d85bebc42bc5e46e77d5", - "5ffd669667b9a53b762adc0ee168e45621951690bbaf5e7de0ec700e2aea98f6" + "1ebab30028815e4388d7fe34cd818b02e2643ba82cf3976488e02f195b8198ad", + "3dbb6e1e9b0a4b040da8394adc3d9c972b9da88a9bde3ee06d815c43a3af99cb" ], - "opening": "4f7436097b408b849a2f5d6aea817178", + "opening": "a827b5c97e86401b3ae59396153293a6", "prefix_proofs": [ { "elements": [ - "f216fa69ab6b93d0e748290eb3750b001ccefc9dded8277e0e3f3ffd22dd4ba6", + "3fad405e53a4c0b7f8463f0c9f3f72ba28b626ddf20b74580557f998e64bab78", "0000000000000000000000000000000000000000000000000000000000000000", - "90dc90af0c433f2b7dbe7e9a6e619d6ccdb4d888905b7e2c526177dba96f539d" + "511d05bffe0a163cb74657a63048b58520b9788440edf76e1404a2d426c0ff6b" ], - "encoding": "02010401020003f216fa69ab6b93d0e748290eb3750b001ccefc9dded8277e0e3f3ffd22dd4ba6000000000000000000000000000000000000000000000000000000000000000090dc90af0c433f2b7dbe7e9a6e619d6ccdb4d888905b7e2c526177dba96f539d", + "encoding": "020104010200033fad405e53a4c0b7f8463f0c9f3f72ba28b626ddf20b74580557f998e64bab780000000000000000000000000000000000000000000000000000000000000000511d05bffe0a163cb74657a63048b58520b9788440edf76e1404a2d426c0ff6b", "results": [ { "depth": 4, @@ -734,9 +734,9 @@ }, { "elements": [ - "36d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb64485" + "c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b170130" ], - "encoding": "0201020102000136d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb64485", + "encoding": "02010201020001c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b170130", "results": [ { "depth": 2, @@ -750,9 +750,9 @@ }, { "elements": [ - "36d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb64485" + "c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b170130" ], - "encoding": "02010102fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e37b23938253cd548c27b39c93fab34ebd71dfe18941e66d7c50ef05c93b8bb10801000136d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb64485", + "encoding": "02010102fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e307d7b9836c7a28578fa550006092318f0d5191528ea128a3ab352c28265507c0010001c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b170130", "results": [ { "depth": 1, @@ -761,7 +761,7 @@ { "depth": 1, "leaf": { - "commitment": "7b23938253cd548c27b39c93fab34ebd71dfe18941e66d7c50ef05c93b8bb108", + "commitment": "07d7b9836c7a28578fa550006092318f0d5191528ea128a3ab352c28265507c0", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -770,16 +770,16 @@ } ], "prefix_roots": [ - "a68c663c7d945ae9f824cbfdcd4d67ef90f4bd4e9c0856cfae4b03bd7b344a4f", - "030e9081b11a772a791303f333998d73d8a2ced522ee230780f738211381b425" + "c864f7e748306e1deb40dd989e30eee91c3425e0c7c28c9e7ef8968c71c582ad", + "bed707cbe11319c68752c2a497de9442dd6c858cf54ead6681f382bbf7c14884" ], - "response": "020000000000000007004024db298706f32a083a0cf8661d37031dba0e29facc36f22f6fb92af567b6266d835cd5b42fd2bbceb7fd32fe42e106c9ca0b3e53425c2bfe978ce15978d9ae084f7436097b408b849a2f5d6aea81717800000007616c6963652d3102f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0000414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf020113e44c320985aa35f6c3418694b90b375f5e3cb635daa7eaf121488ad3704351050000019fae981e8b0000019fae981e8b0000019fae981e8b0000019fae981e8a0000019fae981e8a0302010401020003f216fa69ab6b93d0e748290eb3750b001ccefc9dded8277e0e3f3ffd22dd4ba6000000000000000000000000000000000000000000000000000000000000000090dc90af0c433f2b7dbe7e9a6e619d6ccdb4d888905b7e2c526177dba96f539d0201020102000136d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb6448502010102fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e37b23938253cd548c27b39c93fab34ebd71dfe18941e66d7c50ef05c93b8bb10801000136d535a3ec723f0ab87ca0d4911583cbc96f288745e73d7ee6bebdb43cb6448502a68c663c7d945ae9f824cbfdcd4d67ef90f4bd4e9c0856cfae4b03bd7b344a4f030e9081b11a772a791303f333998d73d8a2ced522ee230780f738211381b42500029e9de5df2a753436897bd4979a65e48626a238e217d8d85bebc42bc5e46e77d55ffd669667b9a53b762adc0ee168e45621951690bbaf5e7de0ec700e2aea98f6", + "response": "0200000000000000070040650555bcf8111fa6bac1d3c9e957b49b647dc8602ca4a8e05e4c331025360edc64e4c697cc2a6eba4ba947db445f4973104d9e2f65eeb11a18d5e8332a091b03a827b5c97e86401b3ae59396153293a600000007616c6963652d3102f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0000414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02016bbb48d38185afac5091f9562388bac093721769b07525e92914d89e0b78ed1b050000019fb36a0e150000019fb36a0e150000019fb36a0e150000019fb36a0e140000019fb36a0e1403020104010200033fad405e53a4c0b7f8463f0c9f3f72ba28b626ddf20b74580557f998e64bab780000000000000000000000000000000000000000000000000000000000000000511d05bffe0a163cb74657a63048b58520b9788440edf76e1404a2d426c0ff6b02010201020001c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b17013002010102fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e307d7b9836c7a28578fa550006092318f0d5191528ea128a3ab352c28265507c0010001c2feca69ef3c9073f85dbc2e4aebe177cd8398aa990c3eac9742519f7b17013002c864f7e748306e1deb40dd989e30eee91c3425e0c7c28c9e7ef8968c71c582adbed707cbe11319c68752c2a497de9442dd6c858cf54ead6681f382bbf7c1488400021ebab30028815e4388d7fe34cd818b02e2643ba82cf3976488e02f195b8198ad3dbb6e1e9b0a4b040da8394adc3d9c972b9da88a9bde3ee06d815c43a3af99cb", "timestamps": [ - 1785340632715, - 1785340632715, - 1785340632715, - 1785340632714, - 1785340632714 + 1785421499925, + 1785421499925, + 1785421499925, + 1785421499924, + 1785421499924 ], "tree_size": 7 } @@ -788,13 +788,13 @@ "name": "fixed-version-middle", "input": { "entry_timestamps": [ - 1785340632716, - 1785340632716, - 1785340632716, - 1785340632716, - 1785340632717, - 1785340632717, - 1785340632717 + 1785421499926, + 1785421499926, + 1785421499926, + 1785421499927, + 1785421499927, + 1785421499927, + 1785421499927 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -871,11 +871,11 @@ "expect": { "binary_ladder": [ { - "commitment": "4c59fd082a26ec9f6eedee964f5a38a2626256ffb2e093df05aad113d375dd12", + "commitment": "053ce18fccf2c11327b32204d66cb2718a0de2ccfa3d587467eb0e22ac9d80bc", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e171", + "commitment": "3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { @@ -891,20 +891,20 @@ "proof": "901738f65e8b0d720acdb242304a8725f8ca1acbb009fbd89c72016887f57fd6d75db48330615e002cec7257a9620a57bd2679cafe860c43c92d86e7751ff5fa38ff87f334f665233fb88fe5d60f240e" } ], - "full_tree_head": "02000000000000000700404e5dd09bc00ce528e7dc8e66dd2ec8fb43b66e9e6a3146ed7c2920f5d715be83cf1d9abd48964d1ee710f220c6260525d5107d2bdaef6717714d1c24f44ca10f", + "full_tree_head": "020000000000000007004054129b05764ee102805db61a29187a3186adb9b5f9a8183e9d8e6d219e372cb124a930971582ead4e30ed099d4892ccc2b8923237f8a561f9cce558ad0026403", "inclusion": [ - "496372721184e7fbce33d4638a3268d2dc22356e36791d0776edfe8dfb318467", - "ad45053068efb805e23d892ff264b556c360d759ef61faf77f1f8433f9144901", - "d25dc3ed16d4196a07ab73ae4d122f9a088f2a343ac68713e2efdcbde0c48887" + "8fcc70a9695a264fb9f17888972cbb26b35a68adb9c0450324cf8e0e7fc83d0d", + "0c24c147923bb5f1d5a972ebc5191540fc949f71821f5dec7b029383fb1c9ad3", + "89f8a702672738e2be1e9187472a9ff2a444f61c08eae7bf2ef59f93af1065b2" ], - "opening": "328b0759f9e4ae158bd572cf767a405d", + "opening": "2db4c56f96de2d61a1a23e79be7abe22", "prefix_proofs": [ { "elements": [ - "9868bf3b8ae871d3861cee518ad188795ce5c7e6f5bac1212197b2ff014be9ae", + "3727d2dedad8917b951d55370932fad4e4b41e4807be8ce1be073b3b9813352f", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e1710202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e1710202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e34c59fd082a26ec9f6eedee964f5a38a2626256ffb2e093df05aad113d375dd120400029868bf3b8ae871d3861cee518ad188795ce5c7e6f5bac1212197b2ff014be9ae0000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3053ce18fccf2c11327b32204d66cb2718a0de2ccfa3d587467eb0e22ac9d80bc0400023727d2dedad8917b951d55370932fad4e4b41e4807be8ce1be073b3b9813352f0000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -921,7 +921,7 @@ { "depth": 2, "leaf": { - "commitment": "8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e171", + "commitment": "3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -929,7 +929,7 @@ { "depth": 2, "leaf": { - "commitment": "8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e171", + "commitment": "3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -937,7 +937,7 @@ { "depth": 4, "leaf": { - "commitment": "4c59fd082a26ec9f6eedee964f5a38a2626256ffb2e093df05aad113d375dd12", + "commitment": "053ce18fccf2c11327b32204d66cb2718a0de2ccfa3d587467eb0e22ac9d80bc", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -946,14 +946,14 @@ } ], "prefix_roots": [ - "d2fec9a496af3eeaee0c2661d178487d92d4f17b4bbe87b3948457a75f1c6b46", - "1a814272cea7f51319f3fe9246cb11b4dfec9eaeb74789d7eef42ac929f2955a" + "515f3256845ba955d7077927a27fe3c712244c39d9836006b250395eed134474", + "a5208d9874a208683f792562eef7c079b345b716bc38c9232cf2044aede96a8f" ], - "response": "02000000000000000700404e5dd09bc00ce528e7dc8e66dd2ec8fb43b66e9e6a3146ed7c2920f5d715be83cf1d9abd48964d1ee710f220c6260525d5107d2bdaef6717714d1c24f44ca10f328b0759f9e4ae158bd572cf767a405d00000007616c6963652d3406f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00014c59fd082a26ec9f6eedee964f5a38a2626256ffb2e093df05aad113d375dd12414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02018cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e171783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c0026fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60300901738f65e8b0d720acdb242304a8725f8ca1acbb009fbd89c72016887f57fd6d75db48330615e002cec7257a9620a57bd2679cafe860c43c92d86e7751ff5fa38ff87f334f665233fb88fe5d60f240e00030000019fae981e8c0000019fae981e8d0000019fae981e8d010601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e1710202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f8cea2c0a7a795ab285859421199b372aa9ae1072d79c85ab8c5a60052b49e1710202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e34c59fd082a26ec9f6eedee964f5a38a2626256ffb2e093df05aad113d375dd120400029868bf3b8ae871d3861cee518ad188795ce5c7e6f5bac1212197b2ff014be9ae000000000000000000000000000000000000000000000000000000000000000002d2fec9a496af3eeaee0c2661d178487d92d4f17b4bbe87b3948457a75f1c6b461a814272cea7f51319f3fe9246cb11b4dfec9eaeb74789d7eef42ac929f2955a0003496372721184e7fbce33d4638a3268d2dc22356e36791d0776edfe8dfb318467ad45053068efb805e23d892ff264b556c360d759ef61faf77f1f8433f9144901d25dc3ed16d4196a07ab73ae4d122f9a088f2a343ac68713e2efdcbde0c48887", + "response": "020000000000000007004054129b05764ee102805db61a29187a3186adb9b5f9a8183e9d8e6d219e372cb124a930971582ead4e30ed099d4892ccc2b8923237f8a561f9cce558ad00264032db4c56f96de2d61a1a23e79be7abe2200000007616c6963652d3406f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001053ce18fccf2c11327b32204d66cb2718a0de2ccfa3d587467eb0e22ac9d80bc414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02013b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c0026fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60300901738f65e8b0d720acdb242304a8725f8ca1acbb009fbd89c72016887f57fd6d75db48330615e002cec7257a9620a57bd2679cafe860c43c92d86e7751ff5fa38ff87f334f665233fb88fe5d60f240e00030000019fb36a0e170000019fb36a0e170000019fb36a0e17010601040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f3b5a72253ffc766aee8ff9577cc2c6732a5f2c799cc130c9da77fa02e31c07ed0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3053ce18fccf2c11327b32204d66cb2718a0de2ccfa3d587467eb0e22ac9d80bc0400023727d2dedad8917b951d55370932fad4e4b41e4807be8ce1be073b3b9813352f000000000000000000000000000000000000000000000000000000000000000002515f3256845ba955d7077927a27fe3c712244c39d9836006b250395eed134474a5208d9874a208683f792562eef7c079b345b716bc38c9232cf2044aede96a8f00038fcc70a9695a264fb9f17888972cbb26b35a68adb9c0450324cf8e0e7fc83d0d0c24c147923bb5f1d5a972ebc5191540fc949f71821f5dec7b029383fb1c9ad389f8a702672738e2be1e9187472a9ff2a444f61c08eae7bf2ef59f93af1065b2", "timestamps": [ - 1785340632716, - 1785340632717, - 1785340632717 + 1785421499927, + 1785421499927, + 1785421499927 ], "tree_size": 7 } @@ -962,13 +962,13 @@ "name": "fixed-version-greatest", "input": { "entry_timestamps": [ - 1785340632718, - 1785340632719, - 1785340632719, - 1785340632719, - 1785340632719, - 1785340632720, - 1785340632720 + 1785421499928, + 1785421499929, + 1785421499929, + 1785421499929, + 1785421499929, + 1785421499929, + 1785421499930 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -1045,42 +1045,42 @@ "expect": { "binary_ladder": [ { - "commitment": "20f9aa37a5bcbcd927a91118e5af17454705cb2087e04d16e604e7a83d77a8b5", + "commitment": "a29d706b706dad45a313eb96595ac142b54d699b6dd5fbe413a9aaa28ff78a8e", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d", + "commitment": "a4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "870e864ca6382c36787f6edc97cf73695d15f7699abcaa0a6f44755b5b42a551", + "commitment": "1fb779a6760b66a708f5ef9498ab9ead9b2bbbae821cc204b471d9f5646779e5", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { "proof": "26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e123409" }, { - "commitment": "df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b583134", + "commitment": "5fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced", "proof": "e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f603" }, { "proof": "79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a" } ], - "full_tree_head": "0200000000000000070040ee9c9e0348ccae6464b4986e82c4fbe0f257a14964419713f9533a8f6c8ed2a9d70e2a86bb38c7ebca3750b2e06436e2090069ecbc5cd0720659f3ed4afba80a", + "full_tree_head": "020000000000000007004076401f2d9f0201beb3acea208f98c2e4b2516b242d35020731cfd70e7f14a7a54f1527bc74d000bd1102bb80e9391697cecd17148f9992ba071bfb5ed857aa03", "inclusion": [ - "e6f9fbe9181ae40278325145abd0486352f683e89b6bbbfb3cdf612a516b7beb", - "53a7ea9db99be34e1d4f2fcf5a563cb49618e42198827b9d5fc797a4d963e1e2", - "f38113ccf03a24786fbe5c659f89d84aa9f28a6fa6902814ad19d6b6da212dcb" + "1e4f4e3d36305e88622656d01abeafdad106718e6a0d3dca598b820a9a015b73", + "0d42e105a840a5b666accd691fbf3cba60b0c46d609aa5c60f92a6a071a91bfa", + "3012b5ea4a192ba39034cca7579cbb0fe3bf3982808f155a5d9dadf9e623c050" ], - "opening": "13f341998f0b008108caac0a9df8e44e", + "opening": "4f0ffcb9a4db09438938a27ff14bb732", "prefix_proofs": [ { "elements": [ - "1b499227429935bc01f5ae90137cb108db90304517c76a07114fe5d9de250fbd", + "3410ad26bc024e22bc77ee9a698609a7bc5d73535642f3e47bd68293d4b8a518", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d0200021b499227429935bc01f5ae90137cb108db90304517c76a07114fe5d9de250fbd0000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fa4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fa4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed0200023410ad26bc024e22bc77ee9a698609a7bc5d73535642f3e47bd68293d4b8a5180000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -1097,7 +1097,7 @@ { "depth": 2, "leaf": { - "commitment": "4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d", + "commitment": "a4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1105,7 +1105,7 @@ { "depth": 2, "leaf": { - "commitment": "4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d", + "commitment": "a4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1114,16 +1114,16 @@ }, { "elements": [ - "6a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065f", - "c4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafe", - "d838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e791" + "6e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e3", + "4f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba", + "057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa" ], - "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b5831340301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45672020d609903470f1075e11f6495d6f7d69051721a6638bf50a2b3f1c655b860200036a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065fc4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafed838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e791", + "encoding": "03028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba85fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e458f61b81a38902502da625270ee8647d8094fad18e5671cc012c0fbc6b2213d780200036e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e34f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa", "results": [ { "depth": 3, "leaf": { - "commitment": "df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b583134", + "commitment": "5fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1135,7 +1135,7 @@ { "depth": 2, "leaf": { - "commitment": "672020d609903470f1075e11f6495d6f7d69051721a6638bf50a2b3f1c655b86", + "commitment": "8f61b81a38902502da625270ee8647d8094fad18e5671cc012c0fbc6b2213d78", "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, "result_type": 2 @@ -1144,18 +1144,18 @@ }, { "elements": [ - "6a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065f", - "c87b8f0efed7e3f90e56903423136c0de5474c9a78cddfeae3156cb09f3d8dce", + "6e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e3", + "984d286b530f1ff36e8636b43a85f879178b325394432f00ee871fed5f7bbeb1", "0000000000000000000000000000000000000000000000000000000000000000", - "c4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafe", - "d838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e791" + "4f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba", + "057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa" ], - "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b58313403010400056a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065fc87b8f0efed7e3f90e56903423136c0de5474c9a78cddfeae3156cb09f3d8dce0000000000000000000000000000000000000000000000000000000000000000c4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafed838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e791", + "encoding": "02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba85fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced03010400056e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e3984d286b530f1ff36e8636b43a85f879178b325394432f00ee871fed5f7bbeb100000000000000000000000000000000000000000000000000000000000000004f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa", "results": [ { "depth": 3, "leaf": { - "commitment": "df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b583134", + "commitment": "5fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1168,11 +1168,11 @@ } ], "prefix_roots": [], - "response": "0200000000000000070040ee9c9e0348ccae6464b4986e82c4fbe0f257a14964419713f9533a8f6c8ed2a9d70e2a86bb38c7ebca3750b2e06436e2090069ecbc5cd0720659f3ed4afba80a13f341998f0b008108caac0a9df8e44e00000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f000120f9aa37a5bcbcd927a91118e5af17454705cb2087e04d16e604e7a83d77a8b5414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02014f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01870e864ca6382c36787f6edc97cf73695d15f7699abcaa0a6f44755b5b42a55126fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b58313479f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fae981e8f0000019fae981e900000019fae981e90030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f4f77a9127d971eedc738e3a37a6f34d1e7d5382c1718a613d195ce17ef943e8d0200021b499227429935bc01f5ae90137cb108db90304517c76a07114fe5d9de250fbd000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b5831340301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45672020d609903470f1075e11f6495d6f7d69051721a6638bf50a2b3f1c655b860200036a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065fc4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafed838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e79102028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8df3fae312448b0813aaf909d86275b37c27b184f7e52dfbcc90fcb3e0b58313403010400056a40291a0f2cfbd1df0ec7c8f8222c4cfc55392a1fa1e9eb8b5b4e453916065fc87b8f0efed7e3f90e56903423136c0de5474c9a78cddfeae3156cb09f3d8dce0000000000000000000000000000000000000000000000000000000000000000c4fbb9f4c6ad7cb1eb5213cd8437741fe2699b1454ca280cd3d8a4025e04cafed838cdbc559f70891eff6b95c4039ddbdc4145242fc0591b44039070a734e791000003e6f9fbe9181ae40278325145abd0486352f683e89b6bbbfb3cdf612a516b7beb53a7ea9db99be34e1d4f2fcf5a563cb49618e42198827b9d5fc797a4d963e1e2f38113ccf03a24786fbe5c659f89d84aa9f28a6fa6902814ad19d6b6da212dcb", + "response": "020000000000000007004076401f2d9f0201beb3acea208f98c2e4b2516b242d35020731cfd70e7f14a7a54f1527bc74d000bd1102bb80e9391697cecd17148f9992ba071bfb5ed857aa034f0ffcb9a4db09438938a27ff14bb73200000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001a29d706b706dad45a313eb96595ac142b54d699b6dd5fbe413a9aaa28ff78a8e414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201a4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c011fb779a6760b66a708f5ef9498ab9ead9b2bbbae821cc204b471d9f5646779e526fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f603015fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fb36a0e190000019fb36a0e190000019fb36a0e1a030501040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fa4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed0202a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fa4bad8c406888a1429bdb0d3d340272feefbcc3fd668cf7fb1fa2430538a5aed0200023410ad26bc024e22bc77ee9a698609a7bc5d73535642f3e47bd68293d4b8a518000000000000000000000000000000000000000000000000000000000000000003028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba85fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e458f61b81a38902502da625270ee8647d8094fad18e5671cc012c0fbc6b2213d780200036e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e34f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa02028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba85fe39fc845622f8d64433655b4a3625f287d74bb43f04a4558e37a16c15dfced03010400056e868114870764bb6c29c59135c23481096cb92990d3983e992c4481000327e3984d286b530f1ff36e8636b43a85f879178b325394432f00ee871fed5f7bbeb100000000000000000000000000000000000000000000000000000000000000004f1a76f8d6598fb551b6bd93a7338ab1f7b86c158030069a12b4db3441a930ba057537a7619155220133342bbae5ae03cbf4ce97aa78b0e9cc18844e6a4c19aa0000031e4f4e3d36305e88622656d01abeafdad106718e6a0d3dca598b820a9a015b730d42e105a840a5b666accd691fbf3cba60b0c46d609aa5c60f92a6a071a91bfa3012b5ea4a192ba39034cca7579cbb0fe3bf3982808f155a5d9dadf9e623c050", "timestamps": [ - 1785340632719, - 1785340632720, - 1785340632720 + 1785421499929, + 1785421499929, + 1785421499930 ], "tree_size": 7 } @@ -1181,13 +1181,13 @@ "name": "fixed-version-with-advertised-size", "input": { "entry_timestamps": [ - 1785340632721, - 1785340632721, - 1785340632722, - 1785340632722, - 1785340632722, - 1785340632722, - 1785340632722 + 1785421499931, + 1785421499931, + 1785421499931, + 1785421499931, + 1785421499932, + 1785421499932, + 1785421499932 ], "label": "616c696365406578616d706c652e636f6d", "last": 4, @@ -1265,35 +1265,35 @@ "expect": { "binary_ladder": [ { - "commitment": "bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c", + "commitment": "6c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "ad25284924f30e7b624ccb288f407a57721f47a2e39023caac49c46e45364b8e", + "commitment": "01e297962de748a983308da6210945135f4e7b88bb152a2f9c92a960156fee0d", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "bbac2d8123f2c27d9974c5c5202da2e6fd90b557a7be1b0ebf687d99b0b69dbb", + "commitment": "8726eab01c4683e4fcd1d22154536de2b9afc63896195ce735d6032558872719", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { "proof": "4d493fd0ff6a928c26e821165294b8375d30556957c68899f5d44d8358cde8ffc491c2208f6d9e8dbcdf2b4005c8e7ff457d570474684ff317b6789806504feb346ad1667cac398558db901c11a15a00" } ], - "full_tree_head": "0200000000000000070040a9f397185d6cb3f7d6a7cef9c9db02279fe160d47df6f55af36e3dbf2aba93acaa7fd85e3414d566b5a78e42585422d164e5081e3b4e30ae998ba9186cd91202", + "full_tree_head": "0200000000000000070040e71121613b001b06d5068873385c29c9712249389f9c3cd943ff6e1234870f16e4ea4aedecd3bc2f541dbd79900ba5daf4a97a056fe2650d397f7d179cebf107", "inclusion": [ - "c8ae879d60fdf415f30d251366d237f0cf562233891eeb29b00b599836370e05", - "88f6b54f93d0959ac7e5acce3b899df8d9025c4040a26b72578cc811a89d6ece", - "73a17755c2d91be42499e9c3820089fb3f782db87a3decfa221eabf846e5f95b" + "0b641a1e230e4b7463b9a03942eb8690a7cdc63dd1ad658d80a0c3ea0e830eba", + "c454526c310cb4d9037c04491410494e01faefb733cf9604463309d0803b16aa", + "15dfda29d9abbacfa25de9582126266dd622f7b7f32b2da94fc663eac3fb27a8" ], - "opening": "d48f397e2b2b731562a2639701e1fedb", + "opening": "66b51ee44d11015c3295a8a120d958d5", "prefix_proofs": [ { "elements": [ - "75a81f410360427449243e9a0596cc1f3f0a619e8cf5ad2e7d1d0df2520ef028", + "e23b6af3908713c44a6afe76927fe93760660bf1e1356a6f8ee258bcaca6bbb2", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "03010401020104000275a81f410360427449243e9a0596cc1f3f0a619e8cf5ad2e7d1d0df2520ef0280000000000000000000000000000000000000000000000000000000000000000", + "encoding": "030104010201040002e23b6af3908713c44a6afe76927fe93760660bf1e1356a6f8ee258bcaca6bbb20000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -1311,7 +1311,7 @@ }, { "elements": [], - "encoding": "040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c02023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2e5eb4a011d8f6fccf9ceaa9477842f409b36c447bf47f9f926ed434905337b26010000", + "encoding": "040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e36c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b02023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee21867e91540cc3fe48030d888c812495ae3d9cba2e8ef806dddf0294595a863d6010000", "results": [ { "depth": 2, @@ -1324,7 +1324,7 @@ { "depth": 2, "leaf": { - "commitment": "bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c", + "commitment": "6c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -1332,7 +1332,7 @@ { "depth": 1, "leaf": { - "commitment": "e5eb4a011d8f6fccf9ceaa9477842f409b36c447bf47f9f926ed434905337b26", + "commitment": "1867e91540cc3fe48030d888c812495ae3d9cba2e8ef806dddf0294595a863d6", "vrf_output": "3ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2" }, "result_type": 2 @@ -1341,15 +1341,15 @@ }, { "elements": [ - "2b8106813339530a67319e46bfb7c8c253bdfd942faa1296c8aefa0d59583d11", - "edd5132cfb84d4b0ae8d94a3b9f1c579712e404d18ffd67385691433253fd990" + "eb37ff2e008778d1b53b1fce1d13473c56293bb90756219e0dd4d795e042f5d6", + "c7fa0cdaea567d1afa8c7fdb30c192cc6f2224176fed643fb06329417cc1f637" ], - "encoding": "0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c02010200022b8106813339530a67319e46bfb7c8c253bdfd942faa1296c8aefa0d59583d11edd5132cfb84d4b0ae8d94a3b9f1c579712e404d18ffd67385691433253fd990", + "encoding": "0202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e36c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b0201020002eb37ff2e008778d1b53b1fce1d13473c56293bb90756219e0dd4d795e042f5d6c7fa0cdaea567d1afa8c7fdb30c192cc6f2224176fed643fb06329417cc1f637", "results": [ { "depth": 2, "leaf": { - "commitment": "bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c", + "commitment": "6c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -1362,13 +1362,13 @@ } ], "prefix_roots": [ - "7e453f5f00c229e5a5e86987ffc7bc45facf9ecffe8ef8051b6cafacccf0c45f" + "45f07e0e01f4926c1245b983014c3653209a6dc421b98d9836f0845eed1c5d97" ], - "response": "0200000000000000070040a9f397185d6cb3f7d6a7cef9c9db02279fe160d47df6f55af36e3dbf2aba93acaa7fd85e3414d566b5a78e42585422d164e5081e3b4e30ae998ba9186cd91202d48f397e2b2b731562a2639701e1fedb00000007616c6963652d3304f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201ad25284924f30e7b624ccb288f407a57721f47a2e39023caac49c46e45364b8e783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01bbac2d8123f2c27d9974c5c5202da2e6fd90b557a7be1b0ebf687d99b0b69dbb4d493fd0ff6a928c26e821165294b8375d30556957c68899f5d44d8358cde8ffc491c2208f6d9e8dbcdf2b4005c8e7ff457d570474684ff317b6789806504feb346ad1667cac398558db901c11a15a0000030000019fae981e920000019fae981e910000019fae981e920303010401020104000275a81f410360427449243e9a0596cc1f3f0a619e8cf5ad2e7d1d0df2520ef0280000000000000000000000000000000000000000000000000000000000000000040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c02023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2e5eb4a011d8f6fccf9ceaa9477842f409b36c447bf47f9f926ed434905337b260100000202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3bd77f410a6122572d35aa0c48a78fad06e9be124f59c87e3940d5b9086f9eb4c02010200022b8106813339530a67319e46bfb7c8c253bdfd942faa1296c8aefa0d59583d11edd5132cfb84d4b0ae8d94a3b9f1c579712e404d18ffd67385691433253fd990017e453f5f00c229e5a5e86987ffc7bc45facf9ecffe8ef8051b6cafacccf0c45f0003c8ae879d60fdf415f30d251366d237f0cf562233891eeb29b00b599836370e0588f6b54f93d0959ac7e5acce3b899df8d9025c4040a26b72578cc811a89d6ece73a17755c2d91be42499e9c3820089fb3f782db87a3decfa221eabf846e5f95b", + "response": "0200000000000000070040e71121613b001b06d5068873385c29c9712249389f9c3cd943ff6e1234870f16e4ea4aedecd3bc2f541dbd79900ba5daf4a97a056fe2650d397f7d179cebf10766b51ee44d11015c3295a8a120d958d500000007616c6963652d3304f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00016c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf020101e297962de748a983308da6210945135f4e7b88bb152a2f9c92a960156fee0d783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c018726eab01c4683e4fcd1d22154536de2b9afc63896195ce735d60325588727194d493fd0ff6a928c26e821165294b8375d30556957c68899f5d44d8358cde8ffc491c2208f6d9e8dbcdf2b4005c8e7ff457d570474684ff317b6789806504feb346ad1667cac398558db901c11a15a0000030000019fb36a0e1c0000019fb36a0e1b0000019fb36a0e1b03030104010201040002e23b6af3908713c44a6afe76927fe93760660bf1e1356a6f8ee258bcaca6bbb20000000000000000000000000000000000000000000000000000000000000000040102010202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e36c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b02023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee21867e91540cc3fe48030d888c812495ae3d9cba2e8ef806dddf0294595a863d60100000202fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e36c3a9c22c89f9ec4c90ad2d2dc951ab28c4c5637837474259e13a1d878cbc08b0201020002eb37ff2e008778d1b53b1fce1d13473c56293bb90756219e0dd4d795e042f5d6c7fa0cdaea567d1afa8c7fdb30c192cc6f2224176fed643fb06329417cc1f6370145f07e0e01f4926c1245b983014c3653209a6dc421b98d9836f0845eed1c5d9700030b641a1e230e4b7463b9a03942eb8690a7cdc63dd1ad658d80a0c3ea0e830ebac454526c310cb4d9037c04491410494e01faefb733cf9604463309d0803b16aa15dfda29d9abbacfa25de9582126266dd622f7b7f32b2da94fc663eac3fb27a8", "timestamps": [ - 1785340632722, - 1785340632721, - 1785340632722 + 1785421499932, + 1785421499931, + 1785421499931 ], "tree_size": 7 } @@ -1377,7 +1377,7 @@ "name": "single-entry-log", "input": { "entry_timestamps": [ - 1785340632723 + 1785421499933 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -1407,13 +1407,13 @@ "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" } ], - "full_tree_head": "02000000000000000100403cca72769974723c6d2ef3bd8891c478341894590eaa81b9e095aeb19909d4285dee09ef00befb4632b72687e819c7e24e678dd7f49611d5a08f920c2c618105", + "full_tree_head": "02000000000000000100401b781c7e3b6776861219166a777e219942770d5a102bcd5f945bf30a66807feb76fb2fe12b77b506d6abe9f3db9ede5a0e3934a0470d86d26c27776f751c5204", "inclusion": [], - "opening": "fe22c92126a75a115e6b9a417a4db700", + "opening": "ba3eb6e858c888d9e3f0d8ff22f07de4", "prefix_proofs": [ { "elements": [], - "encoding": "02010002fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e367506e6b5a33f8b16316d7d16516fc3eeb0f57af37a1e06fc91f61004b4aadc8000000", + "encoding": "02010002fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e35b636dcf80c60093d1bf69850e2dafa615a01da55bb11d594820afe013a54291000000", "results": [ { "depth": 0, @@ -1422,7 +1422,7 @@ { "depth": 0, "leaf": { - "commitment": "67506e6b5a33f8b16316d7d16516fc3eeb0f57af37a1e06fc91f61004b4aadc8", + "commitment": "5b636dcf80c60093d1bf69850e2dafa615a01da55bb11d594820afe013a54291", "vrf_output": "fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e3" }, "result_type": 2 @@ -1431,9 +1431,9 @@ } ], "prefix_roots": [], - "response": "02000000000000000100403cca72769974723c6d2ef3bd8891c478341894590eaa81b9e095aeb19909d4285dee09ef00befb4632b72687e819c7e24e678dd7f49611d5a08f920c2c61810500000000fe22c92126a75a115e6b9a417a4db70000000007616c6963652d3102f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0000414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0200010000019fae981e930102010002fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e367506e6b5a33f8b16316d7d16516fc3eeb0f57af37a1e06fc91f61004b4aadc8000000000000", + "response": "02000000000000000100401b781c7e3b6776861219166a777e219942770d5a102bcd5f945bf30a66807feb76fb2fe12b77b506d6abe9f3db9ede5a0e3934a0470d86d26c27776f751c520400000000ba3eb6e858c888d9e3f0d8ff22f07de400000007616c6963652d3102f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0000414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0200010000019fb36a0e1d0102010002fd9ecf918e3b2a80ad06e2474e7a43de9578e3a10e0e4211d7c907e27179a1e35b636dcf80c60093d1bf69850e2dafa615a01da55bb11d594820afe013a54291000000000000", "timestamps": [ - 1785340632723 + 1785421499933 ], "tree_size": 1, "version": 0 @@ -1443,13 +1443,13 @@ "name": "label-does-not-exist", "input": { "entry_timestamps": [ - 1785340632724, - 1785340632724, - 1785340632724, - 1785340632725, - 1785340632725, - 1785340632725, - 1785340632725 + 1785421499933, + 1785421499934, + 1785421499934, + 1785421499934, + 1785421499934, + 1785421499934, + 1785421499935 ], "label": "6e6f626f6479406578616d706c652e636f6d", "max_ahead": 10000, @@ -1528,25 +1528,25 @@ "proof": "4b04fea1cc97b5612a9cf074b619ab597e884754382b924f15782338f5758f3ced2ac7fcc0b4bd528d2101f7046d3fadb9735b459544ac75f8bc1bca20b4ae2f7ddeef21639ba2ea2b195d2a3879ae0f" } ], - "full_tree_head": "0200000000000000070040f42695aa420874d99f30fd65a52bd2b06ffc96cde84cfc82549770ab9c73d446bcdfc869b8beaf6614d47f7211253e8548bb9fb01e020ef3c843274e296aa90d", + "full_tree_head": "0200000000000000070040f59e6bf1097aca680fa058734ea73d485c7f6736479734a8da0394dd1548ddde997389bb6b5a0104a54ee5a32a214bd906c3b0e65a898ed1a60c45f06e690e00", "inclusion": [ - "1a201592668fe47683262583eb59b79567407e61e4c535162ec3b7b671097773", - "0f2818b81c5f38c22b52b1613d781d17a92886ac0c07979ae36c2c43db7e83ca", - "e8984fd41772627050d716966a193540ba283256d4be74be7621a97a3b182f48" + "29c134db2a33008bf89b67615fcf83440dceaa06e939accb4e0f6112320e0149", + "9348f5a49e259cdf682126071a03a079bf8de825f4448d6fa49f25cf67e6b108", + "81f3cd345125b1d5a10073ce7135878645a6f189ecb84e767c3639ae15094d20" ], "opening": "00000000000000000000000000000000", "prefix_proofs": [ { "elements": [ - "6e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c9", - "18963ce0d787ade654ccd61a1596b9a2bef8fec435ff0e41e309e8dde1769ebd" + "5ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a5573", + "412147cbe85150828a6f85dec172d270a00adb414db77a75707edada6e94deaa" ], - "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb80200026e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c918963ce0d787ade654ccd61a1596b9a2bef8fec435ff0e41e309e8dde1769ebd", + "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee530200025ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a5573412147cbe85150828a6f85dec172d270a00adb414db77a75707edada6e94deaa", "results": [ { "depth": 2, "leaf": { - "commitment": "64228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb8", + "commitment": "37aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee53", "vrf_output": "3ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2" }, "result_type": 2 @@ -1555,15 +1555,15 @@ }, { "elements": [ - "6e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c9", - "685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef" + "5ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a5573", + "4e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e" ], - "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb80200026e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c9685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef", + "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee530200025ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a55734e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e", "results": [ { "depth": 2, "leaf": { - "commitment": "64228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb8", + "commitment": "37aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee53", "vrf_output": "3ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2" }, "result_type": 2 @@ -1572,15 +1572,15 @@ }, { "elements": [ - "bf5eb7397f87667fdf288ed756120404984bce08e47bece37ed3b778fa375364", - "685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef" + "d664416d1270e45bc1b82a7ee626c70212d8174881f6dde994fa0367b45b2068", + "4e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e" ], - "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb8020002bf5eb7397f87667fdf288ed756120404984bce08e47bece37ed3b778fa375364685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef", + "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee53020002d664416d1270e45bc1b82a7ee626c70212d8174881f6dde994fa0367b45b20684e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e", "results": [ { "depth": 2, "leaf": { - "commitment": "64228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb8", + "commitment": "37aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee53", "vrf_output": "3ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2" }, "result_type": 2 @@ -1589,11 +1589,11 @@ } ], "prefix_roots": [], - "response": "0200000000000000070040f42695aa420874d99f30fd65a52bd2b06ffc96cde84cfc82549770ab9c73d446bcdfc869b8beaf6614d47f7211253e8548bb9fb01e020ef3c843274e296aa90d000000000000000000000000000000000000000000000000014b04fea1cc97b5612a9cf074b619ab597e884754382b924f15782338f5758f3ced2ac7fcc0b4bd528d2101f7046d3fadb9735b459544ac75f8bc1bca20b4ae2f7ddeef21639ba2ea2b195d2a3879ae0f00030000019fae981e950000019fae981e950000019fae981e950301023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb80200026e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c918963ce0d787ade654ccd61a1596b9a2bef8fec435ff0e41e309e8dde1769ebd01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb80200026e83a05a1aaf77d561a73545e3de83e40052d95db0d0449908a4687a052996c9685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee264228a9aa773a3be29e1cf4345163ceefeda39e1c7ed8f0d2dfca06042d1adb8020002bf5eb7397f87667fdf288ed756120404984bce08e47bece37ed3b778fa375364685fcbf93f1538181baaa66191677e4efbaff9c33207252c81ad90ab188f1fef0000031a201592668fe47683262583eb59b79567407e61e4c535162ec3b7b6710977730f2818b81c5f38c22b52b1613d781d17a92886ac0c07979ae36c2c43db7e83cae8984fd41772627050d716966a193540ba283256d4be74be7621a97a3b182f48", + "response": "0200000000000000070040f59e6bf1097aca680fa058734ea73d485c7f6736479734a8da0394dd1548ddde997389bb6b5a0104a54ee5a32a214bd906c3b0e65a898ed1a60c45f06e690e00000000000000000000000000000000000000000000000000014b04fea1cc97b5612a9cf074b619ab597e884754382b924f15782338f5758f3ced2ac7fcc0b4bd528d2101f7046d3fadb9735b459544ac75f8bc1bca20b4ae2f7ddeef21639ba2ea2b195d2a3879ae0f00030000019fb36a0e1e0000019fb36a0e1e0000019fb36a0e1f0301023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee530200025ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a5573412147cbe85150828a6f85dec172d270a00adb414db77a75707edada6e94deaa01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee530200025ff0503db7918aaba3bf77df71fda1fd6c7ae5f4e399adf3ef28b7454d3a55734e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee237aacefbfd5fd67e764051f960a670564bb90bf27abc5986fb1be9a65f9fee53020002d664416d1270e45bc1b82a7ee626c70212d8174881f6dde994fa0367b45b20684e5b466713d5ad51789ba2ca0652737073279410684eb8b4131b4d352159f29e00000329c134db2a33008bf89b67615fcf83440dceaa06e939accb4e0f6112320e01499348f5a49e259cdf682126071a03a079bf8de825f4448d6fa49f25cf67e6b10881f3cd345125b1d5a10073ce7135878645a6f189ecb84e767c3639ae15094d20", "timestamps": [ - 1785340632725, - 1785340632725, - 1785340632725 + 1785421499934, + 1785421499934, + 1785421499935 ], "tree_size": 7, "version": 0 @@ -1603,13 +1603,13 @@ "name": "fixed-version-above-the-greatest", "input": { "entry_timestamps": [ - 1785340632725, - 1785340632726, - 1785340632726, - 1785340632726, - 1785340632726, - 1785340632727, - 1785340632727 + 1785421499935, + 1785421499935, + 1785421499936, + 1785421499936, + 1785421499936, + 1785421499936, + 1785421499936 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -1686,15 +1686,15 @@ "expect": { "binary_ladder": [ { - "commitment": "153cf79a0fac56dbcc1b680effc5b8f2b5c9a49f4c7717190b5f99d4b70a1086", + "commitment": "57d7459bf8b02fb74c0fdc279efb9cff96b36ae282f35987e7a1b1c823fbf106", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "f7b7f3db1b851eea90ee8d19f1fd46187c033b3c56156505b03d36ce8a01ab43", + "commitment": "b36f7273808a84494fa91217674bcb36ad7b9eae136e6c90d1ac57a3c8fadd8e", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "1e6fa2073aeef59a5efa022a4b6345f55bcfd281876c918ee322d589da5f3a21", + "commitment": "76d4477caaac295e33267365b6653e4aa57827d615110d875e523696285c81af", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { @@ -1731,20 +1731,20 @@ "proof": "88d1e8277e88abf08a92eda5845d19af3f41bf8af907dfa823e26f5e1b2a5abf254e197a5f52cbaa4c183fbdbef46551aed90df5c61b7e17a31afef8fc2b84c876c0e5b49f07a87ff054a51854229e0b" } ], - "full_tree_head": "02000000000000000700407b74bc35bd2e878143e8ad9ec39075d0f3aaefe202e03963c8218e2385f2fce82b7898d853e7bc53d197b041958012a33309b78a8fa5693201cb2e7778b2c804", + "full_tree_head": "02000000000000000700404fe3bcb2779d8db300dd3af43c6988aa6aefa5e309db79bcad5ff3828c6b138d46095d8ad55ff591d553d8d6e51bfe59478ec930f7b4c72bcf2b53db1b19c900", "inclusion": [ - "552f308dbe58b4b364a0388f7983d77a2e69216e00b6dc486531f34bbc3bd9fe", - "97f8ab875a470b792ff484cbc823a1451a128ce91017836869f8c009cc3d69ad", - "9c4daea901db40c1c77058b7f471e536e0ed1d628b423498b379dd15238d8620" + "fb9648f2a5f018bef3493eb8cf987fc7d4410c7c08b5c55baccca160f49cfbbc", + "b7dfa3c62beb9dc3438552d4ef522cd231147e8690e6c427efdd82157c5382d8", + "7d025fc063764f4d45f7104b6c6985f9a83dc6b51c703db120590b72743f2173" ], "opening": "00000000000000000000000000000000", "prefix_proofs": [ { "elements": [ - "831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e6941", + "12c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca1", "0000000000000000000000000000000000000000000000000000000000000000" ], - "encoding": "0401040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7ff7b7f3db1b851eea90ee8d19f1fd46187c033b3c56156505b03d36ce8a01ab43020002831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e69410000000000000000000000000000000000000000000000000000000000000000", + "encoding": "0401040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb36f7273808a84494fa91217674bcb36ad7b9eae136e6c90d1ac57a3c8fadd8e02000212c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca10000000000000000000000000000000000000000000000000000000000000000", "results": [ { "depth": 4, @@ -1761,7 +1761,7 @@ { "depth": 2, "leaf": { - "commitment": "f7b7f3db1b851eea90ee8d19f1fd46187c033b3c56156505b03d36ce8a01ab43", + "commitment": "b36f7273808a84494fa91217674bcb36ad7b9eae136e6c90d1ac57a3c8fadd8e", "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" }, "result_type": 2 @@ -1770,16 +1770,16 @@ }, { "elements": [ - "831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e6941", - "0ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba37", - "36588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de35006" + "12c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca1", + "482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f43", + "5c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c45" ], - "encoding": "01028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9030003831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e69410ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba3736588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de35006", + "encoding": "01028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba82afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d03000312c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca1482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f435c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c45", "results": [ { "depth": 3, "leaf": { - "commitment": "a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9", + "commitment": "2afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1788,16 +1788,16 @@ }, { "elements": [ - "871d19e4ba8dab17f854b16ce9463dfe3c5e022d8910c0570c5806e057b118dd", - "0ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba37", - "36588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de35006" + "e7e6934aef1e2db4b7323c7a52d0e9c9f05e86405274bcc75f8d3815b4612f23", + "482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f43", + "5c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c45" ], - "encoding": "01028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9030003871d19e4ba8dab17f854b16ce9463dfe3c5e022d8910c0570c5806e057b118dd0ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba3736588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de35006", + "encoding": "01028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba82afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d030003e7e6934aef1e2db4b7323c7a52d0e9c9f05e86405274bcc75f8d3815b4612f23482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f435c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c45", "results": [ { "depth": 3, "leaf": { - "commitment": "a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9", + "commitment": "2afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1806,11 +1806,11 @@ } ], "prefix_roots": [], - "response": "02000000000000000700407b74bc35bd2e878143e8ad9ec39075d0f3aaefe202e03963c8218e2385f2fce82b7898d853e7bc53d197b041958012a33309b78a8fa5693201cb2e7778b2c80400000000000000000000000000000000000000000ef73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001153cf79a0fac56dbcc1b680effc5b8f2b5c9a49f4c7717190b5f99d4b70a1086414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201f7b7f3db1b851eea90ee8d19f1fd46187c033b3c56156505b03d36ce8a01ab43783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c011e6fa2073aeef59a5efa022a4b6345f55bcfd281876c918ee322d589da5f3a2126fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900cf636870256740d131679b75ea70fa702484e540aa0726388bb4230375e42257677d0ff5e3cf23b89a5d5586c9a94f5a76e0db1ddd7a0367eff6becff84e76eb5fe9a93ab52ab62f87ac9656045d6d0800117b66dd3181cf66d4befac10367af86f0aa7ab8871e4cf30b7d4fb668de0d37b9616e24cfd59c2c65545cbbd5443f979cab3be8cad670e7d447167c3d89b90cd083619aa8622495e051d303edb0530800f54a973311b6389a28d86c4c87b203878d2338a7f675393cfa34d618e5cc1a732c10fe6ef0f9729b4c70fc76ab7c1b88829c3e7b1ee5694deef66f5f3a8b5c7fefa7020fc2f24dd3c15b316481a1810a00abf32099de0caf8c5668ed21a3142cce52c18f3e0bb08bcdd29caaee99724b684a9f22c2eb1d96642b58ce4779e1ae0e0f79cc40ac238f7dfbf03e3237b7fd0b34cc646c00af357602492d3296159a0f0064c8e70f0274648ca71b1cb8f3c8ecd2b6ab069d88cc1f2e27e1091f3f44ac0c4ab15c83291054b13718c67ce8334c1141cda608de400e38fa83cb1cad519660d1fa658459baf1bc53c09d212f039f0100f3aff4b58e665ae2e852d5eff30b229f03f313215b252840241a29528db5ae16ed46cc997c4d6bb1d4f862603dfa5497a779b00177680b65e9e3e75a6f39dab809ec338a1e937972e333130daee8330000f639adb88a3956e7a2da5a8b3138822d2984d6fae67eb9f9739f985cd21f374436bb1026287a3fa88b240ab2114d68c37cc44e613b6e6dce4d611f054fac57704f34fdc7d859367c576d0b645edaee0700ea6abd52971f74dee0f79161f834db0f52ef756677f4e11693d5fe62c336ea88f6b0152a24ae6c185663dedad018c59b7260227fbc527878d4138ba0820b2e289bcc999b23b465610434118c76a0c00300666320da4627b99540fc48cb74a739121f3ec99a8d2a92db9ed4fd1c898a2a185dc3aad160be2c5eb836120f56d94f5153a80b5f46d428750cab730a0329ffd115daf0ff31c18fb551db05fe96064a070088d1e8277e88abf08a92eda5845d19af3f41bf8af907dfa823e26f5e1b2a5abf254e197a5f52cbaa4c183fbdbef46551aed90df5c61b7e17a31afef8fc2b84c876c0e5b49f07a87ff054a51854229e0b00030000019fae981e960000019fae981e970000019fae981e97030401040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7ff7b7f3db1b851eea90ee8d19f1fd46187c033b3c56156505b03d36ce8a01ab43020002831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e6941000000000000000000000000000000000000000000000000000000000000000001028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9030003831d5091f6464708f85273fdea5a012f5f6cf3d37c22d9623380711ead3e69410ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba3736588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de3500601028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a84aee5646ce5b359e27120588271609b039c2ab8a70582439ac88240bfb4de9030003871d19e4ba8dab17f854b16ce9463dfe3c5e022d8910c0570c5806e057b118dd0ff899d9e77687cd2371e4e4bee706a9cc9279924c33bca36151a667e46eba3736588434d671933f645f8d2203f8a9b40d110933284b603bea577b1f0de35006000003552f308dbe58b4b364a0388f7983d77a2e69216e00b6dc486531f34bbc3bd9fe97f8ab875a470b792ff484cbc823a1451a128ce91017836869f8c009cc3d69ad9c4daea901db40c1c77058b7f471e536e0ed1d628b423498b379dd15238d8620", + "response": "02000000000000000700404fe3bcb2779d8db300dd3af43c6988aa6aefa5e309db79bcad5ff3828c6b138d46095d8ad55ff591d553d8d6e51bfe59478ec930f7b4c72bcf2b53db1b19c90000000000000000000000000000000000000000000ef73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f000157d7459bf8b02fb74c0fdc279efb9cff96b36ae282f35987e7a1b1c823fbf106414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201b36f7273808a84494fa91217674bcb36ad7b9eae136e6c90d1ac57a3c8fadd8e783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c0176d4477caaac295e33267365b6653e4aa57827d615110d875e523696285c81af26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900cf636870256740d131679b75ea70fa702484e540aa0726388bb4230375e42257677d0ff5e3cf23b89a5d5586c9a94f5a76e0db1ddd7a0367eff6becff84e76eb5fe9a93ab52ab62f87ac9656045d6d0800117b66dd3181cf66d4befac10367af86f0aa7ab8871e4cf30b7d4fb668de0d37b9616e24cfd59c2c65545cbbd5443f979cab3be8cad670e7d447167c3d89b90cd083619aa8622495e051d303edb0530800f54a973311b6389a28d86c4c87b203878d2338a7f675393cfa34d618e5cc1a732c10fe6ef0f9729b4c70fc76ab7c1b88829c3e7b1ee5694deef66f5f3a8b5c7fefa7020fc2f24dd3c15b316481a1810a00abf32099de0caf8c5668ed21a3142cce52c18f3e0bb08bcdd29caaee99724b684a9f22c2eb1d96642b58ce4779e1ae0e0f79cc40ac238f7dfbf03e3237b7fd0b34cc646c00af357602492d3296159a0f0064c8e70f0274648ca71b1cb8f3c8ecd2b6ab069d88cc1f2e27e1091f3f44ac0c4ab15c83291054b13718c67ce8334c1141cda608de400e38fa83cb1cad519660d1fa658459baf1bc53c09d212f039f0100f3aff4b58e665ae2e852d5eff30b229f03f313215b252840241a29528db5ae16ed46cc997c4d6bb1d4f862603dfa5497a779b00177680b65e9e3e75a6f39dab809ec338a1e937972e333130daee8330000f639adb88a3956e7a2da5a8b3138822d2984d6fae67eb9f9739f985cd21f374436bb1026287a3fa88b240ab2114d68c37cc44e613b6e6dce4d611f054fac57704f34fdc7d859367c576d0b645edaee0700ea6abd52971f74dee0f79161f834db0f52ef756677f4e11693d5fe62c336ea88f6b0152a24ae6c185663dedad018c59b7260227fbc527878d4138ba0820b2e289bcc999b23b465610434118c76a0c00300666320da4627b99540fc48cb74a739121f3ec99a8d2a92db9ed4fd1c898a2a185dc3aad160be2c5eb836120f56d94f5153a80b5f46d428750cab730a0329ffd115daf0ff31c18fb551db05fe96064a070088d1e8277e88abf08a92eda5845d19af3f41bf8af907dfa823e26f5e1b2a5abf254e197a5f52cbaa4c183fbdbef46551aed90df5c61b7e17a31afef8fc2b84c876c0e5b49f07a87ff054a51854229e0b00030000019fb36a0e200000019fb36a0e200000019fb36a0e20030401040102010402a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7fb36f7273808a84494fa91217674bcb36ad7b9eae136e6c90d1ac57a3c8fadd8e02000212c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca1000000000000000000000000000000000000000000000000000000000000000001028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba82afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d03000312c6a658da8b54e25bcef8d7039c50b93f67840cf951250153506af0eb839ca1482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f435c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c4501028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba82afc2cc68ea4cad0cf5774f1f14e19edccda09e511aa9f932f7c5034462fbb7d030003e7e6934aef1e2db4b7323c7a52d0e9c9f05e86405274bcc75f8d3815b4612f23482c93c26a12d9f1b3752f66434846fbcdc4929acfe68e8f19c91418d5e00f435c75cad38a5d55da0fa46abeb8ae37a4bc9a2325a8765f3a77ff1585af148c45000003fb9648f2a5f018bef3493eb8cf987fc7d4410c7c08b5c55baccca160f49cfbbcb7dfa3c62beb9dc3438552d4ef522cd231147e8690e6c427efdd82157c5382d87d025fc063764f4d45f7104b6c6985f9a83dc6b51c703db120590b72743f2173", "timestamps": [ - 1785340632726, - 1785340632727, - 1785340632727 + 1785421499936, + 1785421499936, + 1785421499936 ], "tree_size": 7 } @@ -1819,13 +1819,13 @@ "name": "fixed-version-with-expired-entries", "input": { "entry_timestamps": [ - 1785340632729, - 1785340632831, - 1785340632932, - 1785340633034, - 1785340633135, - 1785340633238, - 1785340633340 + 1785421499939, + 1785421500040, + 1785421500140, + 1785421500242, + 1785421500343, + 1785421500444, + 1785421500546 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -1902,15 +1902,15 @@ "expect": { "binary_ladder": [ { - "commitment": "25acf037bc760217523198cebdf2f4083d2bd6643cf4560c94fb714adb503d8f", + "commitment": "b3333f78d504cf686b3524f63872e928390b278bbfc37052ef6c16fc806bbfa4", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "fb03094a7f9144c8fd8606644a7c32e5e72767405f7274d36e224c070e9c7d0b", + "commitment": "35b620cd962f149ccbf545e4ef40bdf1a448c5252a63e803b8c5b43f3d0c383d", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "feda6620ba14359973e8b88e96d356b88e170ac2b410d414eb53b40ae06dfb55", + "commitment": "625d9fcbc4e2a68c0b0ec6d4a5ff88975ca50cfc949511ad0491b62799fc15cc", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { @@ -1923,22 +1923,22 @@ "proof": "79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a" } ], - "full_tree_head": "020000000000000007004013be33afd1dc39a2d2a68def31f61a1bf63192ccd9edda5212a95b2f9d6475b1842fd3cb9b83927ac7bccdd20f309694a3484a447fc53535aede9fa27e33620e", + "full_tree_head": "0200000000000000070040003a8a8778f8af030d6654d6ea403f3f49a4f5b3bb76ef5837ed18efb1ccd8acf4f189e690574c09c5b7eb0e3b5ffacfe0131be3ad2a335eb7cdef6d9574900e", "inclusion": [ - "ccd66dfaae98c01943217badab667ed84a8006c739b21c1834773dd2c95fd386", - "10190ad0064e5f6ce44fb846b5bb944572802d7d55e141222a349d6ddf406369", - "91cd716e56f47776f550df7ebfbd5a3c27cb4d57df2238b9d374ad81e0c21488" + "22c79611e933f85de71463b03332f4f74c894d4be8c3bff2c21c84017f5fdd71", + "7dce264d90b5d054945173227ed938b7266c02cd6005392be1457b134d8667df", + "ea384ba6cdbed264d30d64f2673a7237243454bdc0adaa81f5abf71d1ec8c3e4" ], - "opening": "d3559f404103d6d0d139d3d63964d561", + "opening": "b44c01518e2bfc3ac2b5cbaf82f8aaf0", "prefix_proofs": [ { "elements": [ - "00239a471b2aaae858e64983a0a2da026e7ea5b6bfa16dbe55b11602589cb435", + "c9ecb283547022d8820d8263d8215d4cf1423b1c4b0b09ee4a90af6017dd07f3", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", - "56e092261cf66cd840158caceac0269efa1307fde3a758c804109fc5538456a4" + "dddc1150628b70157a8f76dbe7e302e672a7939e34422dfc7c8381b6d90b1e14" ], - "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba86db1332b444a0c9df42ec8c6d1842ffbfcc55f099c2d483f68e1724ceac926d60301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45c5b3acb82dbd06328467c960b0aae84a2a0f8e4d883767dd29460823fb037d3d02000400239a471b2aaae858e64983a0a2da026e7ea5b6bfa16dbe55b11602589cb4350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056e092261cf66cd840158caceac0269efa1307fde3a758c804109fc5538456a4", + "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8e7579a88e28b0b67da3ec8cfa69b09a7ee74d856731c46a26587c62f1b2cdcec0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e4539ad0753c18737d9153f1f2a899dd83227c5f6f872b8366a001bd792f1518461020004c9ecb283547022d8820d8263d8215d4cf1423b1c4b0b09ee4a90af6017dd07f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dddc1150628b70157a8f76dbe7e302e672a7939e34422dfc7c8381b6d90b1e14", "results": [ { "depth": 6, @@ -1955,7 +1955,7 @@ { "depth": 3, "leaf": { - "commitment": "6db1332b444a0c9df42ec8c6d1842ffbfcc55f099c2d483f68e1724ceac926d6", + "commitment": "e7579a88e28b0b67da3ec8cfa69b09a7ee74d856731c46a26587c62f1b2cdcec", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -1967,7 +1967,7 @@ { "depth": 2, "leaf": { - "commitment": "c5b3acb82dbd06328467c960b0aae84a2a0f8e4d883767dd29460823fb037d3d", + "commitment": "39ad0753c18737d9153f1f2a899dd83227c5f6f872b8366a001bd792f1518461", "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" }, "result_type": 2 @@ -1976,14 +1976,14 @@ } ], "prefix_roots": [ - "87ecaf645511aff4ad4c385f8bd15fdda050733150daf3ed63dbe4b1f4aa795c", - "823d1433a8d7371203a9ca996681a1452582607c561f0df3dd992a51fd093fb1" + "7b51e9df30dd6c4e5d6dfdfbb85c76201d2346ba2640e61af5fceba7f212aa8a", + "88dd1d6dc550e18abc80ba593faece12b42850171b61f851e922514b2848efe5" ], - "response": "020000000000000007004013be33afd1dc39a2d2a68def31f61a1bf63192ccd9edda5212a95b2f9d6475b1842fd3cb9b83927ac7bccdd20f309694a3484a447fc53535aede9fa27e33620ed3559f404103d6d0d139d3d63964d56100000007616c6963652d3606f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f000125acf037bc760217523198cebdf2f4083d2bd6643cf4560c94fb714adb503d8f414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf0201fb03094a7f9144c8fd8606644a7c32e5e72767405f7274d36e224c070e9c7d0b783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01feda6620ba14359973e8b88e96d356b88e170ac2b410d414eb53b40ae06dfb5526fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f6030079f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fae981fca0000019fae9820960000019fae9820fc0106010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba86db1332b444a0c9df42ec8c6d1842ffbfcc55f099c2d483f68e1724ceac926d60301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45c5b3acb82dbd06328467c960b0aae84a2a0f8e4d883767dd29460823fb037d3d02000400239a471b2aaae858e64983a0a2da026e7ea5b6bfa16dbe55b11602589cb4350000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000056e092261cf66cd840158caceac0269efa1307fde3a758c804109fc5538456a40287ecaf645511aff4ad4c385f8bd15fdda050733150daf3ed63dbe4b1f4aa795c823d1433a8d7371203a9ca996681a1452582607c561f0df3dd992a51fd093fb10003ccd66dfaae98c01943217badab667ed84a8006c739b21c1834773dd2c95fd38610190ad0064e5f6ce44fb846b5bb944572802d7d55e141222a349d6ddf40636991cd716e56f47776f550df7ebfbd5a3c27cb4d57df2238b9d374ad81e0c21488", + "response": "0200000000000000070040003a8a8778f8af030d6654d6ea403f3f49a4f5b3bb76ef5837ed18efb1ccd8acf4f189e690574c09c5b7eb0e3b5ffacfe0131be3ad2a335eb7cdef6d9574900eb44c01518e2bfc3ac2b5cbaf82f8aaf000000007616c6963652d3606f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001b3333f78d504cf686b3524f63872e928390b278bbfc37052ef6c16fc806bbfa4414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf020135b620cd962f149ccbf545e4ef40bdf1a448c5252a63e803b8c5b43f3d0c383d783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c01625d9fcbc4e2a68c0b0ec6d4a5ff88975ca50cfc949511ad0491b62799fc15cc26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f6030079f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fb36a0f520000019fb36a101c0000019fb36a10820106010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8e7579a88e28b0b67da3ec8cfa69b09a7ee74d856731c46a26587c62f1b2cdcec0301030249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e4539ad0753c18737d9153f1f2a899dd83227c5f6f872b8366a001bd792f1518461020004c9ecb283547022d8820d8263d8215d4cf1423b1c4b0b09ee4a90af6017dd07f300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000dddc1150628b70157a8f76dbe7e302e672a7939e34422dfc7c8381b6d90b1e14027b51e9df30dd6c4e5d6dfdfbb85c76201d2346ba2640e61af5fceba7f212aa8a88dd1d6dc550e18abc80ba593faece12b42850171b61f851e922514b2848efe5000322c79611e933f85de71463b03332f4f74c894d4be8c3bff2c21c84017f5fdd717dce264d90b5d054945173227ed938b7266c02cd6005392be1457b134d8667dfea384ba6cdbed264d30d64f2673a7237243454bdc0adaa81f5abf71d1ec8c3e4", "timestamps": [ - 1785340633034, - 1785340633238, - 1785340633340 + 1785421500242, + 1785421500444, + 1785421500546 ], "tree_size": 7 } @@ -1992,13 +1992,13 @@ "name": "fixed-version-expired-target", "input": { "entry_timestamps": [ - 1785340633346, - 1785340633447, - 1785340633548, - 1785340633649, - 1785340633751, - 1785340633853, - 1785340633955 + 1785421500548, + 1785421500649, + 1785421500751, + 1785421500852, + 1785421500954, + 1785421501055, + 1785421501157 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -2080,13 +2080,13 @@ "name": "greatest-version-with-expired-entries", "input": { "entry_timestamps": [ - 1785340633958, - 1785340634060, - 1785340634163, - 1785340634265, - 1785340634367, - 1785340634469, - 1785340634572 + 1785421501158, + 1785421501260, + 1785421501361, + 1785421501462, + 1785421501564, + 1785421501665, + 1785421501767 ], "label": "616c696365406578616d706c652e636f6d", "max_ahead": 10000, @@ -2162,46 +2162,46 @@ "expect": { "binary_ladder": [ { - "commitment": "e42e7a4b50fdb0f96d17d5e92ea94ff2ce1fc0e359c43b7153125c6898aedb51", + "commitment": "ac3dc794d894a2af5b4022741c034ecbc9d30345af44d4b81b393ae5b4fc5347", "proof": "f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f00" }, { - "commitment": "3564e579a1344ccdf00c3db139e3ce88996c395a6d7fdc357f4bf662923f6b2d", + "commitment": "9a70f05f6c110d87718749d218abe61d741609bd442bea1d32cad08ec7786785", "proof": "414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02" }, { - "commitment": "4c3bdecc94580254541e076a4997fdc48d5881b02bf28512f165a7eee7abcfc7", + "commitment": "9091c1499bd91845ffc2cf460f6129111f41ae66b96fdc08cd5fcacae8a9d63c", "proof": "783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c" }, { "proof": "26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e123409" }, { - "commitment": "f63f73bf6b9764276d593d106d51f132858ff58729288d467369edd7f97f6784", + "commitment": "a10b9e3431d41736b97329aecb79fbd1dfc8274f51feb0e191c77541bdaca5a2", "proof": "e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f603" }, { "proof": "79f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a" } ], - "full_tree_head": "0200000000000000070040dba3073a80586490e848e919dd672646da2e4e935bf999d22ff2bf193c325ddc767a9aba13f6150e907703c5e2736646f363c680af93878b1f894a9da32c9b0f", + "full_tree_head": "020000000000000007004092066fdb660a01dfa8161063c7db13b52a68d388f3c1cfbf18a33e4eac81466aecf23a9f7ba2d0107210aafc786e5495d384cdab2eef78f3f57f1838cd55b808", "inclusion": [ - "2fa95a841e871f6d2a7bc24d17f0ab1b32058330c81a43316ef032a8b5ed8686", - "3eded459d1250f7e4a8693498bb1f0f793de37df673c5af15b0472dfba57bf2e", - "d9c72316500a1d5f3b9f72df70d8e23f84d2f1a3b9781144819216cc69bb74a8" + "640bb8ffee74cdc03cf7a8dc56eb952a3143b2cd97f6f4bb374097b42c2fb5e7", + "c0ca7d871a7590e4ea6a9478a8cd665a77ec9b309a6c1ee774be218968f9e74c", + "47f05071522b1f5d558ffba5628654f65c5fe2dca4893d40090d4ad41e6ba7f1" ], - "opening": "7cb700bae7e6939a9acf99d090c220dd", + "opening": "a9e579f92e4cf27efa8676bf21f9bf62", "prefix_proofs": [ { "elements": [ - "698322aec3525b3346ab87fb8d8fd40b345c7b9381faff4999a9bca0f14b3556", - "8d7a106625e8dd911f7e9e937a48a4ebf7a8312887e77d8782e92c814b44c4b0", + "b7d14d0e61b4a6a2e9f2b87cc12934a43eb0414a802f633cc9c9ac1aea2642bd", + "e9552236b6837be95ee871867bd92ccf4adf17307293f2d5b150503846b1feca", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", - "5fe8408a764fbf05d5e14a92a0415b44b5024e3107c78a2b5157dc69625e0f9e" + "17880251da9e5393dc126d60e334a24fbb66470b65c67ba8dbf62ac32e4f2e3a" ], - "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f63f73bf6b9764276d593d106d51f132858ff58729288d467369edd7f97f678403010301040006698322aec3525b3346ab87fb8d8fd40b345c7b9381faff4999a9bca0f14b35568d7a106625e8dd911f7e9e937a48a4ebf7a8312887e77d8782e92c814b44c4b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fe8408a764fbf05d5e14a92a0415b44b5024e3107c78a2b5157dc69625e0f9e", + "encoding": "06010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a10b9e3431d41736b97329aecb79fbd1dfc8274f51feb0e191c77541bdaca5a203010301040006b7d14d0e61b4a6a2e9f2b87cc12934a43eb0414a802f633cc9c9ac1aea2642bde9552236b6837be95ee871867bd92ccf4adf17307293f2d5b150503846b1feca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017880251da9e5393dc126d60e334a24fbb66470b65c67ba8dbf62ac32e4f2e3a", "results": [ { "depth": 6, @@ -2218,7 +2218,7 @@ { "depth": 3, "leaf": { - "commitment": "f63f73bf6b9764276d593d106d51f132858ff58729288d467369edd7f97f6784", + "commitment": "a10b9e3431d41736b97329aecb79fbd1dfc8274f51feb0e191c77541bdaca5a2", "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" }, "result_type": 2 @@ -2235,14 +2235,14 @@ } ], "prefix_roots": [ - "ce4ca46898a68265964e8172bcd75531a954d9b00d523f7c83f81555bc06a76d", - "9b7f02cc8b5dd3260b8d7dc0793d55c5fe50f4f85c4637df97f5d6d29bfb99b1" + "5742d4e8a54686a65ec49661b769d311705edf230564fa5d2039c993961d83ad", + "c8626a80f920a5717461488bdc2ff96de261f8b066c4fd50280743ac0ca1b9dc" ], - "response": "0200000000000000070040dba3073a80586490e848e919dd672646da2e4e935bf999d22ff2bf193c325ddc767a9aba13f6150e907703c5e2736646f363c680af93878b1f894a9da32c9b0f000000067cb700bae7e6939a9acf99d090c220dd00000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001e42e7a4b50fdb0f96d17d5e92ea94ff2ce1fc0e359c43b7153125c6898aedb51414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02013564e579a1344ccdf00c3db139e3ce88996c395a6d7fdc357f4bf662923f6b2d783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c014c3bdecc94580254541e076a4997fdc48d5881b02bf28512f165a7eee7abcfc726fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301f63f73bf6b9764276d593d106d51f132858ff58729288d467369edd7f97f678479f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fae9824990000019fae9825650000019fae9825cc0106010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8f63f73bf6b9764276d593d106d51f132858ff58729288d467369edd7f97f678403010301040006698322aec3525b3346ab87fb8d8fd40b345c7b9381faff4999a9bca0f14b35568d7a106625e8dd911f7e9e937a48a4ebf7a8312887e77d8782e92c814b44c4b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000005fe8408a764fbf05d5e14a92a0415b44b5024e3107c78a2b5157dc69625e0f9e02ce4ca46898a68265964e8172bcd75531a954d9b00d523f7c83f81555bc06a76d9b7f02cc8b5dd3260b8d7dc0793d55c5fe50f4f85c4637df97f5d6d29bfb99b100032fa95a841e871f6d2a7bc24d17f0ab1b32058330c81a43316ef032a8b5ed86863eded459d1250f7e4a8693498bb1f0f793de37df673c5af15b0472dfba57bf2ed9c72316500a1d5f3b9f72df70d8e23f84d2f1a3b9781144819216cc69bb74a8", + "response": "020000000000000007004092066fdb660a01dfa8161063c7db13b52a68d388f3c1cfbf18a33e4eac81466aecf23a9f7ba2d0107210aafc786e5495d384cdab2eef78f3f57f1838cd55b80800000006a9e579f92e4cf27efa8676bf21f9bf6200000007616c6963652d3706f73cf4600d657de46b1c253865fb864c21c61d2f953e0de8783551a320a6ead8db24ada8c88dc61ef91918546bfbcf7a368978fa3fd0be323df12dd9e33158b6030f85a1626f67b9ce76824925638f0001ac3dc794d894a2af5b4022741c034ecbc9d30345af44d4b81b393ae5b4fc5347414bf5100e0e7375962137f3033ad5f665473dc0ef92ae6ee72bc707a4823f18b1ada9796df18a84c12362f3b0c62d3de28c2225f21bcad3800f653155c2b53c174111c6b7b441301e95ad238c2edf02019a70f05f6c110d87718749d218abe61d741609bd442bea1d32cad08ec7786785783c97f3a538e3b5efd329babbfd1b04779837fe3bc02ef1853f1a5c230c9fba2ff677633ff6a6fec42460a0544fa203fd64e801091419ada272aabff336e6be2b78ca81ad48ba56b279a340ace4f90c019091c1499bd91845ffc2cf460f6129111f41ae66b96fdc08cd5fcacae8a9d63c26fa91c612726fab2892d9d4bd0f73d52f623d1e536dd405e66fb3fb6c5cb157ac15cffa6eb16f43f72685f8f9b641d097435b4df6f5e3173987f2cec4d7c7506d20083e4c1037957595afa23e12340900e8064efcdf44e6573edc6fdfb948d4d36439d9e825e1e2d4b8b6ceddb5780bf5cb1207e328983ca5b32ff580ad6a1c96b080d6ee0290932ddb84ff37db88380cd470f7dddbf4e7dda6cc76c74592f60301a10b9e3431d41736b97329aecb79fbd1dfc8274f51feb0e191c77541bdaca5a279f3efd60d574baf99ba8351653f86889db173fc5321dbf9738f1fbb84e5fd442f8b21447179087c71097be04beecb9954b83f1c2e3e7346743de7e75ee452a0f1295f7844782688166c0e4c3ea7330a00030000019fb36a14160000019fb36a14e10000019fb36a15470106010601030104028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8a10b9e3431d41736b97329aecb79fbd1dfc8274f51feb0e191c77541bdaca5a203010301040006b7d14d0e61b4a6a2e9f2b87cc12934a43eb0414a802f633cc9c9ac1aea2642bde9552236b6837be95ee871867bd92ccf4adf17307293f2d5b150503846b1feca00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000017880251da9e5393dc126d60e334a24fbb66470b65c67ba8dbf62ac32e4f2e3a025742d4e8a54686a65ec49661b769d311705edf230564fa5d2039c993961d83adc8626a80f920a5717461488bdc2ff96de261f8b066c4fd50280743ac0ca1b9dc0003640bb8ffee74cdc03cf7a8dc56eb952a3143b2cd97f6f4bb374097b42c2fb5e7c0ca7d871a7590e4ea6a9478a8cd665a77ec9b309a6c1ee774be218968f9e74c47f05071522b1f5d558ffba5628654f65c5fe2dca4893d40090d4ad41e6ba7f1", "timestamps": [ - 1785340634265, - 1785340634469, - 1785340634572 + 1785421501462, + 1785421501665, + 1785421501767 ], "tree_size": 7, "version": 6 diff --git a/interop/vectors/tree-head-p256.json b/interop/vectors/tree-head-p256.json new file mode 100644 index 0000000..a1e01f3 --- /dev/null +++ b/interop/vectors/tree-head-p256.json @@ -0,0 +1,222 @@ +{ + "primitive": "tree-head", + "draft": "draft-ietf-keytrans-protocol-05 §11.2, §11.3, §11.4", + "generator": { + "impl": "katie", + "sha": "00da52541f6ae6a7f3905181e2ba9de8ec0d6cdc" + }, + "cipher_suite": 1, + "notes": "Signed tree heads and the configuration every signature covers. `configuration` is the encoded Configuration, `tree_head_tbs` is what the signature is computed over, and `tree_head` is the wire TreeHead. Note the §11.2 reading recorded here: leaf_public_key is emitted only under thirdPartyManagement, which is what katie does and what the draft's prose supports, while the grouped-case reading of the struct would also put it in contactMonitoring -- and keytrans-verification's own notes take that second reading. The two differ by 34 bytes in the Configuration and therefore in every TreeHeadTBS. The negative cases are signatures that must not verify.", + "cases": [ + { + "name": "contact-monitoring-size-1", + "input": { + "max_ahead": 10000, + "max_behind": 10000, + "mode": 1, + "reasonable_monitoring_window": 604800000, + "root": "0101010101010101010101010101010101010101010101010101010101010101", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 1, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "02000000000000000100406daa2527593c97c96ec1ee2a82ad5f715bf8b39cbfd6407a227b767fad18b409468022840ebb30246d94eb55c3048a8e6161ec831f5fe409ef35c233514670e8", + "signature": "6daa2527593c97c96ec1ee2a82ad5f715bf8b39cbfd6407a227b767fad18b409468022840ebb30246d94eb55c3048a8e6161ec831f5fe409ef35c233514670e8", + "tree_head": "000000000000000100406daa2527593c97c96ec1ee2a82ad5f715bf8b39cbfd6407a227b767fad18b409468022840ebb30246d94eb55c3048a8e6161ec831f5fe409ef35c233514670e8", + "tree_head_tbs": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c84000000000000000000010101010101010101010101010101010101010101010101010101010101010101" + } + }, + { + "name": "contact-monitoring-size-8", + "input": { + "max_ahead": 10000, + "max_behind": 10000, + "mode": 1, + "reasonable_monitoring_window": 604800000, + "root": "0808080808080808080808080808080808080808080808080808080808080808", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 8, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "0200000000000000080040a656a9ae2ab6f26e5c4b742c9e150c1ea3f32ae1ebf9a130f37c0bc8b38e2c7dcf57bcaf951c13366ffda20b008e7731b349a264c16d398506b66e89ba34b7f8", + "signature": "a656a9ae2ab6f26e5c4b742c9e150c1ea3f32ae1ebf9a130f37c0bc8b38e2c7dcf57bcaf951c13366ffda20b008e7731b349a264c16d398506b66e89ba34b7f8", + "tree_head": "00000000000000080040a656a9ae2ab6f26e5c4b742c9e150c1ea3f32ae1ebf9a130f37c0bc8b38e2c7dcf57bcaf951c13366ffda20b008e7731b349a264c16d398506b66e89ba34b7f8", + "tree_head_tbs": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c84000000000000000000080808080808080808080808080808080808080808080808080808080808080808" + } + }, + { + "name": "contact-monitoring-size-50", + "input": { + "max_ahead": 10000, + "max_behind": 10000, + "mode": 1, + "reasonable_monitoring_window": 604800000, + "root": "3232323232323232323232323232323232323232323232323232323232323232", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 50, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "0200000000000000320040def539e44ad54749da8f65dd84fe0799343e2eeb58dd9d92cd188b9467b6f37eed164dbf043d853a2014fc7bdebbf4174beb9254023d27a5434bc623bd41f7cd", + "signature": "def539e44ad54749da8f65dd84fe0799343e2eeb58dd9d92cd188b9467b6f37eed164dbf043d853a2014fc7bdebbf4174beb9254023d27a5434bc623bd41f7cd", + "tree_head": "00000000000000320040def539e44ad54749da8f65dd84fe0799343e2eeb58dd9d92cd188b9467b6f37eed164dbf043d853a2014fc7bdebbf4174beb9254023d27a5434bc623bd41f7cd", + "tree_head_tbs": "00010100410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d840000000000002710000000000000271000000000240c84000000000000000000323232323232323232323232323232323232323232323232323232323232323232" + } + }, + { + "name": "third-party-management-size-1", + "input": { + "leaf_public_key": "04158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b9", + "max_ahead": 10000, + "max_behind": 10000, + "mode": 2, + "reasonable_monitoring_window": 604800000, + "root": "0101010101010101010101010101010101010101010101010101010101010101", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 1, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "02000000000000000100403efff5eb85418413fa698c28fecc2e40fe812d1493d7f728885ca87b9d09ead2a376a9a66d4fca5f98a448df60f4d35668036027496d9efcee5f1c0ca69b566a", + "signature": "3efff5eb85418413fa698c28fecc2e40fe812d1493d7f728885ca87b9d09ead2a376a9a66d4fca5f98a448df60f4d35668036027496d9efcee5f1c0ca69b566a", + "tree_head": "000000000000000100403efff5eb85418413fa698c28fecc2e40fe812d1493d7f728885ca87b9d09ead2a376a9a66d4fca5f98a448df60f4d35668036027496d9efcee5f1c0ca69b566a", + "tree_head_tbs": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c84000000000000000000010101010101010101010101010101010101010101010101010101010101010101" + } + }, + { + "name": "third-party-management-size-8", + "input": { + "leaf_public_key": "04158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b9", + "max_ahead": 10000, + "max_behind": 10000, + "mode": 2, + "reasonable_monitoring_window": 604800000, + "root": "0808080808080808080808080808080808080808080808080808080808080808", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 8, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "020000000000000008004079cdba2825acd68d4eec4557fded5bcc0cfc1bf1ccc1811f4d4c2d3bc357074fa9ec69bb75105ecceff0c7c76bf117dcbc0e202cf448144c5c050dc24e80f0b3", + "signature": "79cdba2825acd68d4eec4557fded5bcc0cfc1bf1ccc1811f4d4c2d3bc357074fa9ec69bb75105ecceff0c7c76bf117dcbc0e202cf448144c5c050dc24e80f0b3", + "tree_head": "0000000000000008004079cdba2825acd68d4eec4557fded5bcc0cfc1bf1ccc1811f4d4c2d3bc357074fa9ec69bb75105ecceff0c7c76bf117dcbc0e202cf448144c5c050dc24e80f0b3", + "tree_head_tbs": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c84000000000000000000080808080808080808080808080808080808080808080808080808080808080808" + } + }, + { + "name": "third-party-management-size-50", + "input": { + "leaf_public_key": "04158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b9", + "max_ahead": 10000, + "max_behind": 10000, + "mode": 2, + "reasonable_monitoring_window": 604800000, + "root": "3232323232323232323232323232323232323232323232323232323232323232", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 50, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "configuration": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "02000000000000003200407b038badf7096bf6f5801c53d037dcc919772b1c9c00ef3d4f751f09d96c3483524c90f19a27aab28a712826756bceaf951ed99c9b6c8e68db18cb6d4006cd03", + "signature": "7b038badf7096bf6f5801c53d037dcc919772b1c9c00ef3d4f751f09d96c3483524c90f19a27aab28a712826756bceaf951ed99c9b6c8e68db18cb6d4006cd03", + "tree_head": "000000000000003200407b038badf7096bf6f5801c53d037dcc919772b1c9c00ef3d4f751f09d96c3483524c90f19a27aab28a712826756bceaf951ed99c9b6c8e68db18cb6d4006cd03", + "tree_head_tbs": "00010200410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84004104158c6e85cd19a08adb04bef18dc4ad91da596fb285329ea6ca02a8ea62c452dd2f137ca6832837812f87a235c18775218e448f244c5ba63c45270a23dfb7b6b90000000000002710000000000000271000000000240c84000000000000000000323232323232323232323232323232323232323232323232323232323232323232" + } + }, + { + "name": "third-party-auditing-size-1", + "input": { + "auditor_public_key": "04515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe2203", + "auditor_start_pos": 0, + "auditor_timestamp": 1700000000000, + "max_ahead": 10000, + "max_auditor_lag": 60000, + "max_behind": 10000, + "mode": 3, + "reasonable_monitoring_window": 604800000, + "root": "0101010101010101010101010101010101010101010101010101010101010101", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 1, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "auditor_tree_head": "0000018bcfe56800000000000000000100408e81167b22294d32448ebf820d52927e6a56fbec30433c2553dd8e8365bd081f3398d174404f72484fdf8b5df1ce6b524d6a3ddb9f55a7614f9cd53c8d3197ed", + "auditor_tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c8400000000018bcfe5680000000000000000010101010101010101010101010101010101010101010101010101010101010101", + "configuration": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "0200000000000000010040d4e023467190fd73daa12120d1f5f2587b2acf027e57ea6bb7cf0254b8375bf7741ffb344cdf1dae906977a06ccd41a4e049c8bdd011d96821733d02db9df51d0000018bcfe56800000000000000000100408e81167b22294d32448ebf820d52927e6a56fbec30433c2553dd8e8365bd081f3398d174404f72484fdf8b5df1ce6b524d6a3ddb9f55a7614f9cd53c8d3197ed", + "signature": "d4e023467190fd73daa12120d1f5f2587b2acf027e57ea6bb7cf0254b8375bf7741ffb344cdf1dae906977a06ccd41a4e049c8bdd011d96821733d02db9df51d", + "tree_head": "00000000000000010040d4e023467190fd73daa12120d1f5f2587b2acf027e57ea6bb7cf0254b8375bf7741ffb344cdf1dae906977a06ccd41a4e049c8bdd011d96821733d02db9df51d", + "tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c84000000000000000000010101010101010101010101010101010101010101010101010101010101010101" + } + }, + { + "name": "third-party-auditing-size-8", + "input": { + "auditor_public_key": "04515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe2203", + "auditor_start_pos": 0, + "auditor_timestamp": 1700000000000, + "max_ahead": 10000, + "max_auditor_lag": 60000, + "max_behind": 10000, + "mode": 3, + "reasonable_monitoring_window": 604800000, + "root": "0808080808080808080808080808080808080808080808080808080808080808", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 8, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "auditor_tree_head": "0000018bcfe5680000000000000000080040974f9bf570e682105adcc6de425b2a73c373cd0c74eddf2c739c50727eea5aaa0842afebdf316e85fb731b9584fe91fab2c6fde40bab5fb0e848f495d5d0b044", + "auditor_tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c8400000000018bcfe5680000000000000000080808080808080808080808080808080808080808080808080808080808080808", + "configuration": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "02000000000000000800407d784be539129a8f54ba317d68dc60b65143682ff78429115501f40406cef225a2d9ff823a6cc460209323f55decde416d6f0336f10fe855ba64cb5b39988c540000018bcfe5680000000000000000080040974f9bf570e682105adcc6de425b2a73c373cd0c74eddf2c739c50727eea5aaa0842afebdf316e85fb731b9584fe91fab2c6fde40bab5fb0e848f495d5d0b044", + "signature": "7d784be539129a8f54ba317d68dc60b65143682ff78429115501f40406cef225a2d9ff823a6cc460209323f55decde416d6f0336f10fe855ba64cb5b39988c54", + "tree_head": "000000000000000800407d784be539129a8f54ba317d68dc60b65143682ff78429115501f40406cef225a2d9ff823a6cc460209323f55decde416d6f0336f10fe855ba64cb5b39988c54", + "tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c84000000000000000000080808080808080808080808080808080808080808080808080808080808080808" + } + }, + { + "name": "third-party-auditing-size-50", + "input": { + "auditor_public_key": "04515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe2203", + "auditor_start_pos": 0, + "auditor_timestamp": 1700000000000, + "max_ahead": 10000, + "max_auditor_lag": 60000, + "max_behind": 10000, + "mode": 3, + "reasonable_monitoring_window": 604800000, + "root": "3232323232323232323232323232323232323232323232323232323232323232", + "signature_public_key": "0424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54", + "tree_size": 50, + "vrf_public_key": "02d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84" + }, + "expect": { + "auditor_tree_head": "0000018bcfe5680000000000000000320040ee69f572fb5410ef5fb07d016fae1b63f58975b1804e5b7da81081dca97c0d879ca6c3640b186a93dbd39274026f82d662de69023578f3be0eab84d0b46c8aad", + "auditor_tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c8400000000018bcfe5680000000000000000323232323232323232323232323232323232323232323232323232323232323232", + "configuration": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c840000", + "full_tree_head_same": "01", + "full_tree_head_updated": "0200000000000000320040026fd3d5d6a9912934d2642323031be7b7a60e873df29f5c00fbf3b318a65515fb3f34f0210ccb22a47d3f3fe9ed6aed38881ab791a488381b495b8708930e5f0000018bcfe5680000000000000000320040ee69f572fb5410ef5fb07d016fae1b63f58975b1804e5b7da81081dca97c0d879ca6c3640b186a93dbd39274026f82d662de69023578f3be0eab84d0b46c8aad", + "signature": "026fd3d5d6a9912934d2642323031be7b7a60e873df29f5c00fbf3b318a65515fb3f34f0210ccb22a47d3f3fe9ed6aed38881ab791a488381b495b8708930e5f", + "tree_head": "00000000000000320040026fd3d5d6a9912934d2642323031be7b7a60e873df29f5c00fbf3b318a65515fb3f34f0210ccb22a47d3f3fe9ed6aed38881ab791a488381b495b8708930e5f", + "tree_head_tbs": "00010300410424d839c97ac374c8b4d501b3024c04e2866a5a8905448995522f285d37fbf1be70c6a32182e59b94f19030607ccd566e1f5fd4a2b82d10b42629aecfb9943b54002102d66ee10aac8c03cbff3004f11f0d4506a711687ea2b07482bd600acd9f4d3d84000000000000ea600000000000000000004104515a01505234a85ea5f577a55ec3ef380f1c1ff6831a5f9b2865a29d41029dcef9005ff9cda331eec4d4a141dc5b7be7216980269c57b9ed83cfca1718fe22030000000000002710000000000000271000000000240c84000000000000000000323232323232323232323232323232323232323232323232323232323232323232" + } + } + ] +} diff --git a/interop/vectors/update.json b/interop/vectors/update.json new file mode 100644 index 0000000..cfb8f90 --- /dev/null +++ b/interop/vectors/update.json @@ -0,0 +1,1605 @@ +{ + "primitive": "update", + "draft": "draft-ietf-keytrans-protocol-05 §9.1, §13.5", + "generator": { + "impl": "katie", + "sha": "00da52541f6ae6a7f3905181e2ba9de8ec0d6cdc" + }, + "cipher_suite": 2, + "notes": "CombinedTreeProof structures for §9.1, produced by katie's own algorithm implementation. `mutations` describes how the log was built, `owner` is the label owner's state before the update, and `ladder` gives every search key the proof's lookups need — the union of what the owner already held and what the response would add. These carry no UpdateResponse envelope: katie's tree.Update cannot answer any request (see KT-04), so the envelope was never measured and is not claimed. The proof's elements carry no indication of which log entry they belong to; they are in the order §9.1 asks for them.", + "cases": [ + { + "name": "single-version-previous-frontier-skipped", + "input": { + "entry_timestamps": [ + 1785421503085, + 1785421503086, + 1785421503086, + 1785421503087, + 1785421503087, + 1785421503087, + 1785421503088 + ], + "label": "616c696365406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "0f61f54efe611f32f1a02ccadcae0ad1765687434ba3123cdfdfc95150d424a8", + "version": 6, + "vrf_output": "599424025a070d0e48913585265a30210ffe3416005c77c2354094f14c6358a1" + }, + { + "version": 7, + "vrf_output": "80e204b5356fa5a4e88a74d22763e8212b9e2882397e69aaf852a7010f23f672" + } + ], + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "§9.1 step 2.1: the ladder for the previous tree's rightmost entry arrived with the update that created the owner's last version, so this response carries none.", + "owner": { + "starting": 3, + "upcoming": [ + 4, + 5 + ], + "version_at_starting": 3 + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 1, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 6 + }, + "distinguished": false, + "inclusion": [ + "b8bc2e6ee0d686da4a5996c0976bdf9519916dc9e73475e4953e00abbe6dad12", + "f09f3995ce9f96bba58004e321bb1894f635d8d7b6be1e9613e40e30eaa1a97d", + "f71153fff0e227a89d8681e434e06c7528cc01318c8b90d5a0f245ecc7bc6ffc" + ], + "prefix_proofs": [ + { + "elements": [ + "24571aac6ce6097b3ac44c97cb9c3ce455c0e1dddb485edf5fe76ab917f153b7", + "e3eef9c2e8fcd49919cff47994bc0b3590f2530b280106628e6c42d5b9b8fda2", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "df48e7fedb16bf83cd4e74ca54e6e9a9ab33dcb09a6c1c92e9b0604ddd071349", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "6dc180f6d913f8d1797de4edbb71b24665f5769ef0ef92d004f0634ab7c8f151", + "20f6ced22d235e6264a4bece1c8cfc941d8195be3eef95e22956403e3d25f2ff", + "f0a91937ff9aeaa1e0f7693d3d30a003a415695aecc8fef24148779c1409752c", + "337143ae04df03d622cbf9f3598bd89eb89ea51f38ae633d104ac056289a947c", + "a3ddac0de3af1c5a5f6a0106721a17022164663bf9741ec3fd2aa10ae720f16e" + ], + "encoding": "020284c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66465efb1a0f8ad7fa9e9d9f256756ee035b45580a29b7d4a36fcaf13d1f6ea1cb05010b000e24571aac6ce6097b3ac44c97cb9c3ce455c0e1dddb485edf5fe76ab917f153b7e3eef9c2e8fcd49919cff47994bc0b3590f2530b280106628e6c42d5b9b8fda2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df48e7fedb16bf83cd4e74ca54e6e9a9ab33dcb09a6c1c92e9b0604ddd0713490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006dc180f6d913f8d1797de4edbb71b24665f5769ef0ef92d004f0634ab7c8f15120f6ced22d235e6264a4bece1c8cfc941d8195be3eef95e22956403e3d25f2fff0a91937ff9aeaa1e0f7693d3d30a003a415695aecc8fef24148779c1409752c337143ae04df03d622cbf9f3598bd89eb89ea51f38ae633d104ac056289a947ca3ddac0de3af1c5a5f6a0106721a17022164663bf9741ec3fd2aa10ae720f16e", + "results": [ + { + "depth": 5, + "leaf": { + "commitment": "465efb1a0f8ad7fa9e9d9f256756ee035b45580a29b7d4a36fcaf13d1f6ea1cb", + "vrf_output": "84c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66" + }, + "result_type": 2 + }, + { + "depth": 11, + "result_type": 1 + } + ] + } + ], + "prefix_roots": [ + "2d45d8280388b6eb8f6fb556f69e1fd7a1d2f66e2181b8fa249b1e2040d793f3", + "37391eaa9af7321cd73c6c2de269f32afe7a85b6a1f33f8c3fa24f116f43bd67" + ], + "proof": "030000019fb36a1a6f0000019fb36a1a6f0000019fb36a1a7001020284c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66465efb1a0f8ad7fa9e9d9f256756ee035b45580a29b7d4a36fcaf13d1f6ea1cb05010b000e24571aac6ce6097b3ac44c97cb9c3ce455c0e1dddb485edf5fe76ab917f153b7e3eef9c2e8fcd49919cff47994bc0b3590f2530b280106628e6c42d5b9b8fda2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000df48e7fedb16bf83cd4e74ca54e6e9a9ab33dcb09a6c1c92e9b0604ddd0713490000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006dc180f6d913f8d1797de4edbb71b24665f5769ef0ef92d004f0634ab7c8f15120f6ced22d235e6264a4bece1c8cfc941d8195be3eef95e22956403e3d25f2fff0a91937ff9aeaa1e0f7693d3d30a003a415695aecc8fef24148779c1409752c337143ae04df03d622cbf9f3598bd89eb89ea51f38ae633d104ac056289a947ca3ddac0de3af1c5a5f6a0106721a17022164663bf9741ec3fd2aa10ae720f16e022d45d8280388b6eb8f6fb556f69e1fd7a1d2f66e2181b8fa249b1e2040d793f337391eaa9af7321cd73c6c2de269f32afe7a85b6a1f33f8c3fa24f116f43bd670003b8bc2e6ee0d686da4a5996c0976bdf9519916dc9e73475e4953e00abbe6dad12f09f3995ce9f96bba58004e321bb1894f635d8d7b6be1e9613e40e30eaa1a97df71153fff0e227a89d8681e434e06c7528cc01318c8b90d5a0f245ecc7bc6ffc", + "timestamps": [ + 1785421503087, + 1785421503087, + 1785421503088 + ] + } + }, + { + "name": "single-version-previous-frontier-inspected", + "input": { + "entry_timestamps": [ + 1785421503092, + 1785421503093, + 1785421503093, + 1785421503094, + 1785421503094, + 1785421503094, + 1785421503095 + ], + "label": "626f62406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "75e9437667aef7233c7f5cb6942a25c09ad74916ebef690517917ab7bf7d43a7", + "version": 1, + "vrf_output": "b32c245bcee1511d1b1f2c4ac863aa6d4a47d161925042a5bd3728b4ae09e0ab" + }, + { + "version": 2, + "vrf_output": "50509ab4be0b604b009eefa6a2acc0347f2bd9cd26d9d0ecff59f7bd3106b4f7" + }, + { + "version": 3, + "vrf_output": "5835dbb16e184c83d9ef0eec432aca88a838dcf0f6bb8b747e3779f77da0a3ee" + } + ], + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "§9.1 step 2.2: a greatest-version search over the previous tree's frontier, which is the phase that stops a log from creating a version and hiding it.", + "owner": { + "starting": 3, + "upcoming": [], + "version_at_starting": 0 + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 1, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 1 + }, + "distinguished": false, + "inclusion": [ + "22ec6c77f944e93af96139a82ead3d84d7df23755d5f18f33a391bac53ab9ec9", + "01bab599350bc07c0cbe1148a316dae54fa6fe0bb6779bad775d51a80171a8e8", + "90ab56735a3cc7cf136b1bc2c816849423e4a9982ab1f9338541cfe40d40cc79" + ], + "prefix_proofs": [ + { + "elements": [ + "83ee6a005afcedb1b30bc5bccc5f79ab977e26881742d2084d631d99f5f894af", + "0e370d9a7035963dd80376ed4754f252c971660359d0369c254dbad94889517c", + "d6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f" + ], + "encoding": "0102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f0ab7b702ff7bd5a9216e194eb5966c498da4483298ab6b229daa4f6800dd013b03000383ee6a005afcedb1b30bc5bccc5f79ab977e26881742d2084d631d99f5f894af0e370d9a7035963dd80376ed4754f252c971660359d0369c254dbad94889517cd6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f", + "results": [ + { + "depth": 3, + "leaf": { + "commitment": "0ab7b702ff7bd5a9216e194eb5966c498da4483298ab6b229daa4f6800dd013b", + "vrf_output": "a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f" + }, + "result_type": 2 + } + ] + }, + { + "elements": [ + "9645af6695ae9d5a90ae53fbe46d186c786312ad6707f4efeb39ece1af02fe93", + "b2c4db0889b9dca680bc5e8fafe32e138389f21a145ee1d5c85c3e632f6a9222", + "8e6882d2f99ab23fe6fe20aba231462ae76a9c2fc7aacf31621c337ef48d2bbf", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "309890a2cf6e8ad61a662c9e7076aa2d8435cd9a27681f92801ca24ed6f2e8d3", + "fe5b76f4143a1fd2142796e75d28973115fe294ae1575caa149268f031e36d2c", + "dbe81199d27ae24bfbc492c7964d0a3b74099a5f59a249479cd2979be07baf82", + "8de5158af2888cdeb168d5f8dcf7537f659cbd2fa4d5fb8500b4c4660157549d", + "d6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f" + ], + "encoding": "03010503080305000a9645af6695ae9d5a90ae53fbe46d186c786312ad6707f4efeb39ece1af02fe93b2c4db0889b9dca680bc5e8fafe32e138389f21a145ee1d5c85c3e632f6a92228e6882d2f99ab23fe6fe20aba231462ae76a9c2fc7aacf31621c337ef48d2bbf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000309890a2cf6e8ad61a662c9e7076aa2d8435cd9a27681f92801ca24ed6f2e8d3fe5b76f4143a1fd2142796e75d28973115fe294ae1575caa149268f031e36d2cdbe81199d27ae24bfbc492c7964d0a3b74099a5f59a249479cd2979be07baf828de5158af2888cdeb168d5f8dcf7537f659cbd2fa4d5fb8500b4c4660157549dd6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f", + "results": [ + { + "depth": 5, + "result_type": 1 + }, + { + "depth": 8, + "result_type": 3 + }, + { + "depth": 5, + "result_type": 3 + } + ] + } + ], + "prefix_roots": [ + "6d362f523f0c22287793c85412ad45ceb234129a50b133108f28c0f2af382f50" + ], + "proof": "030000019fb36a1a760000019fb36a1a760000019fb36a1a77020102a23e5c9a8983f63dd4358efa06aa4d2077629a50c452b7e3da7a1b26e6181f7f0ab7b702ff7bd5a9216e194eb5966c498da4483298ab6b229daa4f6800dd013b03000383ee6a005afcedb1b30bc5bccc5f79ab977e26881742d2084d631d99f5f894af0e370d9a7035963dd80376ed4754f252c971660359d0369c254dbad94889517cd6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f03010503080305000a9645af6695ae9d5a90ae53fbe46d186c786312ad6707f4efeb39ece1af02fe93b2c4db0889b9dca680bc5e8fafe32e138389f21a145ee1d5c85c3e632f6a92228e6882d2f99ab23fe6fe20aba231462ae76a9c2fc7aacf31621c337ef48d2bbf00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000309890a2cf6e8ad61a662c9e7076aa2d8435cd9a27681f92801ca24ed6f2e8d3fe5b76f4143a1fd2142796e75d28973115fe294ae1575caa149268f031e36d2cdbe81199d27ae24bfbc492c7964d0a3b74099a5f59a249479cd2979be07baf828de5158af2888cdeb168d5f8dcf7537f659cbd2fa4d5fb8500b4c4660157549dd6cb098fda06939b2b35f8841e12916e46184703da81024bf98b7788bb9bea2f016d362f523f0c22287793c85412ad45ceb234129a50b133108f28c0f2af382f50000322ec6c77f944e93af96139a82ead3d84d7df23755d5f18f33a391bac53ab9ec901bab599350bc07c0cbe1148a316dae54fa6fe0bb6779bad775d51a80171a8e890ab56735a3cc7cf136b1bc2c816849423e4a9982ab1f9338541cfe40d40cc79", + "timestamps": [ + 1785421503094, + 1785421503094, + 1785421503095 + ] + } + }, + { + "name": "multi-version-covered-by-the-ladder", + "input": { + "entry_timestamps": [ + 1785421503099, + 1785421503100, + 1785421503100, + 1785421503101, + 1785421503101, + 1785421503101, + 1785421503102 + ], + "label": "6361726f6c406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "e08b4dcba8ac1b8a8ceb9a615463c877bcc2015e566e89babbc2ff50476768c8", + "version": 0, + "vrf_output": "59b2b7f1b15e9b8a630ebcc077e7eef5cabd784c4b47498dfe095681d75c1f77" + }, + { + "commitment": "656902743914785b0d648b6134693ecb55b8a7498a643b72c069b66cdefc903b", + "version": 1, + "vrf_output": "6bd49bee46d071009be5abee995e7aaae04a9ecf76f17452a6aa50d721add5a4" + }, + { + "commitment": "7dd50f4843d4a649e19bf7cdf4a2c8c3bf6062d3ac3f93f27febf70ae214cca9", + "version": 2, + "vrf_output": "bf73c61cd407cd359603e4f48889c19c38f15ad5bb8c771f52a9954cd2883f72" + }, + { + "version": 3, + "vrf_output": "d0656c31cbe2327510fbdd88fbc1544ec56aa7616194f152eb2356b6b7c84ed6" + } + ], + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "Three versions at once, all of them rungs of the ladder for the new greatest version, so §9.1 asks for no additional inclusion proof.", + "owner": { + "starting": 3, + "upcoming": [] + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 3, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 2 + }, + "distinguished": false, + "inclusion": [ + "ea994fda48ff8d55a76a7cb5c28c927caac6bfe4a15cdf520cb402c5a407d533", + "ea9caf533c104f9af9c4c48857fbf6fca013f22abebb78979964702f445a162b", + "9056b75f9d1f38654385c9ae669e35bdabc99b6cb9dc6867a01830d753e968df" + ], + "prefix_proofs": [ + { + "elements": [ + "5aa0e7306541fa980465839153b39fb148384ce8207f530ec1bbf163e0168be0", + "9add98040b0830841cadfd0d95e8b0b88a13023175cf393a0730241f96452ae9" + ], + "encoding": "010249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45674f08981c14d69f12f12f876d7291aeecb8337b240d173045683710152fad030200025aa0e7306541fa980465839153b39fb148384ce8207f530ec1bbf163e0168be09add98040b0830841cadfd0d95e8b0b88a13023175cf393a0730241f96452ae9", + "results": [ + { + "depth": 2, + "leaf": { + "commitment": "674f08981c14d69f12f12f876d7291aeecb8337b240d173045683710152fad03", + "vrf_output": "49ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45" + }, + "result_type": 2 + } + ] + }, + { + "elements": [ + "e921e36a5a52a745d2796151e69a7cff109c8c0f87b19992f4d74407bef0f4b7", + "d2c2c03400f705297c5e9a193216525c4308c6359bda35d90e887102bbf24124", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "bc51a0d5b9ed02e80a5dedde4a2202914e01b888c01adea07f5a1ba9973a23d5", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "3af36b077a6a8372bce6f16b6e65ab50a9669f97802de8b6a847b90967734c2e", + "9a4ed6a5f2cace025a11a752dc8c4c9d3fbb848517ad214c4bbf9d4fb1c228ef", + "e64b3eb666fea12833c7646f9410757be98e157bd58e7755282c2e7fea80e7ed", + "6104cfadbd695a67d31a897156b24466bc433110a96e9e776ba3f99900c9a296" + ], + "encoding": "04010b010303030105000de921e36a5a52a745d2796151e69a7cff109c8c0f87b19992f4d74407bef0f4b7d2c2c03400f705297c5e9a193216525c4308c6359bda35d90e887102bbf24124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc51a0d5b9ed02e80a5dedde4a2202914e01b888c01adea07f5a1ba9973a23d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003af36b077a6a8372bce6f16b6e65ab50a9669f97802de8b6a847b90967734c2e9a4ed6a5f2cace025a11a752dc8c4c9d3fbb848517ad214c4bbf9d4fb1c228efe64b3eb666fea12833c7646f9410757be98e157bd58e7755282c2e7fea80e7ed6104cfadbd695a67d31a897156b24466bc433110a96e9e776ba3f99900c9a296", + "results": [ + { + "depth": 11, + "result_type": 1 + }, + { + "depth": 3, + "result_type": 1 + }, + { + "depth": 3, + "result_type": 3 + }, + { + "depth": 5, + "result_type": 1 + } + ] + } + ], + "prefix_roots": [ + "feb8873564c445788ef6534e8dae48b632d3101df07f1b5ca91db9122c1383ff" + ], + "proof": "030000019fb36a1a7d0000019fb36a1a7d0000019fb36a1a7e02010249ac592e66e6cdf540930a2de58d345878986b1e337ee0aeb9d5c3730be70e45674f08981c14d69f12f12f876d7291aeecb8337b240d173045683710152fad030200025aa0e7306541fa980465839153b39fb148384ce8207f530ec1bbf163e0168be09add98040b0830841cadfd0d95e8b0b88a13023175cf393a0730241f96452ae904010b010303030105000de921e36a5a52a745d2796151e69a7cff109c8c0f87b19992f4d74407bef0f4b7d2c2c03400f705297c5e9a193216525c4308c6359bda35d90e887102bbf24124000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000bc51a0d5b9ed02e80a5dedde4a2202914e01b888c01adea07f5a1ba9973a23d50000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003af36b077a6a8372bce6f16b6e65ab50a9669f97802de8b6a847b90967734c2e9a4ed6a5f2cace025a11a752dc8c4c9d3fbb848517ad214c4bbf9d4fb1c228efe64b3eb666fea12833c7646f9410757be98e157bd58e7755282c2e7fea80e7ed6104cfadbd695a67d31a897156b24466bc433110a96e9e776ba3f99900c9a29601feb8873564c445788ef6534e8dae48b632d3101df07f1b5ca91db9122c1383ff0003ea994fda48ff8d55a76a7cb5c28c927caac6bfe4a15cdf520cb402c5a407d533ea9caf533c104f9af9c4c48857fbf6fca013f22abebb78979964702f445a162b9056b75f9d1f38654385c9ae669e35bdabc99b6cb9dc6867a01830d753e968df", + "timestamps": [ + 1785421503101, + 1785421503101, + 1785421503102 + ] + } + }, + { + "name": "multi-version-with-additional-proof", + "input": { + "entry_timestamps": [ + 1785421503106, + 1785421503107, + 1785421503107, + 1785421503107, + 1785421503108, + 1785421503108, + 1785421503108 + ], + "label": "6572696e406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "e1e4ffd7cad907d0b63c4c479b135955737c14b34fb750fff8982f839e0e5c19", + "version": 1, + "vrf_output": "223da2fa3c1d5037f5cab56241c3d34cbfad7c6efed0b347570ff70e1a621aeb" + }, + { + "commitment": "afbb1a52367ace864e5fa9b1b0367b68cb1434c79c70a86672c0a6ece401cded", + "version": 2, + "vrf_output": "84c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66" + }, + { + "commitment": "52de5093e882bcca7c4458fc657756c322c51d970ae5c1daabd24f378c1bf7dc", + "version": 3, + "vrf_output": "9895b6beb9e522e552206b6cafa816eab1800711eb47b8774fd89ecc827f0e65" + }, + { + "version": 4, + "vrf_output": "f4b6697930279511c4dbff3c1748a82ea2844c1d062ffbd89d5e2dcee291d306" + }, + { + "version": 5, + "vrf_output": "ba43d1f0c9467f9d3a37f7329eb2cb3b5395eba4186e7ba6df299102ab131ede" + }, + { + "version": 7, + "vrf_output": "7e55537200239f25ea5d93fbd10f082f80763eca77d5f227e179de2dec8ce079" + } + ], + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "Versions 1 to 3 created at once. A ladder for version 3 does not look up version 2, so §9.1 sends a second prefix proof from the same log entry.", + "owner": { + "starting": 3, + "upcoming": [], + "version_at_starting": 0 + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 3, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 3 + }, + "distinguished": false, + "inclusion": [ + "6f20892fa41579a401ead7b301a29879dc175d77be3ccb3df0dc044603e11d1a", + "62de86488ed52fe35ceb2eb21b3bbd3388d2c1b77e14b50e495439f2e1a9c8f6", + "5539e438de57165584fd8822f05eb9814f1ee1de02952e89870caed964bc437c" + ], + "prefix_proofs": [ + { + "elements": [ + "bc674f3eda0f0f1607333cae2df82d0faf9482a3d48d4ef527d72eb53634c86f", + "5645d1ed370ff0d238f06c20c7803273d49d494da689bebc743a4709fddceb0b" + ], + "encoding": "01023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee294ade5f8af74a9b8a97b8d6493e7077b1068724983c7082700e7e87c46aca8be020002bc674f3eda0f0f1607333cae2df82d0faf9482a3d48d4ef527d72eb53634c86f5645d1ed370ff0d238f06c20c7803273d49d494da689bebc743a4709fddceb0b", + "results": [ + { + "depth": 2, + "leaf": { + "commitment": "94ade5f8af74a9b8a97b8d6493e7077b1068724983c7082700e7e87c46aca8be", + "vrf_output": "3ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee2" + }, + "result_type": 2 + } + ] + }, + { + "elements": [ + "0000000000000000000000000000000000000000000000000000000000000000", + "c6ecaa862f5b28ff313715b24a46427753e2b38f32b94ccca2c30446e0a60e14", + "2f09828664cedba8e8593eab6f760d5b16f226860679949a667ff12c9fde2c17", + "9c0bcc0cef284c9d05f0dbcef8b0538503acd093c46fd35db325995d52e253bc", + "3c7da02d444c7b3c032e73bf88eab8c4fe26756f0a7519722b0a3a6cb1951947", + "bc5e673146aa384ef762910326499cb24e9421d52ca1a3e6b1605bd7a655cc17", + "0000000000000000000000000000000000000000000000000000000000000000", + "787d27c6a37feaa7de59b7dcb7478b0bd43e1b377bcf02f9e5dbf7a6d94d8596", + "1cf604adf10fafc0ad71f2a82784428508be347f1258e4a6c923320222ffa1a7" + ], + "encoding": "0501040104026bd49bee46d071009be5abee995e7aaae04a9ecf76f17452a6aa50d721add5a47339b2e2d6d000eb70a95cd060b7036ab5c0888bb75632438b0fea86f5c876120302bf73c61cd407cd359603e4f48889c19c38f15ad5bb8c771f52a9954cd2883f72d732437bfd5fe3a22d870f16f6bb583b96cbf9a84b4512223d768ee4a31f7ca005030500090000000000000000000000000000000000000000000000000000000000000000c6ecaa862f5b28ff313715b24a46427753e2b38f32b94ccca2c30446e0a60e142f09828664cedba8e8593eab6f760d5b16f226860679949a667ff12c9fde2c179c0bcc0cef284c9d05f0dbcef8b0538503acd093c46fd35db325995d52e253bc3c7da02d444c7b3c032e73bf88eab8c4fe26756f0a7519722b0a3a6cb1951947bc5e673146aa384ef762910326499cb24e9421d52ca1a3e6b1605bd7a655cc170000000000000000000000000000000000000000000000000000000000000000787d27c6a37feaa7de59b7dcb7478b0bd43e1b377bcf02f9e5dbf7a6d94d85961cf604adf10fafc0ad71f2a82784428508be347f1258e4a6c923320222ffa1a7", + "results": [ + { + "depth": 4, + "result_type": 1 + }, + { + "depth": 4, + "result_type": 1 + }, + { + "depth": 3, + "leaf": { + "commitment": "7339b2e2d6d000eb70a95cd060b7036ab5c0888bb75632438b0fea86f5c87612", + "vrf_output": "6bd49bee46d071009be5abee995e7aaae04a9ecf76f17452a6aa50d721add5a4" + }, + "result_type": 2 + }, + { + "depth": 5, + "leaf": { + "commitment": "d732437bfd5fe3a22d870f16f6bb583b96cbf9a84b4512223d768ee4a31f7ca0", + "vrf_output": "bf73c61cd407cd359603e4f48889c19c38f15ad5bb8c771f52a9954cd2883f72" + }, + "result_type": 2 + }, + { + "depth": 5, + "result_type": 3 + } + ] + }, + { + "elements": [ + "c14cc1452cfa2aec748743d5668c4c4e9cd62a0274179154b62f7a07e3038e46", + "d6b5fdaec39382cf43f61d6afc0a5c9165d9e0a7da30c67e65c6ee2f1d1cd467", + "6331dda10ec731f775326c4a27aab841a5a67020ca4d190965e1444db091b5eb", + "fb35c9ad6769a61d7c400ce90b29dafae3760939bac398644381e87e3e48f648", + "e1a79bcd968c9c21695995545705331ec7836fc940b95ecb9512686a98484bbe" + ], + "encoding": "0101050005c14cc1452cfa2aec748743d5668c4c4e9cd62a0274179154b62f7a07e3038e46d6b5fdaec39382cf43f61d6afc0a5c9165d9e0a7da30c67e65c6ee2f1d1cd4676331dda10ec731f775326c4a27aab841a5a67020ca4d190965e1444db091b5ebfb35c9ad6769a61d7c400ce90b29dafae3760939bac398644381e87e3e48f648e1a79bcd968c9c21695995545705331ec7836fc940b95ecb9512686a98484bbe", + "results": [ + { + "depth": 5, + "result_type": 1 + } + ] + } + ], + "prefix_roots": [ + "79c7d26a87ec1fd45bde9000e7d188711cf949b7e52be81cb1de65b9737114de" + ], + "proof": "030000019fb36a1a830000019fb36a1a840000019fb36a1a840301023ff1b3e0e4016d2e4616cd0ab1b242c07f7484319e7e5c7bc86a6cb9dd341ee294ade5f8af74a9b8a97b8d6493e7077b1068724983c7082700e7e87c46aca8be020002bc674f3eda0f0f1607333cae2df82d0faf9482a3d48d4ef527d72eb53634c86f5645d1ed370ff0d238f06c20c7803273d49d494da689bebc743a4709fddceb0b0501040104026bd49bee46d071009be5abee995e7aaae04a9ecf76f17452a6aa50d721add5a47339b2e2d6d000eb70a95cd060b7036ab5c0888bb75632438b0fea86f5c876120302bf73c61cd407cd359603e4f48889c19c38f15ad5bb8c771f52a9954cd2883f72d732437bfd5fe3a22d870f16f6bb583b96cbf9a84b4512223d768ee4a31f7ca005030500090000000000000000000000000000000000000000000000000000000000000000c6ecaa862f5b28ff313715b24a46427753e2b38f32b94ccca2c30446e0a60e142f09828664cedba8e8593eab6f760d5b16f226860679949a667ff12c9fde2c179c0bcc0cef284c9d05f0dbcef8b0538503acd093c46fd35db325995d52e253bc3c7da02d444c7b3c032e73bf88eab8c4fe26756f0a7519722b0a3a6cb1951947bc5e673146aa384ef762910326499cb24e9421d52ca1a3e6b1605bd7a655cc170000000000000000000000000000000000000000000000000000000000000000787d27c6a37feaa7de59b7dcb7478b0bd43e1b377bcf02f9e5dbf7a6d94d85961cf604adf10fafc0ad71f2a82784428508be347f1258e4a6c923320222ffa1a70101050005c14cc1452cfa2aec748743d5668c4c4e9cd62a0274179154b62f7a07e3038e46d6b5fdaec39382cf43f61d6afc0a5c9165d9e0a7da30c67e65c6ee2f1d1cd4676331dda10ec731f775326c4a27aab841a5a67020ca4d190965e1444db091b5ebfb35c9ad6769a61d7c400ce90b29dafae3760939bac398644381e87e3e48f648e1a79bcd968c9c21695995545705331ec7836fc940b95ecb9512686a98484bbe0179c7d26a87ec1fd45bde9000e7d188711cf949b7e52be81cb1de65b9737114de00036f20892fa41579a401ead7b301a29879dc175d77be3ccb3df0dc044603e11d1a62de86488ed52fe35ceb2eb21b3bbd3388d2c1b77e14b50e495439f2e1a9c8f65539e438de57165584fd8822f05eb9814f1ee1de02952e89870caed964bc437c", + "timestamps": [ + 1785421503107, + 1785421503108, + 1785421503108 + ] + } + }, + { + "name": "first-version-of-a-new-label", + "input": { + "entry_timestamps": [ + 1785421503113, + 1785421503114, + 1785421503114, + 1785421503114, + 1785421503115, + 1785421503115, + 1785421503115 + ], + "label": "64617665406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "c86033a1c890c92a4821d104f7148ec0ba6ce4eb289a15ddab43053322bde6f8", + "version": 0, + "vrf_output": "8b4273dd3f9d2900d8c8c41cb904917c6a144fec131060c7529691f115a903f4" + }, + { + "version": 1, + "vrf_output": "c3ce4a01e5d79820b64188cc45e47046c4fa30ba425c65a129568e90c68273fe" + } + ], + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "The label did not exist at the owner's reference point, so §9.1 step 2.3 asks for a ladder consistent with no version existing at all.", + "owner": { + "starting": 3, + "upcoming": [] + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 1, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 0 + }, + "distinguished": false, + "inclusion": [ + "ed623f27baaea5f527320ebb878eac4cc67f5d264e66c5d4db72dd7c12c6f7e5", + "b34730f66f91918f28ad0dbcfb8a01e1eee44a1a6104794b611a777f21277e41", + "fc95931de3c922a95fc25532941e84562e16a925dbdef6765fff8adb41df533b" + ], + "prefix_proofs": [ + { + "elements": [ + "cc0951290dabb737d8fcf960282fef24cef804ad0455f0e2138af680beae7ad0", + "f49a10c0d641b173a7e514650ae313ba344f4013cd60858bf24bb8bb27d54094", + "d7878c1efb2d0606a340f2c1eff62ae1920327b7564e7ff4bcc16b5969c85edf" + ], + "encoding": "01028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b948752845511f07442474156840c48a86ec0d8e8d76b1463458f592b9171785030003cc0951290dabb737d8fcf960282fef24cef804ad0455f0e2138af680beae7ad0f49a10c0d641b173a7e514650ae313ba344f4013cd60858bf24bb8bb27d54094d7878c1efb2d0606a340f2c1eff62ae1920327b7564e7ff4bcc16b5969c85edf", + "results": [ + { + "depth": 3, + "leaf": { + "commitment": "b948752845511f07442474156840c48a86ec0d8e8d76b1463458f592b9171785", + "vrf_output": "8bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8" + }, + "result_type": 2 + } + ] + }, + { + "elements": [ + "2b43d1ede73659273c3ce20483f22afe5318fce978f4d2ea8a069cfc58327b0b", + "be613b4a0a1d5c3d7b469e1c1c4cc2a6d3030f5340ce271da3d7c9a51169ff65", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "b7ceb0594e8eea96b0472dcbdc5a3f564ee109d8f9ca157b63cc9a0e0f9a8b6b", + "0000000000000000000000000000000000000000000000000000000000000000", + "f47d77b550c0ac4fe63951bc4de648abaedeb5f02c3d9ce6361136b6807bfcda", + "359558469132d511249007fe52988aed380dfdc38550f1dd34ddcf4d47dc01c2", + "75de8dfb068d5dc44891afca9dacac8ca4cb943c31e2eb2f3e41c81cbd29ee50" + ], + "encoding": "020109030300092b43d1ede73659273c3ce20483f22afe5318fce978f4d2ea8a069cfc58327b0bbe613b4a0a1d5c3d7b469e1c1c4cc2a6d3030f5340ce271da3d7c9a51169ff6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ceb0594e8eea96b0472dcbdc5a3f564ee109d8f9ca157b63cc9a0e0f9a8b6b0000000000000000000000000000000000000000000000000000000000000000f47d77b550c0ac4fe63951bc4de648abaedeb5f02c3d9ce6361136b6807bfcda359558469132d511249007fe52988aed380dfdc38550f1dd34ddcf4d47dc01c275de8dfb068d5dc44891afca9dacac8ca4cb943c31e2eb2f3e41c81cbd29ee50", + "results": [ + { + "depth": 9, + "result_type": 1 + }, + { + "depth": 3, + "result_type": 3 + } + ] + } + ], + "prefix_roots": [ + "677d4e970145992d00080dc26995c0f5dd26614d5e0f634c76e291a2d3ff4d9f" + ], + "proof": "030000019fb36a1a8a0000019fb36a1a8b0000019fb36a1a8b0201028bae45f7cb557efa69cdfc5607df19343b91071c69818c06a124adec721d3ba8b948752845511f07442474156840c48a86ec0d8e8d76b1463458f592b9171785030003cc0951290dabb737d8fcf960282fef24cef804ad0455f0e2138af680beae7ad0f49a10c0d641b173a7e514650ae313ba344f4013cd60858bf24bb8bb27d54094d7878c1efb2d0606a340f2c1eff62ae1920327b7564e7ff4bcc16b5969c85edf020109030300092b43d1ede73659273c3ce20483f22afe5318fce978f4d2ea8a069cfc58327b0bbe613b4a0a1d5c3d7b469e1c1c4cc2a6d3030f5340ce271da3d7c9a51169ff6500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b7ceb0594e8eea96b0472dcbdc5a3f564ee109d8f9ca157b63cc9a0e0f9a8b6b0000000000000000000000000000000000000000000000000000000000000000f47d77b550c0ac4fe63951bc4de648abaedeb5f02c3d9ce6361136b6807bfcda359558469132d511249007fe52988aed380dfdc38550f1dd34ddcf4d47dc01c275de8dfb068d5dc44891afca9dacac8ca4cb943c31e2eb2f3e41c81cbd29ee5001677d4e970145992d00080dc26995c0f5dd26614d5e0f634c76e291a2d3ff4d9f0003ed623f27baaea5f527320ebb878eac4cc67f5d264e66c5d4db72dd7c12c6f7e5b34730f66f91918f28ad0dbcfb8a01e1eee44a1a6104794b611a777f21277e41fc95931de3c922a95fc25532941e84562e16a925dbdef6765fff8adb41df533b", + "timestamps": [ + 1785421503114, + 1785421503115, + 1785421503115 + ] + } + }, + { + "name": "distinguished-entry-asks-for-nothing", + "input": { + "entry_timestamps": [ + 1785421503118, + 1785421503119, + 1785421503119, + 1785421503120, + 1785421503120, + 1785421503120, + 1785421503120 + ], + "label": "6361726f6c406578616d706c652e636f6d", + "ladder": [], + "mode": 1, + "monitoring_window": 0, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "A window of zero makes every entry distinguished, so §9.1 takes its step 3 branch — and with every new version covered by the ladder there is nothing left to ask for. The whole proof is the view update.", + "owner": { + "starting": 3, + "upcoming": [] + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 3, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "distinguished": true, + "inclusion": [ + "aca3fa31eaebc98dfe528e49d2e5c34644272767f4b656a8f8bda17582212214", + "d9173714b6f266dbea33d9868e1bc91870c5b8a79b167ca8e3b60de3ed6ccf2b", + "26d89f5011eee48faca343abf6aa71932c4eb3862060ee5c1abb7d2fbba60689" + ], + "prefix_proofs": [], + "prefix_roots": [ + "c9e7585be2cfd2b4602f7c40f2b1627d267cf0a0f8ea16a9c084624b31ce0ad1", + "1e76722d680fb8ffa2eb568116401af545d3a73f0437baf484c9466108ab5a9e", + "f52c832789386c0cbfb6c3fbaf66f35708ee26b9d3e5d6d3577a1f33b6c57505" + ], + "proof": "030000019fb36a1a900000019fb36a1a900000019fb36a1a900003c9e7585be2cfd2b4602f7c40f2b1627d267cf0a0f8ea16a9c084624b31ce0ad11e76722d680fb8ffa2eb568116401af545d3a73f0437baf484c9466108ab5a9ef52c832789386c0cbfb6c3fbaf66f35708ee26b9d3e5d6d3577a1f33b6c575050003aca3fa31eaebc98dfe528e49d2e5c34644272767f4b656a8f8bda17582212214d9173714b6f266dbea33d9868e1bc91870c5b8a79b167ca8e3b60de3ed6ccf2b26d89f5011eee48faca343abf6aa71932c4eb3862060ee5c1abb7d2fbba60689", + "timestamps": [ + 1785421503120, + 1785421503120, + 1785421503120 + ] + } + }, + { + "name": "distinguished-entry-additional-proof", + "input": { + "entry_timestamps": [ + 1785421503122, + 1785421503123, + 1785421503123, + 1785421503124, + 1785421503124, + 1785421503124, + 1785421503124 + ], + "label": "6572696e406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "7011ba97727d9a4194679794660df2e217e0e3a94b6baaa515fb635f6823a0c2", + "version": 2, + "vrf_output": "84c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66" + } + ], + "mode": 1, + "monitoring_window": 0, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "§9.1 step 3 on its own: the entry is distinguished so no ladder is sent, but version 2 is not a rung of the ladder for version 3 and still needs proving.", + "owner": { + "starting": 3, + "upcoming": [], + "version_at_starting": 0 + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 3, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "distinguished": true, + "inclusion": [ + "1730c4427a904d70b4f190b1b114fd8ae69b94ed6a08ca732f031c651aef68b8", + "4109df81c164c69c409e3a8ce0dd2016b7e0f5b963f5c14759b70b3c5a558ecc", + "d4cce129e8b1f1c066b7bc3344ef13567d42b0bcec362399036f4f755352e3f5" + ], + "prefix_proofs": [ + { + "elements": [ + "3e5a4fa0bb568a025274fecdb14c556ddcf41c156cfa18f5ac0fc29399359af6", + "385cf8e7bfd4394ca4d6ab109b7bd088e714921440adb7251a1a96efabc43ce3", + "bed40b58cdd25fa57ce85a6a1c58429be287b4f1dedab19450640e661fdd2326", + "e85ac26baf07ba43e2bf6084b69b1a4916ccbe9011eee4ad374e82ea0e33ada3", + "7020e94a02ffe041b22c0ad328057478d6cc27448c4069fe517ba25cae36f8dc" + ], + "encoding": "01010500053e5a4fa0bb568a025274fecdb14c556ddcf41c156cfa18f5ac0fc29399359af6385cf8e7bfd4394ca4d6ab109b7bd088e714921440adb7251a1a96efabc43ce3bed40b58cdd25fa57ce85a6a1c58429be287b4f1dedab19450640e661fdd2326e85ac26baf07ba43e2bf6084b69b1a4916ccbe9011eee4ad374e82ea0e33ada37020e94a02ffe041b22c0ad328057478d6cc27448c4069fe517ba25cae36f8dc", + "results": [ + { + "depth": 5, + "result_type": 1 + } + ] + } + ], + "prefix_roots": [ + "45cc567747bf1b3c737cfcbf28d0d14746d749faa35d140717dc187faefc09ef", + "292afc180e90bf08644672c829397084214452a4afd663d5347168b5cc41d4a3" + ], + "proof": "030000019fb36a1a940000019fb36a1a940000019fb36a1a940101010500053e5a4fa0bb568a025274fecdb14c556ddcf41c156cfa18f5ac0fc29399359af6385cf8e7bfd4394ca4d6ab109b7bd088e714921440adb7251a1a96efabc43ce3bed40b58cdd25fa57ce85a6a1c58429be287b4f1dedab19450640e661fdd2326e85ac26baf07ba43e2bf6084b69b1a4916ccbe9011eee4ad374e82ea0e33ada37020e94a02ffe041b22c0ad328057478d6cc27448c4069fe517ba25cae36f8dc0245cc567747bf1b3c737cfcbf28d0d14746d749faa35d140717dc187faefc09ef292afc180e90bf08644672c829397084214452a4afd663d5347168b5cc41d4a300031730c4427a904d70b4f190b1b114fd8ae69b94ed6a08ca732f031c651aef68b84109df81c164c69c409e3a8ce0dd2016b7e0f5b963f5c14759b70b3c5a558eccd4cce129e8b1f1c066b7bc3344ef13567d42b0bcec362399036f4f755352e3f5", + "timestamps": [ + 1785421503124, + 1785421503124, + 1785421503124 + ] + } + }, + { + "name": "single-version-with-advertised-size", + "input": { + "entry_timestamps": [ + 1785421503127, + 1785421503127, + 1785421503128, + 1785421503128, + 1785421503128, + 1785421503128, + 1785421503129 + ], + "label": "616c696365406578616d706c652e636f6d", + "ladder": [ + { + "commitment": "16735168adaf31346d97c53e9708785d8ee282074c3dfba89d85491ccacb4cbf", + "version": 6, + "vrf_output": "599424025a070d0e48913585265a30210ffe3416005c77c2354094f14c6358a1" + }, + { + "version": 7, + "vrf_output": "80e204b5356fa5a4e88a74d22763e8212b9e2882397e69aaf852a7010f23f672" + } + ], + "last": 5, + "mode": 1, + "monitoring_window": 604800000, + "mutations": [ + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d31" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d31" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d32" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d33" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d34" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d35" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d36" + } + ] + }, + { + "add": [ + { + "label": "616c696365406578616d706c652e636f6d", + "value": "616c6963652d37" + }, + { + "label": "626f62406578616d706c652e636f6d", + "value": "626f622d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d31" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d32" + }, + { + "label": "6361726f6c406578616d706c652e636f6d", + "value": "6361726f6c2d33" + }, + { + "label": "64617665406578616d706c652e636f6d", + "value": "646176652d31" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d32" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d33" + }, + { + "label": "6572696e406578616d706c652e636f6d", + "value": "6572696e2d34" + } + ] + } + ], + "note": "The owner advertises a tree size it has seen, so the view update sends §4.2's list rather than the whole frontier and the retained timestamps are omitted.", + "owner": { + "starting": 3, + "upcoming": [ + 4, + 5 + ], + "version_at_starting": 3 + }, + "position": 6, + "signature_public_key": "e2fe2a39b7326642827d6d82d497622cd6c297fe78270eaa436b01f086b6f3cb", + "tree_size": 7, + "versions": 1, + "vrf_public_key": "555f469c22188e90015bdd85b889678d459f86bdf8f7bd34971b10b9b3858609" + }, + "expect": { + "contact": { + "position": 6, + "version": 6 + }, + "distinguished": false, + "inclusion": [], + "prefix_proofs": [ + { + "elements": [ + "1def0b73f6a7447344e8b97e7ca705a6d1fa9676a1806706490e16b00cde24f8", + "0a46326b80aeadaa953ed8c74f7537697a0f6ed85e17df8376f745d2fe82d87c", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "cd67bb83420c504e576fdb6ddeae051b7d6854ae4ed49821117b080564342834", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0000000000000000000000000000000000000000000000000000000000000000", + "0ce100640be4bded834783b37cddc5205f9ca62ede791a7950391efb7876f341", + "a769cb5ef8d9b2f38fa5f5e7b7a7e483dc156437e989468d13dfd019b50e4e4b", + "984deed0558df270302baa5886c47a1a3e6ac6c6ac207f61f1f27be9ad37bf97", + "2231d8d602907328bc9640440cbfe08ecdc76fe50fe9afbf0d94c5419c8b60e0", + "61de2a3aa99e014eb484a49981294b330d246a424800dceffbcf724b4acfa6ae" + ], + "encoding": "020284c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf6663e061ef850b4b8f1f39618f93d746771544b54445342b13e7b98ce1acf1740105010b000e1def0b73f6a7447344e8b97e7ca705a6d1fa9676a1806706490e16b00cde24f80a46326b80aeadaa953ed8c74f7537697a0f6ed85e17df8376f745d2fe82d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd67bb83420c504e576fdb6ddeae051b7d6854ae4ed49821117b0805643428340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce100640be4bded834783b37cddc5205f9ca62ede791a7950391efb7876f341a769cb5ef8d9b2f38fa5f5e7b7a7e483dc156437e989468d13dfd019b50e4e4b984deed0558df270302baa5886c47a1a3e6ac6c6ac207f61f1f27be9ad37bf972231d8d602907328bc9640440cbfe08ecdc76fe50fe9afbf0d94c5419c8b60e061de2a3aa99e014eb484a49981294b330d246a424800dceffbcf724b4acfa6ae", + "results": [ + { + "depth": 5, + "leaf": { + "commitment": "63e061ef850b4b8f1f39618f93d746771544b54445342b13e7b98ce1acf17401", + "vrf_output": "84c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf66" + }, + "result_type": 2 + }, + { + "depth": 11, + "result_type": 1 + } + ] + } + ], + "prefix_roots": [ + "f58754a6457eaa33ddd7f26909308d955a447f2d7cc58f2a40b1e0fd250dae0d" + ], + "proof": "020000019fb36a1a980000019fb36a1a9901020284c552e49bfdd74786d8b71e7729bcbaab61fdcab6e9277d907d327e7a8edf6663e061ef850b4b8f1f39618f93d746771544b54445342b13e7b98ce1acf1740105010b000e1def0b73f6a7447344e8b97e7ca705a6d1fa9676a1806706490e16b00cde24f80a46326b80aeadaa953ed8c74f7537697a0f6ed85e17df8376f745d2fe82d87c000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000cd67bb83420c504e576fdb6ddeae051b7d6854ae4ed49821117b0805643428340000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ce100640be4bded834783b37cddc5205f9ca62ede791a7950391efb7876f341a769cb5ef8d9b2f38fa5f5e7b7a7e483dc156437e989468d13dfd019b50e4e4b984deed0558df270302baa5886c47a1a3e6ac6c6ac207f61f1f27be9ad37bf972231d8d602907328bc9640440cbfe08ecdc76fe50fe9afbf0d94c5419c8b60e061de2a3aa99e014eb484a49981294b330d246a424800dceffbcf724b4acfa6ae01f58754a6457eaa33ddd7f26909308d955a447f2d7cc58f2a40b1e0fd250dae0d0000", + "timestamps": [ + 1785421503128, + 1785421503129 + ] + } + } + ] +} diff --git a/interop/vectors/vrf-p256.json b/interop/vectors/vrf-p256.json new file mode 100644 index 0000000..02a0d6f --- /dev/null +++ b/interop/vectors/vrf-p256.json @@ -0,0 +1,165 @@ +{ + "primitive": "vrf", + "draft": "draft-ietf-keytrans-protocol-05 §11.7", + "generator": { + "impl": "katie", + "sha": "00da52541f6ae6a7f3905181e2ba9de8ec0d6cdc" + }, + "cipher_suite": 1, + "notes": "ECVRF-P256-SHA256-TAI (RFC 9381) over the presentation-language encoding of a VrfInput. Unlike the Ed25519 suite there is no truncation — beta_string is already VRF.Nh = 32 bytes — the integers are big-endian, an encoded point is 33 bytes SEC1 compressed, and VRF.Np is 81. `vrf_input` is that encoding, which is what alpha_string must be; `output` is the prefix tree search key for the label-version pair; `proof` is VRF.Np bytes. The RFC's own Appendix B vectors pin the ECVRF core and are run directly by the Rust side; these pin the KT wrapping around it. The negative case is a proof for one label-version pair checked against another, which must not verify.", + "cases": [ + { + "name": "empty-label-version-0", + "input": { + "label": "", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 0 + }, + "expect": { + "output": "cd01d0f2166a3f1c73556fdd6d6e1c042dc6fb0e3dccd7a0a8a85ccbd94ffa4e", + "proof": "02362c043cf8d2149835f5d557939d8d399f375aae9c8182e89d2a47273e85248f196c9c0419e1e09748724131ca0610df20500285fc279659f71b0a5ab6cb19dddc20c2f42057e9dbd432fee386a5b0cf", + "vrf_input": "0000000000" + } + }, + { + "name": "simple", + "input": { + "label": "616c696365406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 0 + }, + "expect": { + "output": "a5f48cc6ba2a41ca68814238453ee27270d98ffd7b18be7fa0ebffc2ab4d288e", + "proof": "02245033f5e9ba3fe7f1507c260e5b489c7f8f111b662de157fd8b944bb8768ae626ae4c9700a266cd039aa0d8ac04bbacddb5ec331a79bd06369ca72d1e8402e001f551aef98057c217c2f1500b5088f6", + "vrf_input": "11616c696365406578616d706c652e636f6d00000000" + } + }, + { + "name": "version-1", + "input": { + "label": "616c696365406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 1 + }, + "expect": { + "output": "4c6729d86a5d94fc6e479d8e4de46ad1452d1db5270dc6acaea89d4d69420a9e", + "proof": "0341e953d1706e8fe8a3b88a86d7941a37d3825cd89b9c06a5bd5bd42ebb0b57b1cdc73fea4a921eb7c5b0cb07829ace998d28f52b530c25335196b0e45860167012b43f2e633b8fce40860cb1685dd641", + "vrf_input": "11616c696365406578616d706c652e636f6d00000001" + } + }, + { + "name": "version-2", + "input": { + "label": "616c696365406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 2 + }, + "expect": { + "output": "5bb60034632d90508d1944aa71801386bbeaa85d46b2384d1390f34531c33cc7", + "proof": "02658f10c694f82745a65798f2840e090c25c765169b88e7f0fc9314ccd10085e0a8c1c81e5a937215616c85cddd286fbe2f0e9b33810795837e7b1ce7d56c298adac20dca132890cd8c456d8c07db1db2", + "vrf_input": "11616c696365406578616d706c652e636f6d00000002" + } + }, + { + "name": "version-max", + "input": { + "label": "616c696365406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 4294967295 + }, + "expect": { + "output": "c376c57a0d0e291cbedf7c600dbafd8b1ff85e0602fefcd208984c6cb59522be", + "proof": "03ab636e495e054fed810fc2a78c840243025b0a196fd37a5239e2979bb96ee65ef71d5188d1db2b901dfe7e750ad5715a871183d58dbbb95b8533610920905bb307fff590f34eb1f1504037b4ff0ef9f5", + "vrf_input": "11616c696365406578616d706c652e636f6dffffffff" + } + }, + { + "name": "other-label-same-version", + "input": { + "label": "626f62406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 0 + }, + "expect": { + "output": "f954bdf5823b56fe9e9c62775a8c8a4fda828325d0781074e847bd9d4c155a6f", + "proof": "0313b4077862347b19eb342dcbed439d934f34b5a32cd424af1879af49f6cfa21b2cf75277605f3bfd671f653034ed928b0846aee25d6ac24e0063e5e4b81be48db945d1d8cc498e70e8a0847be8bfcc61", + "vrf_input": "0f626f62406578616d706c652e636f6d00000000" + } + }, + { + "name": "label-a", + "input": { + "label": "61", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 1644167168 + }, + "expect": { + "output": "52779f2544d3c003221c370e9878e8782b49386ff95f8ae4363c263e096264e5", + "proof": "02a11755eab8f6612200f131f61e34effcba6850b40efb662afc55ede98ffa891c32a8cdcb21099e7be4d2896c2353a6a40cc385915e336280e9c98071fb95ae980d5ba25e1f37acc03a8c75e0e4d16dc6", + "vrf_input": "016162000000" + } + }, + { + "name": "label-ab", + "input": { + "label": "6162", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 0 + }, + "expect": { + "output": "50aa65e91d45d6445870918813dae73ed6e51c692e89ff7a6fb431b8b02571f5", + "proof": "0222c4460c14fe1bea8148d23b685a127dff1adbf442d12504796dcb7ca58034cb216041f3ddcbd4e7d88c42ffa1da661004d18e959c239e7e08ec1cd81be9b11eb0d97aacff037d6883f996824669b3e6", + "vrf_input": "02616200000000" + } + }, + { + "name": "label-max-len", + "input": { + "label": "616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 7 + }, + "expect": { + "output": "945a6b7ca1d74e7c6b3e40a1e58c991a12cc186b5addb92f9bd2eddbee60a756", + "proof": "029ad0af06d0af0184477dd4979f183806a9a772df2d9792ddeb76e412a794162a2934cab0c4cc66b862eb8e13cd76b6eab035f974643a3a0ceafe7bceae770b78a3d9846c044b4515822ef21ba4ea5281", + "vrf_input": "ff61616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616161616100000007" + } + }, + { + "name": "label-with-nulls", + "input": { + "label": "000100ff", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 3 + }, + "expect": { + "output": "c5298bbb917dfc45085454316c8174945250b7ba7a755bc675706511fbb2e62e", + "proof": "0217e71bfabe8745a81832a72bfb711e7e0fef7c95354c8547e2402db899b59109dceaf399285d13da76d8c1b2e23b904f91b1febd1f33dacb1b3c94317935175e19552997eea65f4606326e90d4467879", + "vrf_input": "04000100ff00000003" + } + }, + { + "name": "proof-for-another-version-does-not-verify", + "input": { + "label": "616c696365406578616d706c652e636f6d", + "private_key": "5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a5a", + "proof": "02245033f5e9ba3fe7f1507c260e5b489c7f8f111b662de157fd8b944bb8768ae626ae4c9700a266cd039aa0d8ac04bbacddb5ec331a79bd06369ca72d1e8402e001f551aef98057c217c2f1500b5088f6", + "public_key": "03f68b1de0f5ce686192f4bd0fd34163863f2e89d385f8bcfb85979533a57ac9b0", + "version": 1 + }, + "expect": { + "error": true + } + } + ] +} diff --git a/interop/vectors/vrf.json b/interop/vectors/vrf.json index ca6d6d0..3eeec5c 100644 --- a/interop/vectors/vrf.json +++ b/interop/vectors/vrf.json @@ -6,7 +6,7 @@ "sha": "00da52541f6ae6a7f3905181e2ba9de8ec0d6cdc" }, "cipher_suite": 2, - "notes": "ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381) over the presentation-language encoding of a VrfInput, with the output truncated to VRF.Nh = 32 bytes per §17.1. `vrf_input` is that encoding, which is what alpha_string must be; `output` is the prefix tree search key for the label-version pair; `proof` is VRF.Np = 80 bytes. The RFC's own Appendix B vectors pin the ECVRF core and are run directly by the Rust side; these pin the KT wrapping around it. The negative case is a proof for one label-version pair checked against another, which must not verify.", + "notes": "ECVRF-EDWARDS25519-SHA512-TAI (RFC 9381) over the presentation-language encoding of a VrfInput, with the output truncated to VRF.Nh = 32 bytes per §17.1. `vrf_input` is that encoding, which is what alpha_string must be; `output` is the prefix tree search key for the label-version pair; `proof` is VRF.Np bytes. The RFC's own Appendix B vectors pin the ECVRF core and are run directly by the Rust side; these pin the KT wrapping around it. The negative case is a proof for one label-version pair checked against another, which must not verify.", "cases": [ { "name": "empty-label-version-0", diff --git a/upstream/draft-protocol b/upstream/draft-protocol index 51e65e0..12121fd 160000 --- a/upstream/draft-protocol +++ b/upstream/draft-protocol @@ -1 +1 @@ -Subproject commit 51e65e0510853471c0b440b9d238044d30a0e6b2 +Subproject commit 12121fd0ea6b969f02de18fb5d02ba0cb87a1d1d