feat: add sync and full_scan methods on esplora client

This commit is contained in:
thunderbiscuit 2024-05-05 20:08:33 -04:00
parent 19723240b7
commit 72b5bfd4c9
No known key found for this signature in database
GPG Key ID: 88253696EB836462
18 changed files with 224 additions and 114 deletions

View File

@ -26,7 +26,8 @@ class LiveTxBuilderTest {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
@ -48,7 +49,8 @@ class LiveTxBuilderTest {
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")

View File

@ -26,7 +26,8 @@ class LiveWalletTest {
val descriptor: Descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
val balance: Balance = wallet.getBalance()
@ -49,14 +50,15 @@ class LiveWalletTest {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
println("New address: ${wallet.getAddress(AddressIndex.New).address}")
println("New address: ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address}")
assert(wallet.getBalance().total > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)

View File

@ -44,7 +44,7 @@ class OfflineWalletTest {
persistenceFilePath,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")

View File

@ -115,6 +115,7 @@ interface EsploraError {
HeaderHashNotFound();
InvalidHttpHeaderName(string name);
InvalidHttpHeaderValue(string value);
RequestAlreadyConsumed();
};
[Error]
@ -238,6 +239,10 @@ dictionary CanonicalTx {
ChainPosition chain_position;
};
interface FullScanRequest {};
interface SyncRequest {};
// ------------------------------------------------------------------------
// bdk crate - wallet module
// ------------------------------------------------------------------------
@ -283,6 +288,10 @@ interface Wallet {
sequence<LocalOutput> list_unspent();
sequence<LocalOutput> list_output();
FullScanRequest start_full_scan();
SyncRequest start_sync_with_revealed_spks();
};
interface Update {};
@ -429,7 +438,10 @@ interface EsploraClient {
constructor(string url);
[Throws=EsploraError]
Update full_scan(Wallet wallet, u64 stop_gap, u64 parallel_requests);
Update full_scan(FullScanRequest full_scan_request, u64 stop_gap, u64 parallel_requests);
[Throws=EsploraError]
Update sync(SyncRequest sync_request, u64 parallel_requests);
[Throws=EsploraError]
void broadcast([ByRef] Transaction transaction);

View File

@ -1,34 +1,28 @@
use crate::bitcoin::OutPoint;
use bdk::bitcoin::address::ParseError;
use bdk::bitcoin::bip32::Error as BdkBip32Error;
use bdk::bitcoin::psbt::PsbtParseError as BdkPsbtParseError;
use bdk::bitcoin::Network;
use bdk::chain::tx_graph::CalculateFeeError as BdkCalculateFeeError;
use bdk::descriptor::DescriptorError as BdkDescriptorError;
use bdk::keys::bip39::Error as BdkBip39Error;
use bdk::miniscript::descriptor::DescriptorKeyParseError as BdkDescriptorKeyParseError;
use bdk::wallet::error::BuildFeeBumpError;
use bdk::wallet::error::CreateTxError as BdkCreateTxError;
use bdk::wallet::signer::SignerError as BdkSignerError;
use bdk::wallet::tx_builder::AddUtxoError;
use bdk::wallet::NewOrLoadError;
use bdk_esplora::esplora_client::{Error as BdkEsploraError, Error};
use bdk_file_store::FileError as BdkFileError;
use bdk_file_store::IterError;
use bitcoin_internals::hex::display::DisplayHex;
use crate::error::bip32::Error as BdkBip32Error;
use bdk::bitcoin::address::ParseError;
use bdk::keys::bip39::Error as BdkBip39Error;
use bdk::miniscript::descriptor::DescriptorKeyParseError as BdkDescriptorKeyParseError;
use bdk::bitcoin::bip32;
use bdk::chain;
use bdk::wallet::error::CreateTxError as BdkCreateTxError;
use std::convert::TryInto;
use bdk::bitcoin::address::Error as BdkAddressError;
use bdk::bitcoin::consensus::encode::Error as BdkEncodeError;
use bdk::bitcoin::psbt::ExtractTxError as BdkExtractTxError;
use chain::local_chain::CannotConnectError as BdkCannotConnectError;
use bdk::chain::local_chain::CannotConnectError as BdkCannotConnectError;
// ------------------------------------------------------------------------
// error definitions
@ -295,6 +289,9 @@ pub enum EsploraError {
#[error("invalid HTTP header value: {value}")]
InvalidHttpHeaderValue { value: String },
#[error("the request has already been consumed")]
RequestAlreadyConsumed,
}
#[derive(Debug, thiserror::Error)]
@ -411,6 +408,7 @@ pub enum TxidParseError {
InvalidTxid { txid: String },
}
// This error combines the Rust bdk::wallet::NewOrLoadError and bdk_file_store::FileError
#[derive(Debug, thiserror::Error)]
pub enum WalletCreationError {
#[error("io error trying to read file: {error_message}")]
@ -422,17 +420,14 @@ pub enum WalletCreationError {
#[error("error with descriptor")]
Descriptor,
#[error("failed to write to persistence")]
Write,
#[error("failed to load from persistence")]
Load,
#[error("failed to either write to or load from persistence, {error_message}")]
Persist { error_message: String },
#[error("wallet is not initialized, persistence backend is empty")]
NotInitialized,
#[error("loaded genesis hash does not match the expected one")]
LoadedGenesisDoesNotMatch,
#[error("loaded genesis hash '{got}' does not match the expected one '{expected}'")]
LoadedGenesisDoesNotMatch { expected: String, got: String },
#[error("loaded network type is not {expected}, got {got:?}")]
LoadedNetworkDoesNotMatch {
@ -560,8 +555,8 @@ impl From<BdkCannotConnectError> for CannotConnectError {
}
}
impl From<BdkCreateTxError<std::io::Error>> for CreateTxError {
fn from(error: BdkCreateTxError<std::io::Error>) -> Self {
impl From<BdkCreateTxError> for CreateTxError {
fn from(error: BdkCreateTxError) -> Self {
match error {
BdkCreateTxError::Descriptor(e) => CreateTxError::Descriptor {
error_message: e.to_string(),
@ -751,6 +746,44 @@ impl From<BdkEsploraError> for EsploraError {
}
}
impl From<Box<BdkEsploraError>> for EsploraError {
fn from(error: Box<BdkEsploraError>) -> Self {
match *error {
BdkEsploraError::Minreq(e) => EsploraError::Minreq {
error_message: e.to_string(),
},
BdkEsploraError::HttpResponse { status, message } => EsploraError::HttpResponse {
status,
error_message: message,
},
BdkEsploraError::Parsing(e) => EsploraError::Parsing {
error_message: e.to_string(),
},
Error::StatusCode(e) => EsploraError::StatusCode {
error_message: e.to_string(),
},
BdkEsploraError::BitcoinEncoding(e) => EsploraError::BitcoinEncoding {
error_message: e.to_string(),
},
BdkEsploraError::HexToArray(e) => EsploraError::HexToArray {
error_message: e.to_string(),
},
BdkEsploraError::HexToBytes(e) => EsploraError::HexToBytes {
error_message: e.to_string(),
},
BdkEsploraError::TransactionNotFound(_) => EsploraError::TransactionNotFound,
BdkEsploraError::HeaderHeightNotFound(height) => {
EsploraError::HeaderHeightNotFound { height }
}
BdkEsploraError::HeaderHashNotFound(_) => EsploraError::HeaderHashNotFound,
Error::InvalidHttpHeaderName(name) => EsploraError::InvalidHttpHeaderName { name },
BdkEsploraError::InvalidHttpHeaderValue(value) => {
EsploraError::InvalidHttpHeaderValue { value }
}
}
}
}
impl From<BdkExtractTxError> for ExtractTxError {
fn from(error: BdkExtractTxError) -> Self {
match error {
@ -852,15 +885,19 @@ impl From<BdkFileError> for WalletCreationError {
}
}
impl From<NewOrLoadError<std::io::Error, IterError>> for WalletCreationError {
fn from(error: NewOrLoadError<std::io::Error, IterError>) -> Self {
impl From<NewOrLoadError> for WalletCreationError {
fn from(error: NewOrLoadError) -> Self {
match error {
NewOrLoadError::Descriptor(_) => WalletCreationError::Descriptor,
NewOrLoadError::Write(_) => WalletCreationError::Write,
NewOrLoadError::Load(_) => WalletCreationError::Load,
NewOrLoadError::Persist(e) => WalletCreationError::Persist {
error_message: e.to_string(),
},
NewOrLoadError::NotInitialized => WalletCreationError::NotInitialized,
NewOrLoadError::LoadedGenesisDoesNotMatch { .. } => {
WalletCreationError::LoadedGenesisDoesNotMatch
NewOrLoadError::LoadedGenesisDoesNotMatch { expected, got } => {
WalletCreationError::LoadedGenesisDoesNotMatch {
expected: expected.to_string(),
got: format!("{:?}", got),
}
}
NewOrLoadError::LoadedNetworkDoesNotMatch { expected, got } => {
WalletCreationError::LoadedNetworkDoesNotMatch { expected, got }
@ -1332,6 +1369,10 @@ mod test {
"header height 123456 not found",
),
(EsploraError::HeaderHashNotFound, "header hash not found"),
(
EsploraError::RequestAlreadyConsumed,
"the request has already been consumed",
),
];
for (error, expected_message) in cases {
@ -1541,20 +1582,21 @@ mod test {
"error with descriptor".to_string(),
),
(
WalletCreationError::Write,
"failed to write to persistence".to_string(),
),
(
WalletCreationError::Load,
"failed to load from persistence".to_string(),
WalletCreationError::Persist {
error_message: "persistence error".to_string(),
},
"failed to either write to or load from persistence, persistence error".to_string(),
),
(
WalletCreationError::NotInitialized,
"wallet is not initialized, persistence backend is empty".to_string(),
),
(
WalletCreationError::LoadedGenesisDoesNotMatch,
"loaded genesis hash does not match the expected one".to_string(),
WalletCreationError::LoadedGenesisDoesNotMatch {
expected: "abc".to_string(),
got: "def".to_string(),
},
"loaded genesis hash 'def' does not match the expected one 'abc'".to_string(),
),
(
WalletCreationError::LoadedNetworkDoesNotMatch {

View File

@ -1,9 +1,15 @@
use crate::error::EsploraError;
use crate::wallet::{Update, Wallet};
use crate::bitcoin::Transaction;
use crate::error::EsploraError;
use crate::types::{FullScanRequest, SyncRequest};
use crate::wallet::Update;
use std::collections::BTreeMap;
use bdk::bitcoin::Transaction as BdkTransaction;
use bdk::wallet::Update as BdkUpdate;
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
use bdk::chain::spk_client::FullScanResult as BdkFullScanResult;
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
use bdk::chain::spk_client::SyncResult as BdkSyncResult;
use bdk::KeychainKind;
use bdk_esplora::esplora_client::{BlockingClient, Builder};
use bdk_esplora::EsploraExt;
@ -17,40 +23,56 @@ impl EsploraClient {
Self(client)
}
// This is a temporary solution for scanning. The long-term solution involves not passing
// the wallet to the client at all.
pub fn full_scan(
&self,
wallet: Arc<Wallet>,
request: Arc<FullScanRequest>,
stop_gap: u64,
parallel_requests: u64,
) -> Result<Arc<Update>, EsploraError> {
let wallet = wallet.get_wallet();
let previous_tip = wallet.latest_checkpoint();
let keychain_spks = wallet.all_unbounded_spk_iters().into_iter().collect();
let (update_graph, last_active_indices) = self
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkFullScanRequest<KeychainKind> = request
.0
.full_scan(keychain_spks, stop_gap as usize, parallel_requests as usize)
.map_err(|e| EsploraError::from(*e))?;
.lock()
.unwrap()
.take()
.ok_or(EsploraError::RequestAlreadyConsumed)?;
let missing_heights = update_graph.missing_heights(wallet.local_chain());
let chain_update = self
.0
.update_local_chain(previous_tip, missing_heights)
.map_err(|e| EsploraError::from(*e))?;
let result: BdkFullScanResult<KeychainKind> =
self.0
.full_scan(request, stop_gap as usize, parallel_requests as usize)?;
let update = BdkUpdate {
last_active_indices,
graph: update_graph,
chain: Some(chain_update),
let update = bdk::wallet::Update {
last_active_indices: result.last_active_indices,
graph: result.graph_update,
chain: Some(result.chain_update),
};
Ok(Arc::new(Update(update)))
}
// pub fn sync();
pub fn sync(
&self,
request: Arc<SyncRequest>,
parallel_requests: u64,
) -> Result<Arc<Update>, EsploraError> {
// using option and take is not ideal but the only way to take full ownership of the request
let request: BdkSyncRequest = request
.0
.lock()
.unwrap()
.take()
.ok_or(EsploraError::RequestAlreadyConsumed)?;
let result: BdkSyncResult = self.0.sync(request, parallel_requests as usize)?;
let update = bdk::wallet::Update {
last_active_indices: BTreeMap::default(),
graph: result.graph_update,
chain: Some(result.chain_update),
};
Ok(Arc::new(Update(update)))
}
pub fn broadcast(&self, transaction: &Transaction) -> Result<(), EsploraError> {
let bdk_transaction: BdkTransaction = transaction.into();

View File

@ -40,8 +40,10 @@ use crate::types::AddressInfo;
use crate::types::Balance;
use crate::types::CanonicalTx;
use crate::types::ChainPosition;
use crate::types::FullScanRequest;
use crate::types::LocalOutput;
use crate::types::ScriptAmount;
use crate::types::SyncRequest;
use crate::wallet::BumpFeeTxBuilder;
use crate::wallet::SentAndReceivedValues;
use crate::wallet::TxBuilder;

View File

@ -1,14 +1,15 @@
use crate::bitcoin::{Address, OutPoint, Script, Transaction, TxOut};
use bdk::chain::spk_client::FullScanRequest as BdkFullScanRequest;
use bdk::chain::spk_client::SyncRequest as BdkSyncRequest;
use bdk::chain::tx_graph::CanonicalTx as BdkCanonicalTx;
use bdk::chain::{ChainPosition as BdkChainPosition, ConfirmationTimeHeightAnchor};
use bdk::wallet::AddressIndex as BdkAddressIndex;
use bdk::wallet::AddressInfo as BdkAddressInfo;
use bdk::wallet::Balance as BdkBalance;
use bdk::KeychainKind;
use bdk::LocalOutput as BdkLocalOutput;
use std::sync::Arc;
use std::sync::{Arc, Mutex};
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum ChainPosition {
@ -108,3 +109,7 @@ impl From<BdkLocalOutput> for LocalOutput {
}
}
}
pub struct FullScanRequest(pub(crate) Mutex<Option<BdkFullScanRequest<KeychainKind>>>);
pub struct SyncRequest(pub(crate) Mutex<Option<BdkSyncRequest>>);

View File

@ -4,7 +4,9 @@ use crate::error::{
CalculateFeeError, CannotConnectError, CreateTxError, PersistenceError, SignerError,
TxidParseError, WalletCreationError,
};
use crate::types::{AddressIndex, AddressInfo, Balance, CanonicalTx, LocalOutput, ScriptAmount};
use crate::types::{
AddressInfo, Balance, CanonicalTx, FullScanRequest, LocalOutput, ScriptAmount, SyncRequest,
};
use bdk::bitcoin::blockdata::script::ScriptBuf as BdkScriptBuf;
use bdk::bitcoin::Network;
@ -142,6 +144,16 @@ impl Wallet {
pub fn list_output(&self) -> Vec<LocalOutput> {
self.get_wallet().list_output().map(|o| o.into()).collect()
}
pub fn start_full_scan(&self) -> Arc<FullScanRequest> {
let request = self.get_wallet().start_full_scan();
Arc::new(FullScanRequest(Mutex::new(Some(request))))
}
pub fn start_sync_with_revealed_spks(&self) -> Arc<SyncRequest> {
let request = self.get_wallet().start_sync_with_revealed_spks();
Arc::new(SyncRequest(Mutex::new(Some(request))))
}
}
pub struct SentAndReceivedValues {

View File

@ -24,7 +24,8 @@ class LiveTxBuilderTest {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
@ -47,7 +48,8 @@ class LiveTxBuilderTest {
val changeDescriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/1/*)", Network.TESTNET)
val wallet = Wallet(externalDescriptor, changeDescriptor, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")

View File

@ -26,7 +26,8 @@ class LiveWalletTest {
val esploraClient: EsploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
// val esploraClient: EsploraClient = EsploraClient("https://mempool.space/testnet/api")
// val esploraClient = EsploraClient("https://blockstream.info/testnet/api")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
@ -47,14 +48,14 @@ class LiveWalletTest {
val descriptor = Descriptor("wpkh(tprv8ZgxMBicQKsPf2qfrEygW6fdYseJDDrVnDv26PH5BHdvSuG6ecCbHqLVof9yZcMoM31z9ur3tTYbSnr1WBqbGX97CbXcmp5H6qeMpyvx35B/84h/1h/0h/0/*)", Network.TESTNET)
val wallet: Wallet = Wallet(descriptor, null, persistenceFilePath, Network.TESTNET)
val esploraClient = EsploraClient("https://esplora.testnet.kuutamo.cloud/")
val update = esploraClient.fullScan(wallet, 10uL, 1uL)
val fullScanRequest: FullScanRequest = wallet.startFullScan()
val update = esploraClient.fullScan(fullScanRequest, 10uL, 1uL)
wallet.applyUpdate(update)
println("Balance: ${wallet.getBalance().total}")
println("New address: ${wallet.getAddress(AddressIndex.New).address.asString()}")
println("New address: ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()}")
assert(wallet.getBalance().total > 0uL) {
"Wallet balance must be greater than 0! Please send funds to ${wallet.getAddress(AddressIndex.New).address} and try again."
"Wallet balance must be greater than 0! Please send funds to ${wallet.revealNextAddress(KeychainKind.EXTERNAL).address.asString()} and try again."
}
val recipient: Address = Address("tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989", Network.TESTNET)

View File

@ -42,7 +42,7 @@ class OfflineWalletTest {
persistenceFilePath,
Network.TESTNET
)
val addressInfo: AddressInfo = wallet.getAddress(AddressIndex.New)
val addressInfo: AddressInfo = wallet.revealNextAddress(KeychainKind.EXTERNAL)
assertTrue(addressInfo.address.isValidForNetwork(Network.TESTNET), "Address is not valid for testnet network")
assertTrue(addressInfo.address.isValidForNetwork(Network.SIGNET), "Address is not valid for signet network")

View File

@ -19,19 +19,20 @@ class LiveTxBuilderTest(unittest.TestCase):
"./bdk_persistence.db",
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
update = esploraClient.full_scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
update = esplora_client.full_scan(
full_scan_request=full_scan_request,
stop_gap=10,
parallel_requests=1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total, 0)
recipient = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network=bdk.Network.TESTNET
)
psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
@ -53,23 +54,24 @@ class LiveTxBuilderTest(unittest.TestCase):
"./bdk_persistence.db",
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
update = esploraClient.full_scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
update = esplora_client.full_scan(
full_scan_request=full_scan_request,
stop_gap=10,
parallel_requests=1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total, 0)
recipient1 = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network=bdk.Network.TESTNET
)
recipient2 = bdk.Address(
address = "tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6",
network = bdk.Network.TESTNET
address="tb1qw2c3lxufxqe2x9s4rdzh65tpf4d7fssjgh8nv6",
network=bdk.Network.TESTNET
)
all_recipients = list(
bdk.ScriptAmount(recipient1.script_pubkey, 4200),

View File

@ -19,11 +19,12 @@ class LiveWalletTest(unittest.TestCase):
"./bdk_persistence.db",
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
update = esploraClient.full_scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
update = esplora_client.full_scan(
full_scan_request=full_scan_request,
stop_gap=10,
parallel_requests=1
)
wallet.apply_update(update)
@ -49,19 +50,20 @@ class LiveWalletTest(unittest.TestCase):
"./bdk_persistence.db",
bdk.Network.TESTNET
)
esploraClient: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
update = esploraClient.full_scan(
wallet = wallet,
stop_gap = 10,
parallel_requests = 1
esplora_client: bdk.EsploraClient = bdk.EsploraClient(url = "https://esplora.testnet.kuutamo.cloud/")
full_scan_request: bdk.FullScanRequest = wallet.start_full_scan()
update = esplora_client.full_scan(
full_scan_request=full_scan_request,
stop_gap=10,
parallel_requests=1
)
wallet.apply_update(update)
self.assertGreater(wallet.get_balance().total, 0)
recipient = bdk.Address(
address = "tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network = bdk.Network.TESTNET
address="tb1qrnfslnrve9uncz9pzpvf83k3ukz22ljgees989",
network=bdk.Network.TESTNET
)
psbt: bdk.Psbt = bdk.TxBuilder().add_recipient(script=recipient.script_pubkey(), amount=4200).fee_rate(fee_rate=bdk.FeeRate.from_sat_per_vb(2)).finish(wallet)
@ -77,7 +79,7 @@ class LiveWalletTest(unittest.TestCase):
fee_rate = wallet.calculate_fee_rate(tx)
print(f"Transaction Fee Rate: {fee_rate.to_sat_per_vb_ceil()} sat/vB")
esploraClient.broadcast(tx)
esplora_client.broadcast(tx)
if __name__ == '__main__':

View File

@ -19,7 +19,7 @@ class OfflineWalletTest(unittest.TestCase):
"./bdk_persistence.db",
bdk.Network.TESTNET
)
address_info: bdk.AddressInfo = wallet.get_address(bdk.AddressIndex.NEW())
address_info: bdk.AddressInfo = wallet.reveal_next_address(bdk.KeychainKind.EXTERNAL)
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.TESTNET), "Address is not valid for testnet network")
self.assertTrue(address_info.address.is_valid_for_network(bdk.Network.SIGNET), "Address is not valid for signet network")

View File

@ -35,8 +35,9 @@ final class LiveTxBuilderTests: XCTestCase {
network: .testnet
)
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
let fullScanRequest: FullScanRequest = wallet.startFullScan()
let update = try esploraClient.fullScan(
wallet: wallet,
fullScanRequest: fullScanRequest,
stopGap: 10,
parallelRequests: 1
)
@ -70,8 +71,9 @@ final class LiveTxBuilderTests: XCTestCase {
network: .testnet
)
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
let fullScanRequest: FullScanRequest = wallet.startFullScan()
let update = try esploraClient.fullScan(
wallet: wallet,
fullScanRequest: fullScanRequest,
stopGap: 10,
parallelRequests: 1
)

View File

@ -35,8 +35,9 @@ final class LiveWalletTests: XCTestCase {
network: .testnet
)
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
let fullScanRequest: FullScanRequest = wallet.startFullScan()
let update = try esploraClient.fullScan(
wallet: wallet,
fullScanRequest: fullScanRequest,
stopGap: 10,
parallelRequests: 1
)
@ -66,8 +67,9 @@ final class LiveWalletTests: XCTestCase {
network: .testnet
)
let esploraClient = EsploraClient(url: "https://esplora.testnet.kuutamo.cloud/")
let fullScanRequest: FullScanRequest = wallet.startFullScan()
let update = try esploraClient.fullScan(
wallet: wallet,
fullScanRequest: fullScanRequest,
stopGap: 10,
parallelRequests: 1
)

View File

@ -34,7 +34,7 @@ final class OfflineWalletTests: XCTestCase {
persistenceBackendPath: dbFilePath.path,
network: .testnet
)
let addressInfo: AddressInfo = wallet.getAddress(addressIndex: AddressIndex.new)
let addressInfo: AddressInfo = try wallet.revealNextAddress(keychain: KeychainKind.external)
XCTAssertTrue(addressInfo.address.isValidForNetwork(network: Network.testnet),
"Address is not valid for testnet network")