Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion zebra-chain/src/parallel/commitment_aux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ use std::io;
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};

use crate::{
block, orchard, sapling,
block::{self, merkle::AuthDataRoot},
orchard, sapling,
serialization::{SerializationError, ZcashDeserialize, ZcashSerialize},
};

Expand All @@ -33,13 +34,26 @@ pub struct BlockCommitmentRoots {
pub sapling_root: sapling::tree::Root,
/// The Orchard note-commitment tree root as of the end of this block (empty below NU5).
pub orchard_root: orchard::tree::Root,
/// The authorizing-data root (ZIP-244 `hashAuthDataRoot`) of *this* block's own
/// transactions.
///
/// Carried so a recipient can authenticate the *predecessor's* note-commitment
/// roots against this block's NU5+ header commitment
/// (`hashBlockCommitments = BLAKE2b(chainHistoryRoot ‖ authDataRoot ‖ 0)`) without
/// downloading this block's body. Like the other roots it carries no trust: it is
/// only the co-input to a hash check against a checkpoint-committed header, so a
/// wrong value fails verification rather than being accepted. Default/zero below
/// NU5, where the header commits the chain-history root directly and this field is
/// unused.
pub auth_data_root: AuthDataRoot,
}

impl ZcashSerialize for BlockCommitmentRoots {
fn zcash_serialize<W: io::Write>(&self, mut writer: W) -> Result<(), io::Error> {
writer.write_u32::<LittleEndian>(self.height.0)?;
self.sapling_root.zcash_serialize(&mut writer)?;
self.orchard_root.zcash_serialize(&mut writer)?;
writer.write_all(&<[u8; 32]>::from(self.auth_data_root))?;
Ok(())
}
}
Expand All @@ -52,10 +66,14 @@ impl ZcashDeserialize for BlockCommitmentRoots {
let height = block::Height(reader.read_u32::<LittleEndian>()?);
let sapling_root = sapling::tree::Root::zcash_deserialize(&mut reader)?;
let orchard_root = orchard::tree::Root::zcash_deserialize(&mut reader)?;
let mut auth_data_root = [0u8; 32];
reader.read_exact(&mut auth_data_root)?;
let auth_data_root = AuthDataRoot::from(auth_data_root);
Ok(BlockCommitmentRoots {
height,
sapling_root,
orchard_root,
auth_data_root,
})
}
}
Expand All @@ -71,6 +89,7 @@ mod tests {
height: block::Height(1_687_200),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: AuthDataRoot::from([7u8; 32]),
};

let bytes = roots
Expand Down
8 changes: 4 additions & 4 deletions zebra-network/src/zakura/handler.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ const _: () =
assert!(LEGACY_REQUEST_STREAM_KIND == super::legacy_gossip::ZAKURA_STREAM_LEGACY_REQUESTS);
const _: () = assert!(DISCOVERY_STREAM_KIND == super::discovery::ZAKURA_STREAM_DISCOVERY);
const _: () = assert!(HEADER_SYNC_STREAM_KIND == super::header_sync::ZAKURA_STREAM_HEADER_SYNC);
const _: () = assert!(ZAKURA_STREAM_VERSION_4 == ZAKURA_HEADER_SYNC_STREAM_VERSION);
const _: () = assert!(ZAKURA_STREAM_VERSION_5 == ZAKURA_HEADER_SYNC_STREAM_VERSION);
const _: () =
assert!(LEGACY_REQUEST_BLOCKS_BY_HASH == super::legacy_gossip::MSG_REQUEST_BLOCKS_BY_HASH);
const _: () = assert!(
Expand Down Expand Up @@ -4122,7 +4122,7 @@ fn should_run_freshness_reaper(
/// The only stream-kind version this v1 handler serves. Every known kind is
/// at version 1; a peer naming any other version of a known kind is rejected.
const ZAKURA_STREAM_VERSION_1: u16 = 1;
const ZAKURA_STREAM_VERSION_4: u16 = 4;
const ZAKURA_STREAM_VERSION_5: u16 = 5;

/// Returns whether the handler can serve a stream with this kind and version.
///
Expand Down Expand Up @@ -6481,7 +6481,7 @@ mod tests {
},
Stream {
kind: HEADER_SYNC_STREAM_KIND,
version: ZAKURA_STREAM_VERSION_4,
version: ZAKURA_STREAM_VERSION_5,
frame_cap: 1024,
capability: ZAKURA_CAP_HEADER_SYNC,
mode: StreamMode::Ordered,
Expand All @@ -6501,7 +6501,7 @@ mod tests {
(LEGACY_GOSSIP_STREAM_KIND, ZAKURA_STREAM_VERSION_1),
(LEGACY_REQUEST_STREAM_KIND, ZAKURA_STREAM_VERSION_1),
(DISCOVERY_STREAM_KIND, ZAKURA_STREAM_VERSION_1),
(HEADER_SYNC_STREAM_KIND, ZAKURA_STREAM_VERSION_4),
(HEADER_SYNC_STREAM_KIND, ZAKURA_STREAM_VERSION_5),
(ZAKURA_STREAM_BLOCK_SYNC, ZAKURA_STREAM_VERSION_1),
] {
assert!(
Expand Down
1 change: 1 addition & 0 deletions zebra-network/src/zakura/header_sync/pipe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -608,6 +608,7 @@ mod tests {
height: block::Height(1),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: block::merkle::AuthDataRoot::from([0u8; 32]),
}],
}
.encode_frame()
Expand Down
1 change: 1 addition & 0 deletions zebra-network/src/zakura/header_sync/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ fn root_at(height: block::Height) -> BlockCommitmentRoots {
height,
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: block::merkle::AuthDataRoot::from([0u8; 32]),
}
}

Expand Down
10 changes: 7 additions & 3 deletions zebra-network/src/zakura/header_sync/wire.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ pub const ZAKURA_STREAM_HEADER_SYNC: u16 = 5;
/// Version of the native header-sync stream.
///
/// Version 4 carries one tree-aux root for each non-empty range header.
pub const ZAKURA_HEADER_SYNC_STREAM_VERSION: u16 = 4;
/// Version 5 extends each tree-aux root with the block's ZIP-244 `auth_data_root`
/// (the co-input needed to authenticate the predecessor's note-commitment roots
/// against this block's NU5+ header commitment). This is a breaking wire change:
/// a v4 and a v5 node cannot exchange header-sync ranges.
pub const ZAKURA_HEADER_SYNC_STREAM_VERSION: u16 = 5;

/// Peer status advertisement.
pub const MSG_HS_STATUS: u8 = 1;
Expand All @@ -29,8 +33,8 @@ pub(super) const HEADER_SYNC_MESSAGE_TYPE_BYTES: usize = 1;
pub(super) const HEADER_SYNC_COUNT_BYTES: usize = 4;
pub(super) const HEADER_SYNC_HAS_ROOTS_BYTES: usize = 1;
pub(super) const HEADER_SYNC_BODY_SIZE_BYTES: usize = 4;
/// Encoded [`BlockCommitmentRoots`]: height + Sapling root + Orchard root.
pub(super) const HEADER_SYNC_BLOCK_COMMITMENT_ROOTS_BYTES: usize = 4 + 32 + 32;
/// Encoded [`BlockCommitmentRoots`]: height + Sapling root + Orchard root + auth-data root.
pub(super) const HEADER_SYNC_BLOCK_COMMITMENT_ROOTS_BYTES: usize = 4 + 32 + 32 + 32;
pub(super) const COMMON_HEADER_BYTES: usize = 1_487;
pub(super) const REGTEST_HEADER_BYTES: usize = 177;
pub(super) const HEADER_SYNC_FANOUT: usize = 3;
Expand Down
1 change: 1 addition & 0 deletions zebra-network/src/zakura/testkit/cluster.rs
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,7 @@ mod tests {
height,
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: block::merkle::AuthDataRoot::from([0u8; 32]),
}
}

Expand Down
5 changes: 5 additions & 0 deletions zebra-replay-bench/src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@ pub fn run_roots(
height,
sapling_root: sapling.root(),
orchard_root: orchard.root(),
auth_data_root: state
.db
.block(height.into())
.map(|block| block.auth_data_root())
.unwrap_or_else(|| zebra_chain::block::merkle::AuthDataRoot::from([0u8; 32])),
});
let done = h - start + 1;
if done.is_multiple_of(5000) || done == total {
Expand Down
7 changes: 7 additions & 0 deletions zebra-state/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Extended value-pool disk serialization with an Ironwood slot after the deferred pool, and
consolidated the current verified-commitment-trees state database format changes under
version `27.3.0`.
- Extended the `commitment_roots_by_height` serving index (and the `tree_aux` header-sync
payload it is served from) with each block's per-height ZIP-244 `auth_data_root`, so a node
can serve the co-input a peer needs to authenticate a block's note-commitment roots against
its successor's NU5+ header commitment. Consolidated under database format version `27.3.0`
(no version bump); serving-index rows from an earlier pre-release build (64 bytes, no
auth-data root) remain readable. Carrying the auth-data root bumps the Zakura header-sync
stream format to version 5, a breaking wire change (all peers must run the new format).
- Added the `vct_upgrade_metadata` column family, recording the upgrade height `U` (the lowest
height this binary committed). `tree_aux` root serving now stitches the per-height trees below
`U` with the serving index at and above `U`, so a node that upgraded mid-chain serves a range
Expand Down
13 changes: 10 additions & 3 deletions zebra-state/src/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,13 +95,20 @@ const DATABASE_FORMAT_VERSION: u64 = 27;
/// Version 3 adds the verified-commitment-trees state format:
/// - the `fast_sync_metadata` column family, which records fast-sync handoff state,
/// - the `commitment_roots_by_height` serving index (design §4), a compact per-height
/// `(sapling_root, orchard_root)` map every node writes so a fast-synced node can serve
/// `tree_aux` roots without per-height trees, and
/// `(sapling_root, orchard_root, auth_data_root)` map every node writes so a fast-synced node
/// can serve `tree_aux` roots without per-height trees. The per-height ZIP-244 `auth_data_root`
/// is the co-input needed to authenticate a block's note-commitment roots against its
/// successor's NU5+ header commitment without re-reading the successor body, and
/// - the on-open repair for incompatible stored history-tree bytes before background format
/// checks read the tip tree.
///
/// New databases populate the serving index going forward; existing ones open with it empty and
/// serve from per-height trees as before.
/// serve from per-height trees as before. Serving-index rows written by an earlier pre-release
/// build of this version (without the `auth_data_root`, 64 instead of 96 bytes) decode with a
/// zero auth-data root (compatibility code in
/// [`disk_format::shielded::CommitmentRootsByHeight`]'s `FromDisk`), and heights with such a row
/// fall back to the body-wait commit path until they are re-served with a real root — so this
/// remains a single consolidated pre-release version-3 format.
const DATABASE_FORMAT_MINOR_VERSION: u64 = 3;

/// The database format patch version, incremented each time the on-disk database format has a
Expand Down
9 changes: 9 additions & 0 deletions zebra-state/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1531,6 +1531,15 @@ where
height,
sapling_root: sapling.root(),
orchard_root: orchard.root(),
// The non-finalized chain holds the full block, so derive its
// ZIP-244 auth-data root to serve as the predecessor-authentication
// co-input (zero only if the block is unexpectedly absent).
auth_data_root: chain
.block(height.into())
.map(|block| block.block.auth_data_root())
.unwrap_or_else(|| {
zebra_chain::block::merkle::AuthDataRoot::from([0u8; 32])
}),
}),
_ => None,
}
Expand Down
16 changes: 15 additions & 1 deletion zebra-state/src/service/finalized_state/commitment_aux.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ use std::{
};

use thiserror::Error;
use zebra_chain::{block, orchard, sapling, sprout};
use zebra_chain::{
block::{self, merkle::AuthDataRoot},
orchard, sapling, sprout,
};

use super::{FromDisk, IntoDisk, ZebraDb};

Expand Down Expand Up @@ -527,6 +530,14 @@ pub(crate) fn produce_block_roots(
height,
sapling_root: sapling.root(),
orchard_root: orchard.root(),
// Below the upgrade height the serving index does not exist, so derive the
// auth-data root from the locally stored block (this archival node holds the
// body for these heights). Zero only if the body is somehow absent, in which
// case the recipient simply re-fetches from a node that has it.
auth_data_root: db
.block(height.into())
.map(|block| block.auth_data_root())
.unwrap_or_else(|| AuthDataRoot::from([0u8; 32])),
});
}
roots
Expand Down Expand Up @@ -652,11 +663,13 @@ mod tests {
height: block::Height(10),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: AuthDataRoot::from([0u8; 32]),
},
BlockCommitmentRoots {
height: block::Height(11),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: AuthDataRoot::from([0u8; 32]),
},
];
let roots = roots
Expand Down Expand Up @@ -698,6 +711,7 @@ mod tests {
height: block::Height(42),
sapling_root: sapling::tree::NoteCommitmentTree::default().root(),
orchard_root: orchard::tree::NoteCommitmentTree::default().root(),
auth_data_root: AuthDataRoot::from([0u8; 32]),
}]);

assert!(
Expand Down
29 changes: 24 additions & 5 deletions zebra-state/src/service/finalized_state/disk_format/shielded.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
use bincode::Options;

use zebra_chain::{
block::Height,
block::{merkle::AuthDataRoot, Height},
orchard, sapling, sprout,
subtree::{NoteCommitmentSubtreeData, NoteCommitmentSubtreeIndex},
};
Expand Down Expand Up @@ -100,25 +100,44 @@ pub struct CommitmentRootsByHeight {
pub sapling: sapling::tree::Root,
/// The Orchard note-commitment tree root at this height.
pub orchard: orchard::tree::Root,
/// The ZIP-244 authorizing-data root (`hashAuthDataRoot`) of this block's
/// transactions. Stored alongside the note-commitment roots so this node can
/// serve it as the co-input needed to authenticate the *predecessor's* roots
/// against this block's NU5+ header commitment, without re-reading the body.
/// Default/zero below NU5.
pub auth_data_root: AuthDataRoot,
}

impl IntoDisk for CommitmentRootsByHeight {
type Bytes = [u8; 64];
type Bytes = [u8; 96];

fn as_bytes(&self) -> Self::Bytes {
let mut out = [0u8; 64];
let mut out = [0u8; 96];
out[..32].copy_from_slice(&IntoDisk::as_bytes(&self.sapling));
out[32..].copy_from_slice(&IntoDisk::as_bytes(&self.orchard));
out[32..64].copy_from_slice(&IntoDisk::as_bytes(&self.orchard));
out[64..].copy_from_slice(&<[u8; 32]>::from(self.auth_data_root));
out
}
}

impl FromDisk for CommitmentRootsByHeight {
fn from_bytes(bytes: impl AsRef<[u8]>) -> Self {
let bytes = bytes.as_ref();
// Backwards compatible with the pre-auth-data 64-byte rows written by an earlier
// pre-release build of this database version: those decode with a zero auth-data
// root, so the writer falls back to the body-wait path for those heights until
// they are re-served with a real root. New rows are 96 bytes.
let auth_data_root = if bytes.len() >= 96 {
let mut auth_data_root = [0u8; 32];
auth_data_root.copy_from_slice(&bytes[64..96]);
AuthDataRoot::from(auth_data_root)
} else {
AuthDataRoot::from([0u8; 32])
};
CommitmentRootsByHeight {
sapling: sapling::tree::Root::from_bytes(&bytes[..32]),
orchard: orchard::tree::Root::from_bytes(&bytes[32..]),
orchard: orchard::tree::Root::from_bytes(&bytes[32..64]),
auth_data_root,
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000001",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000001",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000002",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000001",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,14 @@ expression: cf_data
[
KV(
k: "000000",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000001",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
KV(
k: "000002",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82f",
v: "fbc2f4300c01f0b7820d00e3347c8da4ee614674376cbc45359daa54f9b5493eae2935f1dfd8a24aed7c70df7de3a668eb7a49b1319880dde2bbd9031ae5d82fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
),
]
Loading
Loading