6. add(test): add more tests for height and transaction location serialization (#3879)

* Add round-trip test for OutputLocation disk serialization

* Make the transaction snapshot tests more accurate

Previously, we were showing the genesis transaction hash at height 0, index 0.

But its hash is actually not stored by location in the database,
because the genesis transaction is skipped due to a consensus rule.

* Update the transaction snapshot data

* Add history tree snapshot tests

At the current test heights, the history trees are all empty.

* Add the history tree snapshot data

* Update comments

* Simplify snapshot test code

* Make some serde::Serialize impls test-only, so we can hex-encode them

This should also speed up release compilation a bit.

* Add snapshot test code for UTXOs

* Add snapshot data for UTXOs
This commit is contained in:
teor 2022-03-19 06:30:16 +10:00 committed by GitHub
parent 9a8ab9468d
commit b4deca2912
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 490 additions and 134 deletions

View File

@ -9,7 +9,8 @@ edition = "2021"
[features]
default = []
proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chacha", "tokio"]
proptest-impl = ["proptest", "proptest-derive", "zebra-test", "rand", "rand_chacha", "tokio",
"hex/serde"]
bench = ["zebra-test"]
[dependencies]
@ -76,6 +77,8 @@ itertools = "0.10.3"
spandoc = "0.2.1"
tracing = "0.1.31"
hex = { version = "0.4.3", features = ["serde"] }
proptest = "0.10.1"
proptest-derive = "0.3.0"
rand = { version = "0.8.5", package = "rand" }

View File

@ -28,7 +28,18 @@ type Result<T, E = Error> = std::result::Result<T, E>;
#[derive(Clone, Copy, Serialize, Deserialize)]
#[serde(try_from = "i64")]
#[serde(bound = "C: Constraint")]
pub struct Amount<C = NegativeAllowed>(i64, PhantomData<C>);
pub struct Amount<C = NegativeAllowed>(
/// The inner amount value.
i64,
/// Used for [`Constraint`] type inference.
///
/// # Correctness
///
/// This internal Zebra marker type is not consensus-critical.
/// And it should be ignored during testing. (And other internal uses.)
#[serde(skip)]
PhantomData<C>,
);
impl<C> std::fmt::Debug for Amount<C> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {

View File

@ -14,7 +14,7 @@ pub mod arbitrary;
#[cfg(any(test, feature = "bench", feature = "proptest-impl"))]
pub mod tests;
use std::{collections::HashMap, convert::TryInto, fmt, ops::Neg};
use std::{collections::HashMap, fmt, ops::Neg};
pub use commitment::{
ChainHistoryBlockTxAuthCommitmentHash, ChainHistoryMmrRootHash, Commitment, CommitmentError,
@ -27,8 +27,6 @@ pub use serialize::{SerializedBlock, MAX_BLOCK_BYTES};
#[cfg(any(test, feature = "proptest-impl"))]
pub use arbitrary::LedgerState;
use serde::{Deserialize, Serialize};
use crate::{
amount::NegativeAllowed,
block::merkle::AuthDataRoot,
@ -44,7 +42,8 @@ use crate::{
};
/// A Zcash block, containing a header and a list of transactions.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Serialize))]
pub struct Block {
/// The block header, containing block metadata.
pub header: Header,

View File

@ -1,7 +1,8 @@
//! Transactions and transaction-related structures.
use std::{collections::HashMap, fmt, iter};
use halo2::pasta::pallas;
use serde::{Deserialize, Serialize};
mod auth_digest;
mod hash;
@ -40,8 +41,6 @@ use crate::{
value_balance::{ValueBalance, ValueBalanceError},
};
use std::{collections::HashMap, fmt, iter};
/// A Zcash transaction.
///
/// A transaction is an encoded data structure that facilitates the transfer of
@ -53,7 +52,8 @@ use std::{collections::HashMap, fmt, iter};
/// Zcash has a number of different transaction formats. They are represented
/// internally by different enum variants. Because we checkpoint on Canopy
/// activation, we do not validate any pre-Sapling transaction types.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Serialize))]
// XXX consider boxing the Optional fields of V4 and V5 txs
#[allow(clippy::large_enum_variant)]
pub enum Transaction {

View File

@ -48,7 +48,8 @@ use std::{collections::HashMap, fmt, iter};
pub const MIN_TRANSPARENT_COINBASE_MATURITY: u32 = 100;
/// Arbitrary data inserted by miners into a coinbase transaction.
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Serialize))]
pub struct CoinbaseData(
/// Invariant: this vec, together with the coinbase height, must be less than
/// 100 bytes. We enforce this by only constructing CoinbaseData fields by
@ -92,10 +93,16 @@ impl std::fmt::Debug for CoinbaseData {
/// OutPoint
///
/// A particular transaction output reference.
#[derive(Copy, Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Serialize))]
pub struct OutPoint {
/// References the transaction that contains the UTXO being spent.
///
/// # Correctness
///
/// Consensus-critical serialization uses [`ZcashSerialize`].
/// [`serde`]-based hex serialization must only be used for testing.
#[cfg_attr(any(test, feature = "proptest-impl"), serde(with = "hex"))]
pub hash: transaction::Hash,
/// Identifies which UTXO from that transaction is referenced; the
@ -103,8 +110,25 @@ pub struct OutPoint {
pub index: u32,
}
impl OutPoint {
/// Returns a new OutPoint from an in-memory output `index`.
///
/// # Panics
///
/// If `index` doesn't fit in a [`u32`].
pub fn from_usize(hash: transaction::Hash, index: usize) -> OutPoint {
OutPoint {
hash,
index: index
.try_into()
.expect("valid in-memory output indexes fit in a u32"),
}
}
}
/// A transparent input to a transaction.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Serialize))]
pub enum Input {
/// A reference to an output of a previous transaction.
PrevOut {
@ -289,8 +313,8 @@ impl Input {
/// I only own one UTXO worth 2 ZEC, I would construct a transaction
/// that spends my UTXO and sends 1 ZEC to you and 1 ZEC back to me
/// (just like receiving change).
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary))]
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
#[cfg_attr(any(test, feature = "proptest-impl"), derive(Arbitrary, Serialize))]
pub struct Output {
/// Transaction value.
// At https://en.bitcoin.it/wiki/Protocol_documentation#tx, this is an i64.

View File

@ -7,12 +7,19 @@ use crate::serialization::{
};
/// An encoding of a Bitcoin script.
#[derive(Clone, Eq, PartialEq, Serialize, Deserialize, Hash)]
#[derive(Clone, Eq, PartialEq, Hash)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
derive(proptest_derive::Arbitrary, serde::Serialize)
)]
pub struct Script(Vec<u8>);
pub struct Script(
/// # Correctness
///
/// Consensus-critical serialization uses [`ZcashSerialize`].
/// [`serde`]-based hex serialization must only be used for testing.
#[cfg_attr(any(test, feature = "proptest-impl"), serde(with = "hex"))]
Vec<u8>,
);
impl Script {
/// Create a new Bitcoin script from its raw bytes.

View File

@ -12,7 +12,7 @@ use crate::{
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(
any(test, feature = "proptest-impl"),
derive(proptest_derive::Arbitrary)
derive(proptest_derive::Arbitrary, serde::Serialize)
)]
pub struct Utxo {
/// The output itself.

View File

@ -35,12 +35,15 @@ zebra-test = { path = "../zebra-test/", optional = true }
[dev-dependencies]
color-eyre = "0.6.0"
once_cell = "1.10.0"
insta = { version = "1.13.0", features = ["ron"] }
itertools = "0.10.3"
once_cell = "1.10.0"
spandoc = "0.2.1"
hex = { version = "0.4.3", features = ["serde"] }
insta = { version = "1.13.0", features = ["ron"] }
proptest = "0.10.1"
proptest-derive = "0.3.0"
spandoc = "0.2.1"
# TODO: replace w/ crate version when released: https://github.com/ZcashFoundation/zebra/issues/2083
# Note: if updating this, also update the workspace Cargo.toml to match.

View File

@ -10,9 +10,16 @@ use zebra_chain::{
};
use crate::service::finalized_state::{
arbitrary::assert_value_properties, disk_format::TransactionLocation,
arbitrary::assert_value_properties,
disk_format::{transparent::OutputLocation, TransactionLocation},
};
#[test]
fn roundtrip_block_height() {
zebra_test::init();
proptest!(|(val in any::<block::Height>())| assert_value_properties(val));
}
#[test]
fn roundtrip_transaction_location() {
zebra_test::init();
@ -20,15 +27,15 @@ fn roundtrip_transaction_location() {
}
#[test]
fn roundtrip_block_hash() {
fn roundtrip_output_location() {
zebra_test::init();
proptest!(|(val in any::<block::Hash>())| assert_value_properties(val));
proptest!(|(val in any::<OutputLocation>())| assert_value_properties(val));
}
#[test]
fn roundtrip_block_height() {
fn roundtrip_block_hash() {
zebra_test::init();
proptest!(|(val in any::<block::Height>())| assert_value_properties(val));
proptest!(|(val in any::<block::Hash>())| assert_value_properties(val));
}
#[test]

View File

@ -102,12 +102,17 @@ impl ZebraDb {
// Read transaction methods
/// Returns the [`TransactionLocation`] for [`transaction::Hash`],
/// if it exists in the finalized chain.
pub fn transaction_location(&self, hash: transaction::Hash) -> Option<TransactionLocation> {
let tx_by_hash = self.db.cf_handle("tx_by_hash").unwrap();
self.db.zs_get(tx_by_hash, &hash)
}
/// Returns the [`Transaction`] with [`transaction::Hash`],
/// if it exists in the finalized chain.
pub fn transaction(&self, hash: transaction::Hash) -> Option<Arc<Transaction>> {
let tx_by_hash = self.db.cf_handle("tx_by_hash").unwrap();
self.db
.zs_get(tx_by_hash, &hash)
self.transaction_location(hash)
.map(|TransactionLocation { index, height }| {
let block = self
.block(height.into())

View File

@ -29,27 +29,26 @@
//! cargo insta test --review --delete-unreferenced-snapshots
//! ```
//! to update the test snapshots, then commit the `test_*.snap` files using git.
//!
//! # TODO
//!
//! Test the rest of the shielded data,
//! and data activated in Overwinter and later network upgrades.
use std::sync::Arc;
use serde::{Deserialize, Serialize};
use serde::Serialize;
use zebra_chain::{
block::{self, Block, Height},
block::{self, Block, Height, SerializedBlock},
orchard,
parameters::Network::{self, *},
sapling,
serialization::{ZcashDeserializeInto, ZcashSerialize},
transaction::Transaction,
transaction::{self, Transaction},
transparent,
};
use crate::{
service::finalized_state::{disk_format::TransactionLocation, FinalizedState},
service::finalized_state::{
disk_format::{block::TransactionIndex, transparent::OutputLocation, TransactionLocation},
FinalizedState,
},
Config,
};
@ -57,7 +56,7 @@ use crate::{
///
/// This structure snapshots the height and hash on separate lines,
/// which looks good for a single entry.
#[derive(Clone, Debug, Eq, PartialEq, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
struct Tip {
height: u32,
block_hash: String,
@ -76,28 +75,25 @@ impl From<(Height, block::Hash)> for Tip {
///
/// This structure is used to snapshot the height and hash on the same line,
/// which looks good for a vector of heights and hashes.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize)]
struct BlockHash(String);
/// Block data structure for RON snapshots.
///
/// This structure is used to snapshot the height and block data on separate lines,
/// which looks good for a vector of heights and block data.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
struct BlockData {
height: u32,
block: String,
#[serde(with = "hex")]
block: SerializedBlock,
}
impl BlockData {
pub fn new(height: Height, block: &Block) -> BlockData {
let block = block
.zcash_serialize_to_vec()
.expect("serialization of stored block succeeds");
BlockData {
height: height.0,
block: hex::encode(block),
block: block.into(),
}
}
}
@ -106,18 +102,19 @@ impl BlockData {
///
/// This structure is used to snapshot the location and transaction hash on separate lines,
/// which looks good for a vector of locations and transaction hashes.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
struct TransactionHash {
loc: TransactionLocation,
hash: String,
#[derive(Clone, Debug, Eq, PartialEq, Serialize)]
struct TransactionHashByLocation {
loc: Option<TransactionLocation>,
#[serde(with = "hex")]
hash: transaction::Hash,
}
impl TransactionHash {
pub fn new(loc: TransactionLocation, transaction: &Transaction) -> TransactionHash {
TransactionHash {
loc,
hash: transaction.hash().to_string(),
}
impl TransactionHashByLocation {
pub fn new(
loc: Option<TransactionLocation>,
hash: transaction::Hash,
) -> TransactionHashByLocation {
TransactionHashByLocation { loc, hash }
}
}
@ -125,9 +122,12 @@ impl TransactionHash {
///
/// This structure is used to snapshot the location and transaction data on separate lines,
/// which looks good for a vector of locations and transaction data.
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize)]
#[derive(Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Serialize)]
struct TransactionData {
loc: TransactionLocation,
// TODO: after #3145, replace with:
// #[serde(with = "hex")]
// transaction: SerializedTransaction,
transaction: String,
}
@ -177,6 +177,8 @@ fn test_block_and_transaction_data_with_network(network: Network) {
};
// We limit the number of blocks, because the serialized data is a few kilobytes per block.
//
// TODO: Test data activated in Overwinter and later network upgrades.
for height in 0..=2 {
let block: Arc<Block> = blocks
.get(&height)
@ -223,10 +225,22 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
let mut stored_transaction_hashes = Vec::new();
let mut stored_transactions = Vec::new();
let mut stored_utxos = Vec::new();
let sapling_tree_at_tip = state.sapling_note_commitment_tree();
let orchard_tree_at_tip = state.orchard_note_commitment_tree();
// Test the history tree.
//
// TODO: test non-empty history trees, using Heartwood or later blocks.
// test the rest of the chain data (value balance).
let history_tree_at_tip = state.history_tree();
for query_height in 0..=max_height.0 {
let query_height = Height(query_height);
// Check block height, block hash, and block database queries.
// Check all the block column families,
// using block height, block hash, and block database queries.
let stored_block_hash = state
.hash(query_height)
.expect("heights up to tip have hashes");
@ -237,14 +251,15 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
.block(query_height.into())
.expect("heights up to tip have blocks");
// Check the sapling and orchard note commitment trees.
//
// TODO: test the rest of the shielded data (anchors, nullifiers, sprout)
let sapling_tree_by_height = state
.sapling_note_commitment_tree_by_height(&query_height)
.expect("heights up to tip have Sapling trees");
let orchard_tree_by_height = state
.orchard_note_commitment_tree_by_height(&query_height)
.expect("heights up to tip have Orchard trees");
let sapling_tree_at_tip = state.sapling_note_commitment_tree();
let orchard_tree_at_tip = state.db.orchard_note_commitment_tree();
// We don't need to snapshot the heights,
// because they are fully determined by the tip and block hashes.
@ -257,6 +272,12 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
assert_eq!(sapling_tree_at_tip, sapling_tree_by_height);
assert_eq!(orchard_tree_at_tip, orchard_tree_by_height);
// Skip these checks for empty history trees.
if let Some(history_tree_at_tip) = history_tree_at_tip.as_ref() {
assert_eq!(history_tree_at_tip.current_height(), max_height);
assert_eq!(history_tree_at_tip.network(), state.network());
}
}
assert_eq!(
@ -277,11 +298,55 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
let transaction = &stored_block.transactions[tx_index];
let transaction_location = TransactionLocation::from_usize(query_height, tx_index);
let transaction_hash = TransactionHash::new(transaction_location, transaction);
let transaction_hash = transaction.hash();
let transaction_data = TransactionData::new(transaction_location, transaction);
stored_transaction_hashes.push(transaction_hash);
// Check all the transaction column families,
// using transaction location queries.
let stored_transaction_location = state.transaction_location(transaction_hash);
// Consensus: the genesis transaction is not indexed.
if query_height.0 > 0 {
assert_eq!(stored_transaction_location, Some(transaction_location));
} else {
assert_eq!(stored_transaction_location, None);
}
let stored_transaction_hash =
TransactionHashByLocation::new(stored_transaction_location, transaction_hash);
stored_transaction_hashes.push(stored_transaction_hash);
stored_transactions.push(transaction_data);
for output_index in 0..stored_block.transactions[tx_index].outputs().len() {
let output = &stored_block.transactions[tx_index].outputs()[output_index];
let outpoint =
transparent::OutPoint::from_usize(transaction_hash, output_index);
let output_location =
OutputLocation::from_usize(transaction_hash, output_index);
let stored_utxo = state.utxo(&outpoint);
if let Some(stored_utxo) = &stored_utxo {
assert_eq!(&stored_utxo.output, output);
assert_eq!(stored_utxo.height, query_height);
assert_eq!(
stored_utxo.from_coinbase,
transaction_location.index == TransactionIndex::from_usize(0),
"coinbase transactions must be the first transaction in a block:\n\
from_coinbase was: {from_coinbase},\n\
but transaction index was: {tx_index},\n\
at: {transaction_location:?},\n\
{output_location:?}",
from_coinbase = stored_utxo.from_coinbase,
);
}
// TODO: use output_location in #3151
stored_utxos.push((outpoint, stored_utxo));
}
}
}
@ -291,13 +356,6 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
"unsorted: {:?}",
stored_block_hashes
);
assert!(is_sorted(&stored_blocks), "unsorted: {:?}", stored_blocks);
assert!(
is_sorted(&stored_transaction_hashes),
"unsorted: {:?}",
stored_transaction_hashes
);
assert!(
is_sorted(&stored_transactions),
"unsorted: {:?}",
@ -310,14 +368,19 @@ fn snapshot_block_and_transaction_data(state: &FinalizedState) {
insta::assert_ron_snapshot!("block_hashes", stored_block_hashes);
insta::assert_ron_snapshot!("blocks", stored_blocks);
insta::assert_ron_snapshot!("transaction_hashes", stored_transaction_hashes);
insta::assert_ron_snapshot!("transactions", stored_transactions);
insta::assert_ron_snapshot!("utxos", stored_utxos);
// These snapshots will change if the trees do not have cached roots.
// But we expect them to always have cached roots,
// because those roots are used to populate the anchor column families.
insta::assert_ron_snapshot!("sapling_trees", stored_sapling_trees);
insta::assert_ron_snapshot!("orchard_trees", stored_orchard_trees);
insta::assert_ron_snapshot!("transaction_hashes", stored_transaction_hashes);
insta::assert_ron_snapshot!("transactions", stored_transactions);
// The zcash_history types used in this tree don't support serde.
insta::assert_debug_snapshot!("history_tree", (max_height, history_tree_at_tip));
}
}

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
0,
),
HistoryTree(
None,
),
)

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
1,
),
HistoryTree(
None,
),
)

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
2,
),
HistoryTree(
None,
),
)

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
0,
),
HistoryTree(
None,
),
)

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
1,
),
HistoryTree(
None,
),
)

View File

@ -0,0 +1,12 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: "(max_height, history_tree_at_tip)"
---
(
Height(
2,
),
HistoryTree(
None,
),
)

View File

@ -1,15 +1,10 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
]

View File

@ -1,22 +1,17 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(1),
index: TransactionIndex(0),
),
)),
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
),
]

View File

@ -1,29 +1,24 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(1),
index: TransactionIndex(0),
),
)),
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(2),
index: TransactionIndex(0),
),
)),
hash: "8974d08d1c5f9c860d8b629d582a56659a4a1dcb2b5f98a25a5afcc2a784b0f4",
),
]

View File

@ -1,15 +1,10 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
]

View File

@ -1,22 +1,17 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(1),
index: TransactionIndex(0),
),
)),
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
),
]

View File

@ -1,29 +1,24 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
assertion_line: 274
expression: stored_transaction_hashes
---
[
TransactionHash(
loc: TransactionLocation(
height: Height(0),
index: TransactionIndex(0),
),
TransactionHashByLocation(
loc: None,
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(1),
index: TransactionIndex(0),
),
)),
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
),
TransactionHash(
loc: TransactionLocation(
TransactionHashByLocation(
loc: Some(TransactionLocation(
height: Height(2),
index: TransactionIndex(0),
),
)),
hash: "5822c0532da8a008259ac39933d3210e508c17e3ba21d2b2c428785efdccb3d5",
),
]

View File

@ -0,0 +1,10 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
]

View File

@ -0,0 +1,32 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
(OutPoint(
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(50000),
lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(12500),
lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"),
),
height: Height(1),
from_coinbase: true,
))),
]

View File

@ -0,0 +1,54 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
(OutPoint(
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(50000),
lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "851bf6fbf7a976327817c738c489d7fa657752445430922d94c983c0b9ed4609",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(12500),
lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "8974d08d1c5f9c860d8b629d582a56659a4a1dcb2b5f98a25a5afcc2a784b0f4",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(100000),
lock_script: Script("21027a46eb513588b01b37ea24303f4b628afd12cc20df789fede0921e43cad3e875ac"),
),
height: Height(2),
from_coinbase: true,
))),
(OutPoint(
hash: "8974d08d1c5f9c860d8b629d582a56659a4a1dcb2b5f98a25a5afcc2a784b0f4",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(25000),
lock_script: Script("a9147d46a730d31f97b1930d3368a967c309bd4d136a87"),
),
height: Height(2),
from_coinbase: true,
))),
]

View File

@ -0,0 +1,10 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
]

View File

@ -0,0 +1,32 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
(OutPoint(
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(50000),
lock_script: Script("21025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99ac"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(12500),
lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"),
),
height: Height(1),
from_coinbase: true,
))),
]

View File

@ -0,0 +1,54 @@
---
source: zebra-state/src/service/finalized_state/zebra_db/block/tests/snapshot.rs
expression: stored_utxos
---
[
(OutPoint(
hash: "c4eaa58879081de3c24a7b117ed2b28300e7ec4c4c1dff1d3f1268b7857a4ddb",
index: 0,
), None),
(OutPoint(
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(50000),
lock_script: Script("21025229e1240a21004cf8338db05679fa34753706e84f6aebba086ba04317fd8f99ac"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "f37e9f691fffb635de0999491d906ee85ba40cd36dae9f6e5911a8277d7c5f75",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(12500),
lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"),
),
height: Height(1),
from_coinbase: true,
))),
(OutPoint(
hash: "5822c0532da8a008259ac39933d3210e508c17e3ba21d2b2c428785efdccb3d5",
index: 0,
), Some(Utxo(
output: Output(
value: Amount(100000),
lock_script: Script("2102acce9f6c16986c525fd34759d851ef5b4b85b5019a57bd59747be0ef1ba62523ac"),
),
height: Height(2),
from_coinbase: true,
))),
(OutPoint(
hash: "5822c0532da8a008259ac39933d3210e508c17e3ba21d2b2c428785efdccb3d5",
index: 1,
), Some(Utxo(
output: Output(
value: Amount(25000),
lock_script: Script("a914ef775f1f997f122a062fff1a2d7443abd1f9c64287"),
),
height: Height(2),
from_coinbase: true,
))),
]