2024-05-08 14:36:52 +02:00
|
|
|
#![cfg(feature = "miniscript")]
|
|
|
|
|
2023-09-29 15:43:48 +02:00
|
|
|
use rand::distributions::{Alphanumeric, DistString};
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
feat!: Rework sqlite, changesets, persistence and wallet-construction
Rework sqlite: Instead of only supported one schema (defined in
`bdk_sqlite`), we have a schema per changeset type for more flexiblity.
* rm `bdk_sqlite` crate (as we don't need `bdk_sqlite::Store` anymore).
* add `sqlite` feature on `bdk_chain` which adds methods on each
changeset type for initializing tables, loading the changeset and
writing.
Rework changesets: Some callers may want to use `KeychainTxOutIndex`
where `K` may change per descriptor on every run. So we only want to
persist the last revealed indices by `DescriptorId` (which uniquely-ish
identifies the descriptor).
* rm `keychain_added` field from `keychain_txout`'s changeset.
* Add `keychain_added` to `CombinedChangeSet` (which is renamed to
`WalletChangeSet`).
Rework persistence: add back some safety and convenience when persisting
our types. Working with changeset directly (as we were doing before) can
be cumbersome.
* Intoduce `struct Persisted<T>` which wraps a type `T` which stores
staged changes to it. This adds safety when creating and or loading
`T` from db.
* `struct Persisted<T>` methods, `create`, `load` and `persist`, are
avaliable if `trait PersistWith<Db>` is implemented for `T`. `Db`
represents the database connection and `PersistWith` should be
implemented per database-type.
* For async, we have `trait PersistedAsyncWith<Db>`.
* `Wallet` has impls of `PersistedWith<rusqlite::Connection>`,
`PersistedWith<rusqlite::Transaction>` and
`PersistedWith<bdk_file_store::Store>` by default.
Rework wallet-construction: Before, we had multiple methods for loading
and creating with different input-counts so it would be unwieldly to add
more parameters in the future. This also makes it difficult to impl
`PersistWith` (which has a single method for `load` that takes in
`PersistWith::LoadParams` and a single method for `create` that takes in
`PersistWith::CreateParams`).
* Introduce a builder pattern when constructing a `Wallet`. For loading
from persistence or `ChangeSet`, we have `LoadParams`. For creating a
new wallet, we have `CreateParams`.
2024-07-11 04:49:01 +00:00
|
|
|
use bdk_chain::{spk_txout::SpkTxOutIndex, tx_graph::TxGraph, Anchor};
|
2023-09-29 15:43:48 +02:00
|
|
|
use bitcoin::{
|
2023-10-16 19:51:53 +11:00
|
|
|
locktime::absolute::LockTime, secp256k1::Secp256k1, transaction, Amount, OutPoint, ScriptBuf,
|
|
|
|
Sequence, Transaction, TxIn, TxOut, Txid, Witness,
|
2023-09-29 15:43:48 +02:00
|
|
|
};
|
|
|
|
use miniscript::Descriptor;
|
|
|
|
|
|
|
|
/// Template for creating a transaction in `TxGraph`.
|
|
|
|
///
|
|
|
|
/// The incentive for transaction templates is to create a transaction history in a simple manner to
|
|
|
|
/// avoid having to explicitly hash previous transactions to form previous outpoints of later
|
|
|
|
/// transactions.
|
|
|
|
#[derive(Clone, Copy, Default)]
|
|
|
|
pub struct TxTemplate<'a, A> {
|
|
|
|
/// Uniquely identifies the transaction, before it can have a txid.
|
|
|
|
pub tx_name: &'a str,
|
|
|
|
pub inputs: &'a [TxInTemplate<'a>],
|
|
|
|
pub outputs: &'a [TxOutTemplate],
|
|
|
|
pub anchors: &'a [A],
|
|
|
|
pub last_seen: Option<u64>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub enum TxInTemplate<'a> {
|
|
|
|
/// This will give a random txid and vout.
|
|
|
|
Bogus,
|
|
|
|
|
|
|
|
/// This is used for coinbase transactions because they do not have previous outputs.
|
|
|
|
Coinbase,
|
|
|
|
|
|
|
|
/// Contains the `tx_name` and `vout` that we are spending. The rule is that we must only spend
|
|
|
|
/// from tx of a previous `TxTemplate`.
|
|
|
|
PrevTx(&'a str, usize),
|
|
|
|
}
|
|
|
|
|
|
|
|
pub struct TxOutTemplate {
|
|
|
|
pub value: u64,
|
|
|
|
pub spk_index: Option<u32>, // some = get spk from SpkTxOutIndex, none = random spk
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(unused)]
|
|
|
|
impl TxOutTemplate {
|
|
|
|
pub fn new(value: u64, spk_index: Option<u32>) -> Self {
|
|
|
|
TxOutTemplate { value, spk_index }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
2024-02-13 21:29:12 +08:00
|
|
|
pub fn init_graph<'a, A: Anchor + Clone + 'a>(
|
2024-02-08 15:45:42 +08:00
|
|
|
tx_templates: impl IntoIterator<Item = &'a TxTemplate<'a, A>>,
|
|
|
|
) -> (TxGraph<A>, SpkTxOutIndex<u32>, HashMap<&'a str, Txid>) {
|
2024-01-15 18:52:03 +01:00
|
|
|
let (descriptor, _) =
|
|
|
|
Descriptor::parse_descriptor(&Secp256k1::signing_only(), super::DESCRIPTORS[2]).unwrap();
|
2024-02-08 15:45:42 +08:00
|
|
|
let mut graph = TxGraph::<A>::default();
|
2023-09-29 15:43:48 +02:00
|
|
|
let mut spk_index = SpkTxOutIndex::default();
|
|
|
|
(0..10).for_each(|index| {
|
|
|
|
spk_index.insert_spk(
|
|
|
|
index,
|
|
|
|
descriptor
|
|
|
|
.at_derivation_index(index)
|
|
|
|
.unwrap()
|
|
|
|
.script_pubkey(),
|
|
|
|
);
|
|
|
|
});
|
|
|
|
let mut tx_ids = HashMap::<&'a str, Txid>::new();
|
|
|
|
|
|
|
|
for (bogus_txin_vout, tx_tmp) in tx_templates.into_iter().enumerate() {
|
|
|
|
let tx = Transaction {
|
2023-10-16 19:51:53 +11:00
|
|
|
version: transaction::Version::non_standard(0),
|
2023-09-29 15:43:48 +02:00
|
|
|
lock_time: LockTime::ZERO,
|
|
|
|
input: tx_tmp
|
|
|
|
.inputs
|
|
|
|
.iter()
|
|
|
|
.map(|input| match input {
|
|
|
|
TxInTemplate::Bogus => TxIn {
|
|
|
|
previous_output: OutPoint::new(
|
|
|
|
bitcoin::hashes::Hash::hash(
|
|
|
|
Alphanumeric
|
|
|
|
.sample_string(&mut rand::thread_rng(), 20)
|
|
|
|
.as_bytes(),
|
|
|
|
),
|
|
|
|
bogus_txin_vout as u32,
|
|
|
|
),
|
|
|
|
script_sig: ScriptBuf::new(),
|
|
|
|
sequence: Sequence::default(),
|
|
|
|
witness: Witness::new(),
|
|
|
|
},
|
|
|
|
TxInTemplate::Coinbase => TxIn {
|
|
|
|
previous_output: OutPoint::null(),
|
|
|
|
script_sig: ScriptBuf::new(),
|
|
|
|
sequence: Sequence::MAX,
|
|
|
|
witness: Witness::new(),
|
|
|
|
},
|
|
|
|
TxInTemplate::PrevTx(prev_name, prev_vout) => {
|
|
|
|
let prev_txid = tx_ids.get(prev_name).expect(
|
|
|
|
"txin template must spend from tx of template that comes before",
|
|
|
|
);
|
|
|
|
TxIn {
|
|
|
|
previous_output: OutPoint::new(*prev_txid, *prev_vout as _),
|
|
|
|
script_sig: ScriptBuf::new(),
|
|
|
|
sequence: Sequence::default(),
|
|
|
|
witness: Witness::new(),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
output: tx_tmp
|
|
|
|
.outputs
|
|
|
|
.iter()
|
|
|
|
.map(|output| match &output.spk_index {
|
|
|
|
None => TxOut {
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::from_sat(output.value),
|
2023-09-29 15:43:48 +02:00
|
|
|
script_pubkey: ScriptBuf::new(),
|
|
|
|
},
|
|
|
|
Some(index) => TxOut {
|
2023-10-16 19:51:53 +11:00
|
|
|
value: Amount::from_sat(output.value),
|
2023-09-29 15:43:48 +02:00
|
|
|
script_pubkey: spk_index.spk_at_index(index).unwrap().to_owned(),
|
|
|
|
},
|
|
|
|
})
|
|
|
|
.collect(),
|
|
|
|
};
|
|
|
|
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
tx_ids.insert(tx_tmp.tx_name, tx.compute_txid());
|
2023-09-29 15:43:48 +02:00
|
|
|
spk_index.scan(&tx);
|
|
|
|
let _ = graph.insert_tx(tx.clone());
|
|
|
|
for anchor in tx_tmp.anchors.iter() {
|
deps(bdk): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
deps(chain): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(chain): use `minimal_non_dust()` instead of `dust_value()`
fix(chain): use `compute_txid()` instead of `txid`
deps(testenv): bump `electrsd` to `0.28.0`
deps(electrum): bump `electrum-client` to `0.20.0`
fix(electrum): use `compute_txid()` instead of `txid`
deps(esplora): bump `esplora-client` to `0.8.0`
deps(bitcoind_rpc): bump `bitcoin` to `0.32.0`, `bitcoincore-rpc` to
`0.19.0`
fix(bitcoind_rpc): use `compute_txid()` instead of `txid`
fix(nursery/tmp_plan): use proper `sighash` errors, and fix the expected
`Signature` fields
fix(sqlite): use `compute_txid()` instead of `txid`
deps(hwi): bump `hwi` to `0.9.0`
deps(wallet): bump `bitcoin` to `0.32.0`, miniscript to `12.0.0`
fix(wallet): use `compute_txid()` and `minimal_non_dust()`
- update to use `compute_txid()` instead of deprecated `txid()`
- update to use `minimal_non_dust()` instead of `dust_value()`
- remove unused `bitcoin::hex::FromHex`.
fix(wallet): uses `.into` conversion on `Network` for `NetworkKind`
- uses `.into()` when appropriate, otherwise use the explicit
`NetworkKind`, and it's `.is_mainnet()` method.
fix(wallet): add P2wpkh, Taproot, InputsIndex errors to `SignerError`
fix(wallet): fields on taproot, and ecdsa `Signature` structure
fix(wallet/wallet): convert `Weight` to `usize` for now
- converts the `bitcoin-units::Weight` type to `usize` with help of
`to_wu()` method.
- it should be updated/refactored in the future to handle the `Weight`
type throughout the code instead of current `usize`, only converting
it for now.
- allows the usage of deprecated `is_provably_unspendable()`, needs
further discussion if suggested `is_op_return` is suitable.
- update the expect field to `signature`, as it was renamed from `sig`.
fix(wallet/wallet): use `is_op_return` instead of
`is_provably_unspendable`
fix(wallet/wallet): use `relative::Locktime` instead of `Sequence`
fix(wallet/descriptor): use `ParsePublicKeyError`
fix(wallet/descriptor): use `.into()` to convert from `AbsLockTime` and
`RelLockTime` to `absolute::LockTime` and `relative::LockTime`
fix(wallet/wallet): use `Message::from_digest()` instead of relying on
deprecated `ThirtyTwoByteHash` trait.
fix(wallet/descriptor+wallet): expect `Threshold` type, and handle it
internally
fix(wallet/wallet): remove `0x` prefix from expected `TxId` display
fix(examples): use `compute_txid()` instead of `txid`
fix(ci): remove usage of `bitcoin/no-std` feature
- remove comment: `# The `no-std` feature it's implied when the `std` feature is disabled.`
2024-05-22 18:34:30 -03:00
|
|
|
let _ = graph.insert_anchor(tx.compute_txid(), anchor.clone());
|
2023-09-29 15:43:48 +02:00
|
|
|
}
|
2024-05-23 17:33:45 -04:00
|
|
|
let _ = graph.insert_seen_at(tx.compute_txid(), tx_tmp.last_seen.unwrap_or(0));
|
2023-09-29 15:43:48 +02:00
|
|
|
}
|
|
|
|
(graph, spk_index, tx_ids)
|
|
|
|
}
|