Merge bitcoindevkit/bdk#1229: Use a universal lookahead value for KeychainTxOutIndex
and have a reasonable default
1def76f1f17fc876534c798883099c58de57c0fd chore: make clippy happy and bump clippy msrv (志宇) c9467dcbb27bd497006846dcfcefb0c2cf8823c4 chore: improve documentation of lookahead (LLFourn) bc796f412acdc3d2cd96f7aec0f24fa47c1fe889 fix(example): bitcoind_rpc_polling now initializes local_chain properly (志宇) 4fd539b6470f7f771e3b5e09e3287952fa7a1825 feat(chain)!: `KeychainTxOutIndex` uses a universal lookahead (Antoine Poinsot) Pull request description: ### Description The `bdk::Wallet` is currently created without setting any lookahead value for the keychain. This implicitly makes it a lookahead of 0. As this is a high-level interface we should avoid footguns and aim for a reasonable default. To fix this, we have also decided to change `KeychainTxOutIndex` to have a default lookahead. Additionally, we have simplified the `KeychainTxOutIndex` API to have a single `lookahead` that is ONLY set at construction `KeychainTxOutIndex::new(lookahead: u32) -> Self`. This avoids the footguns of having methods which allows the caller to decrease the `lookahead` (which will panic). ### Notes to the reviewers ~A way to set this value externally is introduced in #1172. This PR only aims to use a saner default than 0. `1_000` is the value used by the Bitcoin Core wallet, and that seems reasonable to me.~ Edit: we should NOT allow setting the `lookahead` value after-the-fact. Instead, the `lookahead` should be provided to the wallet's constructor. @evanlinjin: I don't think additional tests are necessary as no additional features are added, and the surface area of the API is decreased. The original tests already thoroughly test the `lookahead` concept. ### Checklists #### All Submissions: *(This section was updated as this PR changed from being a simple setting to introducing a new method.)* * [x] I've signed all my commits * [x] I followed the [contribution guidelines](https://github.com/bitcoindevkit/bdk/blob/master/CONTRIBUTING.md) * [x] I ran `cargo fmt` and `cargo clippy` before committing ~* [ ] I've added tests~ * [x] I've added docs ACKs for top commit: LLFourn: ACK 1def76f1f17fc876534c798883099c58de57c0fd Tree-SHA512: b4c3be8a4f2ac4877cf3f05852147e7dd1daeb02d3bc40895f02fd2a58e584f1dc0735b524153ff0875380ac93c0b4c31e516873d7a9b0027fdbbb5fe7970ff2
This commit is contained in:
commit
7eff024213
2
.github/workflows/cont_integration.yml
vendored
2
.github/workflows/cont_integration.yml
vendored
@ -118,7 +118,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
- uses: actions-rs/toolchain@v1
|
- uses: actions-rs/toolchain@v1
|
||||||
with:
|
with:
|
||||||
toolchain: "stable"
|
toolchain: stable
|
||||||
components: clippy
|
components: clippy
|
||||||
override: true
|
override: true
|
||||||
- name: Rust Cache
|
- name: Rust Cache
|
||||||
|
@ -1 +1 @@
|
|||||||
msrv="1.57.0"
|
msrv="1.63.0"
|
||||||
|
@ -575,7 +575,7 @@ mod test {
|
|||||||
|
|
||||||
if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
|
if let ExtendedDescriptor::Pkh(pkh) = xdesc.0 {
|
||||||
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
|
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
|
||||||
let purpose = path.get(0).unwrap();
|
let purpose = path.first().unwrap();
|
||||||
assert_matches!(purpose, Hardened { index: 44 });
|
assert_matches!(purpose, Hardened { index: 44 });
|
||||||
let coin_type = path.get(1).unwrap();
|
let coin_type = path.get(1).unwrap();
|
||||||
assert_matches!(coin_type, Hardened { index: 0 });
|
assert_matches!(coin_type, Hardened { index: 0 });
|
||||||
@ -589,7 +589,7 @@ mod test {
|
|||||||
|
|
||||||
if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
|
if let ExtendedDescriptor::Pkh(pkh) = tdesc.0 {
|
||||||
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
|
let path: Vec<ChildNumber> = pkh.into_inner().full_derivation_path().unwrap().into();
|
||||||
let purpose = path.get(0).unwrap();
|
let purpose = path.first().unwrap();
|
||||||
assert_matches!(purpose, Hardened { index: 44 });
|
assert_matches!(purpose, Hardened { index: 44 });
|
||||||
let coin_type = path.get(1).unwrap();
|
let coin_type = path.get(1).unwrap();
|
||||||
assert_matches!(coin_type, Hardened { index: 1 });
|
assert_matches!(coin_type, Hardened { index: 1 });
|
||||||
|
@ -812,9 +812,10 @@ pub struct SignOptions {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Customize which taproot script-path leaves the signer should sign.
|
/// Customize which taproot script-path leaves the signer should sign.
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum TapLeavesOptions {
|
pub enum TapLeavesOptions {
|
||||||
/// The signer will sign all the leaves it has a key for.
|
/// The signer will sign all the leaves it has a key for.
|
||||||
|
#[default]
|
||||||
All,
|
All,
|
||||||
/// The signer won't sign leaves other than the ones specified. Note that it could still ignore
|
/// The signer won't sign leaves other than the ones specified. Note that it could still ignore
|
||||||
/// some of the specified leaves, if it doesn't have the right key to sign them.
|
/// some of the specified leaves, if it doesn't have the right key to sign them.
|
||||||
@ -825,12 +826,6 @@ pub enum TapLeavesOptions {
|
|||||||
None,
|
None,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TapLeavesOptions {
|
|
||||||
fn default() -> Self {
|
|
||||||
TapLeavesOptions::All
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(clippy::derivable_impls)]
|
#[allow(clippy::derivable_impls)]
|
||||||
impl Default for SignOptions {
|
impl Default for SignOptions {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
|
@ -811,9 +811,10 @@ impl<'a, D> TxBuilder<'a, D, DefaultCoinSelectionAlgorithm, BumpFee> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Ordering of the transaction's inputs and outputs
|
/// Ordering of the transaction's inputs and outputs
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Default, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
pub enum TxOrdering {
|
pub enum TxOrdering {
|
||||||
/// Randomized (default)
|
/// Randomized (default)
|
||||||
|
#[default]
|
||||||
Shuffle,
|
Shuffle,
|
||||||
/// Unchanged
|
/// Unchanged
|
||||||
Untouched,
|
Untouched,
|
||||||
@ -821,12 +822,6 @@ pub enum TxOrdering {
|
|||||||
Bip69Lexicographic,
|
Bip69Lexicographic,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TxOrdering {
|
|
||||||
fn default() -> Self {
|
|
||||||
TxOrdering::Shuffle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TxOrdering {
|
impl TxOrdering {
|
||||||
/// Sort transaction inputs and outputs by [`TxOrdering`] variant
|
/// Sort transaction inputs and outputs by [`TxOrdering`] variant
|
||||||
pub fn sort_tx(&self, tx: &mut Transaction) {
|
pub fn sort_tx(&self, tx: &mut Transaction) {
|
||||||
@ -880,9 +875,10 @@ impl RbfValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Policy regarding the use of change outputs when creating a transaction
|
/// Policy regarding the use of change outputs when creating a transaction
|
||||||
#[derive(Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
#[derive(Default, Debug, Ord, PartialOrd, Eq, PartialEq, Hash, Clone, Copy)]
|
||||||
pub enum ChangeSpendPolicy {
|
pub enum ChangeSpendPolicy {
|
||||||
/// Use both change and non-change outputs (default)
|
/// Use both change and non-change outputs (default)
|
||||||
|
#[default]
|
||||||
ChangeAllowed,
|
ChangeAllowed,
|
||||||
/// Only use change outputs (see [`TxBuilder::only_spend_change`])
|
/// Only use change outputs (see [`TxBuilder::only_spend_change`])
|
||||||
OnlyChange,
|
OnlyChange,
|
||||||
@ -890,12 +886,6 @@ pub enum ChangeSpendPolicy {
|
|||||||
ChangeForbidden,
|
ChangeForbidden,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for ChangeSpendPolicy {
|
|
||||||
fn default() -> Self {
|
|
||||||
ChangeSpendPolicy::ChangeAllowed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ChangeSpendPolicy {
|
impl ChangeSpendPolicy {
|
||||||
pub(crate) fn is_satisfied_by(&self, utxo: &LocalOutput) -> bool {
|
pub(crate) fn is_satisfied_by(&self, utxo: &LocalOutput) -> bool {
|
||||||
match self {
|
match self {
|
||||||
|
@ -5,12 +5,13 @@ use crate::{
|
|||||||
spk_iter::BIP32_MAX_INDEX,
|
spk_iter::BIP32_MAX_INDEX,
|
||||||
SpkIterator, SpkTxOutIndex,
|
SpkIterator, SpkTxOutIndex,
|
||||||
};
|
};
|
||||||
use alloc::vec::Vec;
|
|
||||||
use bitcoin::{OutPoint, Script, TxOut};
|
use bitcoin::{OutPoint, Script, TxOut};
|
||||||
use core::{fmt::Debug, ops::Deref};
|
use core::{fmt::Debug, ops::Deref};
|
||||||
|
|
||||||
use crate::Append;
|
use crate::Append;
|
||||||
|
|
||||||
|
const DEFAULT_LOOKAHEAD: u32 = 1_000;
|
||||||
|
|
||||||
/// A convenient wrapper around [`SpkTxOutIndex`] that relates script pubkeys to miniscript public
|
/// A convenient wrapper around [`SpkTxOutIndex`] that relates script pubkeys to miniscript public
|
||||||
/// [`Descriptor`]s.
|
/// [`Descriptor`]s.
|
||||||
///
|
///
|
||||||
@ -46,7 +47,7 @@ use crate::Append;
|
|||||||
/// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
/// # let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
||||||
/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
/// # let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
||||||
/// # let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
|
/// # let (internal_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/*)").unwrap();
|
||||||
/// # let descriptor_for_user_42 = external_descriptor.clone();
|
/// # let (descriptor_for_user_42, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/2/*)").unwrap();
|
||||||
/// txout_index.add_keychain(MyKeychain::External, external_descriptor);
|
/// txout_index.add_keychain(MyKeychain::External, external_descriptor);
|
||||||
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor);
|
/// txout_index.add_keychain(MyKeychain::Internal, internal_descriptor);
|
||||||
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42);
|
/// txout_index.add_keychain(MyKeychain::MyAppUser { user_id: 42 }, descriptor_for_user_42);
|
||||||
@ -65,17 +66,12 @@ pub struct KeychainTxOutIndex<K> {
|
|||||||
// last revealed indexes
|
// last revealed indexes
|
||||||
last_revealed: BTreeMap<K, u32>,
|
last_revealed: BTreeMap<K, u32>,
|
||||||
// lookahead settings for each keychain
|
// lookahead settings for each keychain
|
||||||
lookahead: BTreeMap<K, u32>,
|
lookahead: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<K> Default for KeychainTxOutIndex<K> {
|
impl<K> Default for KeychainTxOutIndex<K> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self::new(DEFAULT_LOOKAHEAD)
|
||||||
inner: SpkTxOutIndex::default(),
|
|
||||||
keychains: BTreeMap::default(),
|
|
||||||
last_revealed: BTreeMap::default(),
|
|
||||||
lookahead: BTreeMap::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,6 +114,25 @@ impl<K: Clone + Ord + Debug> Indexer for KeychainTxOutIndex<K> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<K> KeychainTxOutIndex<K> {
|
||||||
|
/// Construct a [`KeychainTxOutIndex`] with the given `lookahead`.
|
||||||
|
///
|
||||||
|
/// The `lookahead` is the number of script pubkeys to derive and cache from the internal
|
||||||
|
/// descriptors over and above the last revealed script index. Without a lookahead the index
|
||||||
|
/// will miss outputs you own when processing transactions whose output script pubkeys lie
|
||||||
|
/// beyond the last revealed index. In certain situations, such as when performing an initial
|
||||||
|
/// scan of the blockchain during wallet import, it may be uncertain or unknown what the index
|
||||||
|
/// of the last revealed script pubkey actually is.
|
||||||
|
pub fn new(lookahead: u32) -> Self {
|
||||||
|
Self {
|
||||||
|
inner: SpkTxOutIndex::default(),
|
||||||
|
keychains: BTreeMap::new(),
|
||||||
|
last_revealed: BTreeMap::new(),
|
||||||
|
lookahead,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
||||||
/// Return a reference to the internal [`SpkTxOutIndex`].
|
/// Return a reference to the internal [`SpkTxOutIndex`].
|
||||||
pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
|
pub fn inner(&self) -> &SpkTxOutIndex<(K, u32)> {
|
||||||
@ -145,54 +160,22 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|||||||
pub fn add_keychain(&mut self, keychain: K, descriptor: Descriptor<DescriptorPublicKey>) {
|
pub fn add_keychain(&mut self, keychain: K, descriptor: Descriptor<DescriptorPublicKey>) {
|
||||||
let old_descriptor = &*self
|
let old_descriptor = &*self
|
||||||
.keychains
|
.keychains
|
||||||
.entry(keychain)
|
.entry(keychain.clone())
|
||||||
.or_insert_with(|| descriptor.clone());
|
.or_insert_with(|| descriptor.clone());
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
&descriptor, old_descriptor,
|
&descriptor, old_descriptor,
|
||||||
"keychain already contains a different descriptor"
|
"keychain already contains a different descriptor"
|
||||||
);
|
);
|
||||||
|
self.replenish_lookahead(&keychain, self.lookahead);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return the lookahead setting for each keychain.
|
/// Get the lookahead setting.
|
||||||
///
|
///
|
||||||
/// Refer to [`set_lookahead`] for a deeper explanation of the `lookahead`.
|
/// Refer to [`new`] for more information on the `lookahead`.
|
||||||
///
|
///
|
||||||
/// [`set_lookahead`]: Self::set_lookahead
|
/// [`new`]: Self::new
|
||||||
pub fn lookaheads(&self) -> &BTreeMap<K, u32> {
|
pub fn lookahead(&self) -> u32 {
|
||||||
&self.lookahead
|
self.lookahead
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience method to call [`set_lookahead`] for all keychains.
|
|
||||||
///
|
|
||||||
/// [`set_lookahead`]: Self::set_lookahead
|
|
||||||
pub fn set_lookahead_for_all(&mut self, lookahead: u32) {
|
|
||||||
for keychain in &self.keychains.keys().cloned().collect::<Vec<_>>() {
|
|
||||||
self.set_lookahead(keychain, lookahead);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the lookahead count for `keychain`.
|
|
||||||
///
|
|
||||||
/// The lookahead is the number of scripts to cache ahead of the last revealed script index. This
|
|
||||||
/// is useful to find outputs you own when processing block data that lie beyond the last revealed
|
|
||||||
/// index. In certain situations, such as when performing an initial scan of the blockchain during
|
|
||||||
/// wallet import, it may be uncertain or unknown what the last revealed index is.
|
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// This will panic if the `keychain` does not exist.
|
|
||||||
pub fn set_lookahead(&mut self, keychain: &K, lookahead: u32) {
|
|
||||||
self.lookahead.insert(keychain.clone(), lookahead);
|
|
||||||
self.replenish_lookahead(keychain);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convenience method to call [`lookahead_to_target`] for multiple keychains.
|
|
||||||
///
|
|
||||||
/// [`lookahead_to_target`]: Self::lookahead_to_target
|
|
||||||
pub fn lookahead_to_target_multi(&mut self, target_indexes: BTreeMap<K, u32>) {
|
|
||||||
for (keychain, target_index) in target_indexes {
|
|
||||||
self.lookahead_to_target(&keychain, target_index)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Store lookahead scripts until `target_index`.
|
/// Store lookahead scripts until `target_index`.
|
||||||
@ -201,22 +184,14 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|||||||
pub fn lookahead_to_target(&mut self, keychain: &K, target_index: u32) {
|
pub fn lookahead_to_target(&mut self, keychain: &K, target_index: u32) {
|
||||||
let next_index = self.next_store_index(keychain);
|
let next_index = self.next_store_index(keychain);
|
||||||
if let Some(temp_lookahead) = target_index.checked_sub(next_index).filter(|&v| v > 0) {
|
if let Some(temp_lookahead) = target_index.checked_sub(next_index).filter(|&v| v > 0) {
|
||||||
let old_lookahead = self.lookahead.insert(keychain.clone(), temp_lookahead);
|
self.replenish_lookahead(keychain, temp_lookahead);
|
||||||
self.replenish_lookahead(keychain);
|
|
||||||
|
|
||||||
// revert
|
|
||||||
match old_lookahead {
|
|
||||||
Some(lookahead) => self.lookahead.insert(keychain.clone(), lookahead),
|
|
||||||
None => self.lookahead.remove(keychain),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replenish_lookahead(&mut self, keychain: &K) {
|
fn replenish_lookahead(&mut self, keychain: &K, lookahead: u32) {
|
||||||
let descriptor = self.keychains.get(keychain).expect("keychain must exist");
|
let descriptor = self.keychains.get(keychain).expect("keychain must exist");
|
||||||
let next_store_index = self.next_store_index(keychain);
|
let next_store_index = self.next_store_index(keychain);
|
||||||
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
|
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
|
||||||
let lookahead = self.lookahead.get(keychain).map_or(0, |v| *v);
|
|
||||||
|
|
||||||
for (new_index, new_spk) in
|
for (new_index, new_spk) in
|
||||||
SpkIterator::new_with_range(descriptor, next_store_index..next_reveal_index + lookahead)
|
SpkIterator::new_with_range(descriptor, next_store_index..next_reveal_index + lookahead)
|
||||||
@ -388,12 +363,8 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|||||||
|
|
||||||
let target_index = if has_wildcard { target_index } else { 0 };
|
let target_index = if has_wildcard { target_index } else { 0 };
|
||||||
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
|
let next_reveal_index = self.last_revealed.get(keychain).map_or(0, |v| *v + 1);
|
||||||
let lookahead = self.lookahead.get(keychain).map_or(0, |v| *v);
|
|
||||||
|
|
||||||
debug_assert_eq!(
|
debug_assert!(next_reveal_index + self.lookahead >= self.next_store_index(keychain));
|
||||||
next_reveal_index + lookahead,
|
|
||||||
self.next_store_index(keychain)
|
|
||||||
);
|
|
||||||
|
|
||||||
// if we need to reveal new indices, the latest revealed index goes here
|
// if we need to reveal new indices, the latest revealed index goes here
|
||||||
let mut reveal_to_index = None;
|
let mut reveal_to_index = None;
|
||||||
@ -401,12 +372,12 @@ impl<K: Clone + Ord + Debug> KeychainTxOutIndex<K> {
|
|||||||
// if the target is not yet revealed, but is already stored (due to lookahead), we need to
|
// if the target is not yet revealed, but is already stored (due to lookahead), we need to
|
||||||
// set the `reveal_to_index` as target here (as the `for` loop below only updates
|
// set the `reveal_to_index` as target here (as the `for` loop below only updates
|
||||||
// `reveal_to_index` for indexes that are NOT stored)
|
// `reveal_to_index` for indexes that are NOT stored)
|
||||||
if next_reveal_index <= target_index && target_index < next_reveal_index + lookahead {
|
if next_reveal_index <= target_index && target_index < next_reveal_index + self.lookahead {
|
||||||
reveal_to_index = Some(target_index);
|
reveal_to_index = Some(target_index);
|
||||||
}
|
}
|
||||||
|
|
||||||
// we range over indexes that are not stored
|
// we range over indexes that are not stored
|
||||||
let range = next_reveal_index + lookahead..=target_index + lookahead;
|
let range = next_reveal_index + self.lookahead..=target_index + self.lookahead;
|
||||||
for (new_index, new_spk) in SpkIterator::new_with_range(descriptor, range) {
|
for (new_index, new_spk) in SpkIterator::new_with_range(descriptor, range) {
|
||||||
let _inserted = self
|
let _inserted = self
|
||||||
.inner
|
.inner
|
||||||
|
@ -148,7 +148,7 @@ mod test {
|
|||||||
Descriptor<DescriptorPublicKey>,
|
Descriptor<DescriptorPublicKey>,
|
||||||
Descriptor<DescriptorPublicKey>,
|
Descriptor<DescriptorPublicKey>,
|
||||||
) {
|
) {
|
||||||
let mut txout_index = KeychainTxOutIndex::<TestKeychain>::default();
|
let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0);
|
||||||
|
|
||||||
let secp = Secp256k1::signing_only();
|
let secp = Secp256k1::signing_only();
|
||||||
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
||||||
|
@ -168,9 +168,7 @@ impl<I: Clone + Ord> SpkTxOutIndex<I> {
|
|||||||
///
|
///
|
||||||
/// Returns `None` if the `TxOut` hasn't been scanned or if nothing matching was found there.
|
/// Returns `None` if the `TxOut` hasn't been scanned or if nothing matching was found there.
|
||||||
pub fn txout(&self, outpoint: OutPoint) -> Option<(&I, &TxOut)> {
|
pub fn txout(&self, outpoint: OutPoint) -> Option<(&I, &TxOut)> {
|
||||||
self.txouts
|
self.txouts.get(&outpoint).map(|v| (&v.0, &v.1))
|
||||||
.get(&outpoint)
|
|
||||||
.map(|(spk_i, txout)| (spk_i, txout))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the script that has been inserted at the `index`.
|
/// Returns the script that has been inserted at the `index`.
|
||||||
|
@ -581,10 +581,7 @@ impl<A: Clone + Ord> TxGraph<A> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (outpoint, txout) in changeset.txouts {
|
for (outpoint, txout) in changeset.txouts {
|
||||||
let tx_entry = self
|
let tx_entry = self.txs.entry(outpoint.txid).or_default();
|
||||||
.txs
|
|
||||||
.entry(outpoint.txid)
|
|
||||||
.or_insert_with(Default::default);
|
|
||||||
|
|
||||||
match tx_entry {
|
match tx_entry {
|
||||||
(TxNodeInternal::Whole(_), _, _) => { /* do nothing since we already have full tx */
|
(TxNodeInternal::Whole(_), _, _) => { /* do nothing since we already have full tx */
|
||||||
@ -597,13 +594,13 @@ impl<A: Clone + Ord> TxGraph<A> {
|
|||||||
|
|
||||||
for (anchor, txid) in changeset.anchors {
|
for (anchor, txid) in changeset.anchors {
|
||||||
if self.anchors.insert((anchor.clone(), txid)) {
|
if self.anchors.insert((anchor.clone(), txid)) {
|
||||||
let (_, anchors, _) = self.txs.entry(txid).or_insert_with(Default::default);
|
let (_, anchors, _) = self.txs.entry(txid).or_default();
|
||||||
anchors.insert(anchor);
|
anchors.insert(anchor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (txid, new_last_seen) in changeset.last_seen {
|
for (txid, new_last_seen) in changeset.last_seen {
|
||||||
let (_, _, last_seen) = self.txs.entry(txid).or_insert_with(Default::default);
|
let (_, _, last_seen) = self.txs.entry(txid).or_default();
|
||||||
if new_last_seen > *last_seen {
|
if new_last_seen > *last_seen {
|
||||||
*last_seen = new_last_seen;
|
*last_seen = new_last_seen;
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
mod tx_template;
|
mod tx_template;
|
||||||
|
#[allow(unused_imports)]
|
||||||
pub use tx_template::*;
|
pub use tx_template::*;
|
||||||
|
|
||||||
#[allow(unused_macros)]
|
#[allow(unused_macros)]
|
||||||
|
@ -27,9 +27,10 @@ fn insert_relevant_txs() {
|
|||||||
let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
|
let spk_0 = descriptor.at_derivation_index(0).unwrap().script_pubkey();
|
||||||
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
|
let spk_1 = descriptor.at_derivation_index(9).unwrap().script_pubkey();
|
||||||
|
|
||||||
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::default();
|
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<()>>::new(
|
||||||
|
KeychainTxOutIndex::new(10),
|
||||||
|
);
|
||||||
graph.index.add_keychain((), descriptor);
|
graph.index.add_keychain((), descriptor);
|
||||||
graph.index.set_lookahead(&(), 10);
|
|
||||||
|
|
||||||
let tx_a = Transaction {
|
let tx_a = Transaction {
|
||||||
output: vec![
|
output: vec![
|
||||||
@ -118,12 +119,12 @@ fn test_list_owned_txouts() {
|
|||||||
let (desc_1, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/0/*)").unwrap();
|
let (desc_1, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/0/*)").unwrap();
|
||||||
let (desc_2, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/1/*)").unwrap();
|
let (desc_2, _) = Descriptor::parse_descriptor(&Secp256k1::signing_only(), "tr(tprv8ZgxMBicQKsPd3krDUsBAmtnRsK3rb8u5yi1zhQgMhF1tR8MW7xfE4rnrbbsrbPR52e7rKapu6ztw1jXveJSCGHEriUGZV7mCe88duLp5pj/86'/1'/0'/1/*)").unwrap();
|
||||||
|
|
||||||
let mut graph =
|
let mut graph = IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>::new(
|
||||||
IndexedTxGraph::<ConfirmationHeightAnchor, KeychainTxOutIndex<String>>::default();
|
KeychainTxOutIndex::new(10),
|
||||||
|
);
|
||||||
|
|
||||||
graph.index.add_keychain("keychain_1".into(), desc_1);
|
graph.index.add_keychain("keychain_1".into(), desc_1);
|
||||||
graph.index.add_keychain("keychain_2".into(), desc_2);
|
graph.index.add_keychain("keychain_2".into(), desc_2);
|
||||||
graph.index.set_lookahead_for_all(10);
|
|
||||||
|
|
||||||
// Get trusted and untrusted addresses
|
// Get trusted and untrusted addresses
|
||||||
|
|
||||||
|
@ -18,12 +18,14 @@ enum TestKeychain {
|
|||||||
Internal,
|
Internal,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init_txout_index() -> (
|
fn init_txout_index(
|
||||||
|
lookahead: u32,
|
||||||
|
) -> (
|
||||||
bdk_chain::keychain::KeychainTxOutIndex<TestKeychain>,
|
bdk_chain::keychain::KeychainTxOutIndex<TestKeychain>,
|
||||||
Descriptor<DescriptorPublicKey>,
|
Descriptor<DescriptorPublicKey>,
|
||||||
Descriptor<DescriptorPublicKey>,
|
Descriptor<DescriptorPublicKey>,
|
||||||
) {
|
) {
|
||||||
let mut txout_index = bdk_chain::keychain::KeychainTxOutIndex::<TestKeychain>::default();
|
let mut txout_index = bdk_chain::keychain::KeychainTxOutIndex::<TestKeychain>::new(lookahead);
|
||||||
|
|
||||||
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
let secp = bdk_chain::bitcoin::secp256k1::Secp256k1::signing_only();
|
||||||
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
let (external_descriptor,_) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "tr([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/0/*)").unwrap();
|
||||||
@ -46,7 +48,7 @@ fn spk_at_index(descriptor: &Descriptor<DescriptorPublicKey>, index: u32) -> Scr
|
|||||||
fn test_set_all_derivation_indices() {
|
fn test_set_all_derivation_indices() {
|
||||||
use bdk_chain::indexed_tx_graph::Indexer;
|
use bdk_chain::indexed_tx_graph::Indexer;
|
||||||
|
|
||||||
let (mut txout_index, _, _) = init_txout_index();
|
let (mut txout_index, _, _) = init_txout_index(0);
|
||||||
let derive_to: BTreeMap<_, _> =
|
let derive_to: BTreeMap<_, _> =
|
||||||
[(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
|
[(TestKeychain::External, 12), (TestKeychain::Internal, 24)].into();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -64,19 +66,10 @@ fn test_set_all_derivation_indices() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_lookahead() {
|
fn test_lookahead() {
|
||||||
let (mut txout_index, external_desc, internal_desc) = init_txout_index();
|
let (mut txout_index, external_desc, internal_desc) = init_txout_index(10);
|
||||||
|
|
||||||
// ensure it does not break anything if lookahead is set multiple times
|
|
||||||
(0..=10).for_each(|lookahead| txout_index.set_lookahead(&TestKeychain::External, lookahead));
|
|
||||||
(0..=20)
|
|
||||||
.filter(|v| v % 2 == 0)
|
|
||||||
.for_each(|lookahead| txout_index.set_lookahead(&TestKeychain::Internal, lookahead));
|
|
||||||
|
|
||||||
assert_eq!(txout_index.inner().all_spks().len(), 30);
|
|
||||||
|
|
||||||
// given:
|
// given:
|
||||||
// - external lookahead set to 10
|
// - external lookahead set to 10
|
||||||
// - internal lookahead set to 20
|
|
||||||
// when:
|
// when:
|
||||||
// - set external derivation index to value higher than last, but within the lookahead value
|
// - set external derivation index to value higher than last, but within the lookahead value
|
||||||
// expect:
|
// expect:
|
||||||
@ -97,7 +90,7 @@ fn test_lookahead() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
txout_index.inner().all_spks().len(),
|
txout_index.inner().all_spks().len(),
|
||||||
10 /* external lookahead */ +
|
10 /* external lookahead */ +
|
||||||
20 /* internal lookahead */ +
|
10 /* internal lookahead */ +
|
||||||
index as usize + 1 /* `derived` count */
|
index as usize + 1 /* `derived` count */
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -127,7 +120,7 @@ fn test_lookahead() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// given:
|
// given:
|
||||||
// - internal lookahead is 20
|
// - internal lookahead is 10
|
||||||
// - internal derivation index is `None`
|
// - internal derivation index is `None`
|
||||||
// when:
|
// when:
|
||||||
// - derivation index is set ahead of current derivation index + lookahead
|
// - derivation index is set ahead of current derivation index + lookahead
|
||||||
@ -148,7 +141,7 @@ fn test_lookahead() {
|
|||||||
assert_eq!(
|
assert_eq!(
|
||||||
txout_index.inner().all_spks().len(),
|
txout_index.inner().all_spks().len(),
|
||||||
10 /* external lookahead */ +
|
10 /* external lookahead */ +
|
||||||
20 /* internal lookahead */ +
|
10 /* internal lookahead */ +
|
||||||
20 /* external stored index count */ +
|
20 /* external stored index count */ +
|
||||||
25 /* internal stored index count */
|
25 /* internal stored index count */
|
||||||
);
|
);
|
||||||
@ -226,8 +219,7 @@ fn test_lookahead() {
|
|||||||
// - last used index should change as expected
|
// - last used index should change as expected
|
||||||
#[test]
|
#[test]
|
||||||
fn test_scan_with_lookahead() {
|
fn test_scan_with_lookahead() {
|
||||||
let (mut txout_index, external_desc, _) = init_txout_index();
|
let (mut txout_index, external_desc, _) = init_txout_index(10);
|
||||||
txout_index.set_lookahead_for_all(10);
|
|
||||||
|
|
||||||
let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
|
let spks: BTreeMap<u32, ScriptBuf> = [0, 10, 20, 30]
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@ -281,7 +273,7 @@ fn test_scan_with_lookahead() {
|
|||||||
#[test]
|
#[test]
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn test_wildcard_derivations() {
|
fn test_wildcard_derivations() {
|
||||||
let (mut txout_index, external_desc, _) = init_txout_index();
|
let (mut txout_index, external_desc, _) = init_txout_index(0);
|
||||||
let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
|
let external_spk_0 = external_desc.at_derivation_index(0).unwrap().script_pubkey();
|
||||||
let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
|
let external_spk_16 = external_desc.at_derivation_index(16).unwrap().script_pubkey();
|
||||||
let external_spk_26 = external_desc.at_derivation_index(26).unwrap().script_pubkey();
|
let external_spk_26 = external_desc.at_derivation_index(26).unwrap().script_pubkey();
|
||||||
@ -339,7 +331,7 @@ fn test_wildcard_derivations() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_non_wildcard_derivations() {
|
fn test_non_wildcard_derivations() {
|
||||||
let mut txout_index = KeychainTxOutIndex::<TestKeychain>::default();
|
let mut txout_index = KeychainTxOutIndex::<TestKeychain>::new(0);
|
||||||
|
|
||||||
let secp = bitcoin::secp256k1::Secp256k1::signing_only();
|
let secp = bitcoin::secp256k1::Secp256k1::signing_only();
|
||||||
let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
|
let (no_wildcard_descriptor, _) = Descriptor::<DescriptorPublicKey>::parse_descriptor(&secp, "wpkh([73c5da0a/86'/0'/0']xprv9xgqHN7yz9MwCkxsBPN5qetuNdQSUttZNKw1dcYTV4mkaAFiBVGQziHs3NRSWMkCzvgjEe3n9xV8oYywvM8at9yRqyaZVz6TYYhX98VjsUk/1/0)").unwrap();
|
||||||
|
@ -477,7 +477,7 @@ fn populate_with_txids(
|
|||||||
|
|
||||||
let spk = tx
|
let spk = tx
|
||||||
.output
|
.output
|
||||||
.get(0)
|
.first()
|
||||||
.map(|txo| &txo.script_pubkey)
|
.map(|txo| &txo.script_pubkey)
|
||||||
.expect("tx must have an output");
|
.expect("tx must have an output");
|
||||||
|
|
||||||
|
@ -12,7 +12,7 @@ use bdk_bitcoind_rpc::{
|
|||||||
Emitter,
|
Emitter,
|
||||||
};
|
};
|
||||||
use bdk_chain::{
|
use bdk_chain::{
|
||||||
bitcoin::{Block, Transaction},
|
bitcoin::{constants::genesis_block, Block, Transaction},
|
||||||
indexed_tx_graph, keychain,
|
indexed_tx_graph, keychain,
|
||||||
local_chain::{self, CheckPoint, LocalChain},
|
local_chain::{self, CheckPoint, LocalChain},
|
||||||
ConfirmationTimeHeightAnchor, IndexedTxGraph,
|
ConfirmationTimeHeightAnchor, IndexedTxGraph,
|
||||||
@ -64,9 +64,6 @@ struct RpcArgs {
|
|||||||
/// Starting block height to fallback to if no point of agreement if found
|
/// Starting block height to fallback to if no point of agreement if found
|
||||||
#[clap(env = "FALLBACK_HEIGHT", long, default_value = "0")]
|
#[clap(env = "FALLBACK_HEIGHT", long, default_value = "0")]
|
||||||
fallback_height: u32,
|
fallback_height: u32,
|
||||||
/// The unused-scripts lookahead will be kept at this size
|
|
||||||
#[clap(long, default_value = "10")]
|
|
||||||
lookahead: u32,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<RpcArgs> for Auth {
|
impl From<RpcArgs> for Auth {
|
||||||
@ -120,10 +117,11 @@ fn main() -> anyhow::Result<()> {
|
|||||||
"[{:>10}s] loaded initial changeset from db",
|
"[{:>10}s] loaded initial changeset from db",
|
||||||
start.elapsed().as_secs_f32()
|
start.elapsed().as_secs_f32()
|
||||||
);
|
);
|
||||||
|
let (init_chain_changeset, init_graph_changeset) = init_changeset;
|
||||||
|
|
||||||
let graph = Mutex::new({
|
let graph = Mutex::new({
|
||||||
let mut graph = IndexedTxGraph::new(index);
|
let mut graph = IndexedTxGraph::new(index);
|
||||||
graph.apply_changeset(init_changeset.1);
|
graph.apply_changeset(init_graph_changeset);
|
||||||
graph
|
graph
|
||||||
});
|
});
|
||||||
println!(
|
println!(
|
||||||
@ -131,7 +129,16 @@ fn main() -> anyhow::Result<()> {
|
|||||||
start.elapsed().as_secs_f32()
|
start.elapsed().as_secs_f32()
|
||||||
);
|
);
|
||||||
|
|
||||||
let chain = Mutex::new(LocalChain::from_changeset(init_changeset.0)?);
|
let chain = Mutex::new(if init_chain_changeset.is_empty() {
|
||||||
|
let genesis_hash = genesis_block(args.network).block_hash();
|
||||||
|
let (chain, chain_changeset) = LocalChain::from_genesis_hash(genesis_hash);
|
||||||
|
let mut db = db.lock().unwrap();
|
||||||
|
db.stage((chain_changeset, Default::default()));
|
||||||
|
db.commit()?;
|
||||||
|
chain
|
||||||
|
} else {
|
||||||
|
LocalChain::from_changeset(init_chain_changeset)?
|
||||||
|
});
|
||||||
println!(
|
println!(
|
||||||
"[{:>10}s] loaded local chain from changeset",
|
"[{:>10}s] loaded local chain from changeset",
|
||||||
start.elapsed().as_secs_f32()
|
start.elapsed().as_secs_f32()
|
||||||
@ -161,13 +168,9 @@ fn main() -> anyhow::Result<()> {
|
|||||||
match rpc_cmd {
|
match rpc_cmd {
|
||||||
RpcCommands::Sync { rpc_args } => {
|
RpcCommands::Sync { rpc_args } => {
|
||||||
let RpcArgs {
|
let RpcArgs {
|
||||||
fallback_height,
|
fallback_height, ..
|
||||||
lookahead,
|
|
||||||
..
|
|
||||||
} = rpc_args;
|
} = rpc_args;
|
||||||
|
|
||||||
graph.lock().unwrap().index.set_lookahead_for_all(lookahead);
|
|
||||||
|
|
||||||
let chain_tip = chain.lock().unwrap().tip();
|
let chain_tip = chain.lock().unwrap().tip();
|
||||||
let rpc_client = rpc_args.new_client()?;
|
let rpc_client = rpc_args.new_client()?;
|
||||||
let mut emitter = Emitter::new(&rpc_client, chain_tip, fallback_height);
|
let mut emitter = Emitter::new(&rpc_client, chain_tip, fallback_height);
|
||||||
@ -233,13 +236,10 @@ fn main() -> anyhow::Result<()> {
|
|||||||
}
|
}
|
||||||
RpcCommands::Live { rpc_args } => {
|
RpcCommands::Live { rpc_args } => {
|
||||||
let RpcArgs {
|
let RpcArgs {
|
||||||
fallback_height,
|
fallback_height, ..
|
||||||
lookahead,
|
|
||||||
..
|
|
||||||
} = rpc_args;
|
} = rpc_args;
|
||||||
let sigterm_flag = start_ctrlc_handler();
|
let sigterm_flag = start_ctrlc_handler();
|
||||||
|
|
||||||
graph.lock().unwrap().index.set_lookahead_for_all(lookahead);
|
|
||||||
let last_cp = chain.lock().unwrap().tip();
|
let last_cp = chain.lock().unwrap().tip();
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user