Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
3973bce
move logic that will happen before the continuous sync loop above the…
Oscar-Pepper Aug 6, 2026
cfdf8d8
Merge branch 'dev' into continuous_sync
Oscar-Pepper Aug 12, 2026
f499059
cont. moving sync initialisation out of continuous sync loop
Oscar-Pepper Aug 13, 2026
36886e0
implement the core of continuous sync
Oscar-Pepper Aug 13, 2026
b29f1e6
add note on mempool logic improvement
Oscar-Pepper Aug 13, 2026
2f5592b
finish core continuous sync implementation
Oscar-Pepper Aug 14, 2026
8735629
move mempool shutdown logic back into drain verdict and update to lat…
Oscar-Pepper Aug 14, 2026
f8a6d79
fix errors and warnings and adjust shutdown logic ordering
Oscar-Pepper Aug 20, 2026
65814a0
fix incorrect update scan ranges ordering and protect from unnecessar…
Oscar-Pepper Aug 21, 2026
7145dd3
debug test failure. move setting found note ranges below transparent …
Oscar-Pepper Aug 21, 2026
309c7d0
clean up shutdown
Oscar-Pepper Aug 21, 2026
2527325
move initial sync state to avoid overflow errors and incorrect sync s…
Oscar-Pepper Aug 21, 2026
556e740
revert sync status changes
Oscar-Pepper Aug 22, 2026
29ae787
remove tracing subscriber from fixed test
Oscar-Pepper Aug 22, 2026
2cb1704
resolve merge conflicts with latest dev
Oscar-Pepper Aug 22, 2026
7da5631
finish merge with latest dev
Oscar-Pepper Aug 22, 2026
c9c752a
fix build errors and improve error handling in add_subtree_roots test
Oscar-Pepper Aug 22, 2026
6f44735
fix add_subtree_roots test
Oscar-Pepper Aug 22, 2026
a8a1e9e
remove dbg and add todo
Oscar-Pepper Aug 22, 2026
704987b
fix issue with re-org while chain tip is still being scanned
Oscar-Pepper Aug 23, 2026
8e400cd
solve issues with wallet tree bounds updates and verification issues …
Oscar-Pepper Aug 23, 2026
4f2f45f
move chain tip prioritization to after subtree roots have been fetched
Oscar-Pepper Aug 23, 2026
5c23022
fix issue where scan range was stuck in scanning when waiting for cha…
Oscar-Pepper Aug 23, 2026
89c6ec1
skip instead of error if scan target txid not found
Oscar-Pepper Aug 23, 2026
c8aa38b
fix issue where upper bound may be missing when resetting back to iro…
Oscar-Pepper Aug 24, 2026
fac3173
add new block checks when mempool stream closes
Oscar-Pepper Aug 24, 2026
2ba1387
add checks for incorrect shard ranges
Oscar-Pepper Aug 24, 2026
b0c9516
add transparent scanning of compact blocks
Oscar-Pepper Aug 25, 2026
0576d34
continue implementing compact block taddr scanning
Oscar-Pepper Aug 25, 2026
cea28f8
finish compact block scanning for taddrs
Oscar-Pepper Aug 26, 2026
319edc0
fix script errors
Oscar-Pepper Aug 27, 2026
db47282
now mempool triggers new blocks check, extend the interval and reset …
Oscar-Pepper Aug 27, 2026
3980ddc
write test for gap taddr chain tip continuous scanning in compact blocks
Oscar-Pepper Aug 28, 2026
0d46dd2
move gap loop logic into scan_compact_blocks
Oscar-Pepper Sep 2, 2026
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
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ zcash_primitives = { version = "0.30.0", features = ["non-standard-fees"] }
zcash_proofs = "0.30.0"
zcash_protocol = "0.10.4" # public API (TODO: remove from public API)
zcash_transparent = "0.10.0"
zcash_script = "0.4.5"

### Blockchain Protocol
bip0039 = { version = "0.14", features = ["rand"] }
Expand Down
9 changes: 3 additions & 6 deletions libtonode-tests/tests/concrete.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1690,7 +1690,6 @@ async fn mine_to_transparent_coinbase_maturity() {
}

mod testnet_test {
use pepper_sync::sync_status;
use zingo_test_vectors::seeds::HOSPITAL_MUSEUM_SEED;
use zingolib::{
config::{ChainType, ClientConfig, WalletConfig},
Expand Down Expand Up @@ -1730,11 +1729,9 @@ mod testnet_test {
let mut interval = tokio::time::interval(std::time::Duration::from_millis(100));
interval.set_missed_tick_behavior(tokio::time::MissedTickBehavior::Delay);
interval.tick().await;
while sync_status(&*lightclient.wallet().read().await)
.await
.unwrap()
.percentage_total_outputs_scanned
> 1.0
while lightclient
.latest_sync_status()
.is_none_or(|status| status.percentage_total_outputs_scanned < 1.0)
{
interval.tick().await;
}
Expand Down
81 changes: 27 additions & 54 deletions libtonode-tests/tests/sync.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{num::NonZeroU32, time::Duration};

use incrementalmerkletree::Position;
use pepper_sync::error::SyncError;
use pepper_sync::sync::ScanPriority;
use pepper_sync::test_support::block;
use pepper_sync::wallet::ShardTrees;
Expand All @@ -15,6 +16,8 @@ use zingo_test_vectors::seeds::HOSPITAL_MUSEUM_SEED;
use zingolib::config::{ChainType, ClientConfig, WalletConfig};
use zingolib::data::PollReport;
use zingolib::lightclient::DEFAULT_REQUEST_TIMEOUT;
use zingolib::lightclient::error::LightClientError;
use zingolib::sync::SyncModeError;
use zingolib::testutils::default_test_wallet_settings;
use zingolib::testutils::lightclient::from_inputs::quick_send;
use zingolib::testutils::paths::get_cargo_manifest_dir;
Expand All @@ -32,57 +35,7 @@ use zingolib_testutils::scenarios::{
self, IndexerConvergence, increase_height_and_wait_for_client,
};

#[ignore = "temporary mainnet test for sync development"]
#[tokio::test]
async fn sync_mainnet_test() {
zingolib::ensure_default_crypto_provider();
tracing_subscriber::fmt().init();

let uri = construct_indexer_uri(SYNC_TEST_INDEXER.to_string()).unwrap();
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path().to_path_buf();
let config = ClientConfig::builder()
.set_indexer_uri(uri.clone())
.set_chain_type(ChainType::Mainnet)
.set_wallet_dir(temp_path)
.set_wallet_config(WalletConfig::MnemonicPhrase {
mnemonic_phrase: HOSPITAL_MUSEUM_SEED.to_string(),
no_of_accounts: NonZeroU32::try_from(1).expect("hard-coded integer"),
birthday: 1_500_000,
wallet_settings: default_test_wallet_settings(),
})
.build()
.unwrap();
let mut lightclient = LightClient::new(config, true).await.unwrap();

lightclient.sync().await.unwrap();
let mut interval = tokio::time::interval(zingo_netutils::time::test::SETTLE_POLL_INTERVAL);
loop {
interval.tick().await;
{
let wallet = lightclient.wallet().read().await;
tracing::info!(
"{}",
json::JsonValue::from(pepper_sync::sync_status(&*wallet).await.unwrap())
);
tracing::info!("WALLET DEBUG:");
tracing::info!("uas: {}", wallet.unified_addresses().len());
tracing::info!("taddrs: {}", wallet.transparent_addresses().len());
tracing::info!("blocks: {}", wallet.wallet_blocks.len());
tracing::info!("txs: {}", wallet.wallet_transactions.len());
tracing::info!("nullifiers o: {}", wallet.nullifier_map.orchard.len());
tracing::info!("nullifiers s: {}", wallet.nullifier_map.sapling.len());
tracing::info!("outpoints: {}", wallet.outpoint_map.len());
}
lightclient.flush().await.unwrap();
}

// let wallet = lightclient.wallet.read().await;
// dbg!(&wallet.wallet_blocks);
// dbg!(&wallet.nullifier_map);
// dbg!(&wallet.sync_state);
}

// TODO: migrate to mock test as this test is long and connects to mainnet
#[tokio::test]
async fn add_subtree_roots() {
fn assert_subtree_roots_match_server(
Expand Down Expand Up @@ -230,11 +183,19 @@ async fn add_subtree_roots() {
tokio::time::sleep(Duration::from_secs(1)).await;
}
let _ = lightclient.stop_sync();
let _ = lightclient.await_sync().await;
match lightclient.await_sync().await {
Ok(_) => {}
Err(LightClientError::SyncError(SyncError::SyncModeError(
SyncModeError::SyncNotRunning,
)))
| Err(LightClientError::SyncNotRunning) => {}
Err(e) => {
panic!("{e}");
}
}

{
let shard_trees = &mut lightclient.wallet().write().await.shard_trees;

assert_subtree_roots_match_server(
shard_trees,
sapling_subtree_roots_server.clone(),
Expand All @@ -258,6 +219,9 @@ async fn add_subtree_roots() {
assert!(orchard_shard_addrs.len() != orchard_subtree_roots_server.len());
}

// must wait for a new block to be mined to trigger get_subtree_roots in sync
tokio::time::sleep(Duration::from_secs(100)).await;

lightclient.sync().await.unwrap();
while !(lightclient
.wallet()
Expand All @@ -272,7 +236,16 @@ async fn add_subtree_roots() {
tokio::time::sleep(Duration::from_secs(1)).await;
}
let _ = lightclient.stop_sync();
let _ = lightclient.await_sync().await;
match lightclient.await_sync().await {
Ok(_) => {}
Err(LightClientError::SyncError(SyncError::SyncModeError(
SyncModeError::SyncNotRunning,
)))
| Err(LightClientError::SyncNotRunning) => {}
Err(e) => {
panic!("{e}");
}
}

{
let shard_trees = &mut lightclient.wallet().write().await.shard_trees;
Expand Down
1 change: 1 addition & 0 deletions pepper-sync/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ zcash_note_encryption.workspace = true
zcash_primitives.workspace = true
zcash_protocol.workspace = true
zcash_transparent.workspace = true
zcash_script.workspace = true

# Protocol
bip32.workspace = true
Expand Down
18 changes: 9 additions & 9 deletions pepper-sync/src/client/fetch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,27 +102,27 @@ where
{
match fetch_request {
FetchRequest::ChainTip(sender) => {
tracing::debug!("Fetching chain tip.");
tracing::info!("Fetching chain tip.");
let block_id = get_latest_block(client).await;
let _ignore_error = sender.send(block_id);
}
FetchRequest::CompactBlock(sender, block_height) => {
tracing::debug!("Fetching compact block. {:?}", &block_height);
tracing::info!("Fetching compact block. {:?}", &block_height);
let block = get_block(client, block_height).await;
let _ignore_error = sender.send(block);
}
FetchRequest::CompactBlockRange(sender, block_range) => {
tracing::debug!("Fetching compact blocks. {:?}", &block_range);
tracing::info!("Fetching compact blocks. {:?}", &block_range);
let block_stream = get_block_range(client, block_range).await;
let _ignore_error = sender.send(block_stream);
}
FetchRequest::NullifierRange(sender, block_range) => {
tracing::debug!("Fetching nullifiers. {:?}", &block_range);
tracing::info!("Fetching nullifiers. {:?}", &block_range);
let block_stream = get_block_range_nullifiers(client, block_range).await;
let _ignore_error = sender.send(block_stream);
}
FetchRequest::SubtreeRoots(sender, start_index, shielded_protocol, max_entries) => {
tracing::debug!(
tracing::info!(
"Fetching subtree roots. start index: {}. shielded protocol: {}",
start_index,
shielded_protocol
Expand All @@ -132,17 +132,17 @@ where
let _ignore_error = sender.send(subtree_roots);
}
FetchRequest::TreeState(sender, block_height) => {
tracing::debug!("Fetching tree state. {:?}", &block_height);
tracing::info!("Fetching tree state. {:?}", &block_height);
let tree_state = get_tree_state(client, block_height).await;
let _ignore_error = sender.send(tree_state);
}
FetchRequest::Transaction(sender, txid) => {
tracing::debug!("Fetching transaction. {:?}", txid);
tracing::info!("Fetching transaction. {:?}", txid);
let transaction = get_transaction(client, txid).await;
let _ignore_error = sender.send(transaction);
}
FetchRequest::UtxoMetadata(sender, (addresses, start_height)) => {
tracing::debug!(
tracing::info!(
"Fetching unspent transparent output metadata from {:?} for addresses:\n{:?}",
&start_height,
&addresses
Expand All @@ -151,7 +151,7 @@ where
let _ignore_error = sender.send(utxo_metadata);
}
FetchRequest::TransparentAddressTxs(sender, (address, block_range)) => {
tracing::debug!(
tracing::info!(
"Fetching raw transactions in block range {:?} for address {:?}",
&block_range,
&address
Expand Down
23 changes: 20 additions & 3 deletions pepper-sync/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,12 +86,21 @@ pub struct SyncConfig {
pub transparent_address_discovery: TransparentAddressDiscovery,
/// Performance level
pub performance_level: PerformanceLevel,
/// Shutdown on completion
///
/// If not set, sync will not shutdown until the consumer sets the `SyncMode` to `Shutdown` variant.
/// The sync engine will regularly check for new blocks mined so the wallet will always be updated to the state
/// of the latest chain.
///
/// If set, sync will still check for any newly mined blocks during scanning. But when the wallet is completely
/// up-to-date with the latest chain, sync will shutdown.
pub shutdown_on_completion: bool,
}

#[cfg(feature = "wallet_essentials")]
impl SyncConfig {
fn serialized_version() -> u8 {
1
2
}

/// Deserialize into `reader`
Expand All @@ -101,10 +110,16 @@ impl SyncConfig {
let gap_limit = reader.read_u8()?;
let scopes = reader.read_u8()?;
let performance_level = if version >= 1 {
PerformanceLevel::read(reader)?
PerformanceLevel::read(&mut reader)?
} else {
PerformanceLevel::High
};
let shutdown_on_completion = if version >= 2 {
reader.read_u8()? != 0
} else {
false
};

Ok(Self {
transparent_address_discovery: TransparentAddressDiscovery {
gap_limit,
Expand All @@ -115,6 +130,7 @@ impl SyncConfig {
},
},
performance_level,
shutdown_on_completion,
})
}

Expand All @@ -133,7 +149,8 @@ impl SyncConfig {
scopes |= 0b100;
}
writer.write_u8(scopes)?;
self.performance_level.write(writer)?;
self.performance_level.write(&mut writer)?;
writer.write_u8(self.shutdown_on_completion as u8)?;

Ok(())
}
Expand Down
13 changes: 12 additions & 1 deletion pepper-sync/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ pub enum ScanError {
/// Continuity error.
#[error("continuity error")]
ContinuityError(#[from] ContinuityError),
/// Zcash client backend scan error
/// Invalid encoding.
#[error(transparent)]
EncodingError(#[from] EncodingInvalid),
/// Invalid sapling nullifier
Expand Down Expand Up @@ -298,6 +298,17 @@ pub enum ScanError {
/// Failed to parse encoded address.
#[error("failed to parse encoded address")]
AddressParseError(#[from] zcash_address::unified::ParseError),
/// Compact transaction contained transparent output with value outside the valid zatoshi range.
#[error(
"compact transaction contained transparent output with value {0} which is outside the valid zatoshi range"
)]
TransparentOutputInvalidValue(u64),
/// All transparent addresses are already in use.
#[error("all transparent addresses are already in use")]
AllAddressesInUse,
/// Transparent address derivation error.
#[error("transparent address derivation error. {0}")]
TransparentAddressDerivationError(bip32::Error),
}

/// The encoding of a compact Sapling output or compact Orchard action was invalid.
Expand Down
Loading
Loading