Update bitcoin, miniscript, electrum-client

This commit is contained in:
Alekos Filini
2021-02-02 20:06:40 -05:00
parent 4c36020e95
commit 3d9d6fee07
17 changed files with 701 additions and 581 deletions

View File

@@ -76,6 +76,7 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize};
use miniscript::descriptor::{ShInner, WshInner};
use miniscript::{Descriptor, DescriptorPublicKey, ScriptContext, Terminal};
use crate::database::BatchDatabase;
@@ -107,6 +108,10 @@ impl FromStr for WalletExport {
}
}
fn remove_checksum(s: String) -> String {
s.splitn(2, '#').next().map(String::from).unwrap()
}
impl WalletExport {
/// Export a wallet
///
@@ -127,6 +132,7 @@ impl WalletExport {
let descriptor = wallet
.descriptor
.to_string_with_secret(&wallet.signers.as_key_map(wallet.secp_ctx()));
let descriptor = remove_checksum(descriptor);
Self::is_compatible_with_core(&descriptor)?;
let blockheight = match wallet.database.borrow().iter_txs(false) {
@@ -150,7 +156,9 @@ impl WalletExport {
};
let desc_to_string = |d: &Descriptor<DescriptorPublicKey>| {
d.to_string_with_secret(&wallet.change_signers.as_key_map(wallet.secp_ctx()))
let descriptor =
d.to_string_with_secret(&wallet.change_signers.as_key_map(wallet.secp_ctx()));
remove_checksum(descriptor)
};
if export.change_descriptor() != wallet.change_descriptor.as_ref().map(desc_to_string) {
return Err("Incompatible change descriptor");
@@ -161,7 +169,7 @@ impl WalletExport {
fn is_compatible_with_core(descriptor: &str) -> Result<(), &'static str> {
fn check_ms<Ctx: ScriptContext>(
terminal: Terminal<String, Ctx>,
terminal: &Terminal<String, Ctx>,
) -> Result<(), &'static str> {
if let Terminal::Multi(_, _) = terminal {
Ok(())
@@ -170,13 +178,22 @@ impl WalletExport {
}
}
// pkh(), wpkh(), sh(wpkh()) are always fine, as well as multi() and sortedmulti()
match Descriptor::<String>::from_str(descriptor).map_err(|_| "Invalid descriptor")? {
Descriptor::Pk(_)
| Descriptor::Pkh(_)
| Descriptor::Wpkh(_)
| Descriptor::ShWpkh(_) => Ok(()),
Descriptor::Sh(ms) => check_ms(ms.node),
Descriptor::Wsh(ms) | Descriptor::ShWsh(ms) => check_ms(ms.node),
Descriptor::Pkh(_) | Descriptor::Wpkh(_) => Ok(()),
Descriptor::Sh(sh) => match sh.as_inner() {
ShInner::Wpkh(_) => Ok(()),
ShInner::SortedMulti(_) => Ok(()),
ShInner::Wsh(wsh) => match wsh.as_inner() {
WshInner::SortedMulti(_) => Ok(()),
WshInner::Ms(ms) => check_ms(&ms.node),
},
ShInner::Ms(ms) => check_ms(&ms.node),
},
Descriptor::Wsh(wsh) => match wsh.as_inner() {
WshInner::SortedMulti(_) => Ok(()),
WshInner::Ms(ms) => check_ms(&ms.node),
},
_ => Err("The descriptor is not compatible with Bitcoin Core"),
}
}

View File

@@ -36,11 +36,11 @@ use bitcoin::secp256k1::Secp256k1;
use bitcoin::consensus::encode::serialize;
use bitcoin::util::base58;
use bitcoin::util::bip32::ChildNumber;
use bitcoin::util::psbt::raw::Key as PSBTKey;
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
use bitcoin::{Address, Network, OutPoint, Script, Transaction, TxOut, Txid};
use miniscript::descriptor::DescriptorTrait;
use miniscript::psbt::PsbtInputSatisfier;
#[allow(unused_imports)]
@@ -60,16 +60,14 @@ use address_validator::AddressValidator;
use coin_selection::DefaultCoinSelectionAlgorithm;
use signer::{Signer, SignerOrdering, SignersContainer};
use tx_builder::{BumpFee, CreateTx, FeePolicy, TxBuilder, TxParams};
use utils::{
check_nlocktime, check_nsequence_rbf, descriptor_to_pk_ctx, After, Older, SecpCtx,
DUST_LIMIT_SATOSHI,
};
use utils::{check_nlocktime, check_nsequence_rbf, After, Older, SecpCtx, DUST_LIMIT_SATOSHI};
use crate::blockchain::{Blockchain, Progress};
use crate::database::{BatchDatabase, BatchOperations, DatabaseUtils};
use crate::descriptor::derived::AsDerived;
use crate::descriptor::{
get_checksum, DescriptorMeta, DescriptorScripts, ExtendedDescriptor, ExtractPolicy, Policy,
ToWalletDescriptor, XKeyUtils,
get_checksum, DerivedDescriptor, DerivedDescriptorMeta, DescriptorMeta, DescriptorScripts,
ExtendedDescriptor, ExtractPolicy, Policy, ToWalletDescriptor, XKeyUtils,
};
use crate::error::Error;
use crate::psbt::PSBTUtils;
@@ -134,7 +132,9 @@ where
client: B,
current_height: Option<u32>,
) -> Result<Self, Error> {
let (descriptor, keymap) = descriptor.to_wallet_descriptor(network)?;
let secp = Secp256k1::new();
let (descriptor, keymap) = descriptor.to_wallet_descriptor(&secp, network)?;
database.check_descriptor_checksum(
KeychainKind::External,
get_checksum(&descriptor.to_string())?.as_bytes(),
@@ -142,7 +142,8 @@ where
let signers = Arc::new(SignersContainer::from(keymap));
let (change_descriptor, change_signers) = match change_descriptor {
Some(desc) => {
let (change_descriptor, change_keymap) = desc.to_wallet_descriptor(network)?;
let (change_descriptor, change_keymap) =
desc.to_wallet_descriptor(&secp, network)?;
database.check_descriptor_checksum(
KeychainKind::Internal,
get_checksum(&change_descriptor.to_string())?.as_bytes(),
@@ -168,7 +169,7 @@ where
current_height,
client,
database: RefCell::new(database),
secp: Secp256k1::new(),
secp,
})
}
}
@@ -181,12 +182,11 @@ where
/// Return a newly generated address using the external descriptor
pub fn get_new_address(&self) -> Result<Address, Error> {
let index = self.fetch_and_increment_index(KeychainKind::External)?;
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
self.descriptor
.derive(ChildNumber::from_normal_idx(index)?)
.address(self.network, deriv_ctx)
.ok_or(Error::ScriptDoesntHaveAddressForm)
.as_derived(index, &self.secp)
.address(self.network)
.map_err(|_| Error::ScriptDoesntHaveAddressForm)
}
/// Return whether or not a `script` is part of this wallet (either internal or external)
@@ -666,7 +666,6 @@ where
let vbytes = tx.get_weight() as f32 / 4.0;
let feerate = details.fees as f32 / vbytes;
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
// remove the inputs from the tx and process them
let original_txin = tx.input.drain(..).collect::<Vec<_>>();
let original_utxos = original_txin
@@ -686,7 +685,7 @@ where
Some((keychain, _)) => (
self._get_descriptor_for_keychain(keychain)
.0
.max_satisfaction_weight(deriv_ctx)
.max_satisfaction_weight()
.unwrap(),
keychain,
),
@@ -855,7 +854,7 @@ where
// - Try to derive the descriptor by looking at the txout. If it's in our database, we
// know exactly which `keychain` to use, and which derivation index it is
// - If that fails, try to derive it by looking at the psbt input: the complete logic
// is in `src/descriptor/mod.rs`, but it will basically look at `hd_keypaths`,
// is in `src/descriptor/mod.rs`, but it will basically look at `bip32_derivation`,
// `redeem_script` and `witness_script` to determine the right derivation
// - If that also fails, it will try it on the internal descriptor, if present
let desc = psbt
@@ -879,7 +878,6 @@ where
match desc {
Some(desc) => {
let mut tmp_input = bitcoin::TxIn::default();
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
match desc.satisfy(
&mut tmp_input,
(
@@ -887,7 +885,6 @@ where
After::new(current_height, false),
Older::new(current_height, create_height, false),
),
deriv_ctx,
) {
Ok(_) => {
let psbt_input = &mut psbt.inputs[n];
@@ -933,31 +930,30 @@ where
}
}
fn get_descriptor_for_txout(&self, txout: &TxOut) -> Result<Option<ExtendedDescriptor>, Error> {
fn get_descriptor_for_txout(
&self,
txout: &TxOut,
) -> Result<Option<DerivedDescriptor<'_>>, Error> {
Ok(self
.database
.borrow()
.get_path_from_script_pubkey(&txout.script_pubkey)?
.map(|(keychain, child)| (self.get_descriptor_for_keychain(keychain), child))
.map(|(desc, child)| desc.derive(ChildNumber::from_normal_idx(child).unwrap())))
.map(|(desc, child)| desc.as_derived(child, &self.secp)))
}
fn get_change_address(&self) -> Result<Script, Error> {
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
let (desc, keychain) = self._get_descriptor_for_keychain(KeychainKind::Internal);
let index = self.fetch_and_increment_index(keychain)?;
Ok(desc
.derive(ChildNumber::from_normal_idx(index)?)
.script_pubkey(deriv_ctx))
Ok(desc.as_derived(index, &self.secp).script_pubkey())
}
fn fetch_and_increment_index(&self, keychain: KeychainKind) -> Result<u32, Error> {
let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
let index = match descriptor.is_fixed() {
true => 0,
false => self.database.borrow_mut().increment_last_index(keychain)?,
let index = match descriptor.is_deriveable() {
false => 0,
true => self.database.borrow_mut().increment_last_index(keychain)?,
};
if self
@@ -969,12 +965,11 @@ where
self.cache_addresses(keychain, index, CACHE_ADDR_BATCH_SIZE)?;
}
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
let derived_descriptor = descriptor.as_derived(index, &self.secp);
let hd_keypaths = derived_descriptor.get_hd_keypaths(&self.secp)?;
let script = derived_descriptor.script_pubkey();
let hd_keypaths = descriptor.get_hd_keypaths(index, &self.secp)?;
let script = descriptor
.derive(ChildNumber::from_normal_idx(index)?)
.script_pubkey(deriv_ctx);
for validator in &self.address_validators {
validator.validate(keychain, &hd_keypaths, &script)?;
}
@@ -989,7 +984,7 @@ where
mut count: u32,
) -> Result<(), Error> {
let (descriptor, keychain) = self._get_descriptor_for_keychain(keychain);
if descriptor.is_fixed() {
if !descriptor.is_deriveable() {
if from > 0 {
return Ok(());
}
@@ -997,16 +992,12 @@ where
count = 1;
}
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
let mut address_batch = self.database.borrow().begin_batch();
let start_time = time::Instant::new();
for i in from..(from + count) {
address_batch.set_script_pubkey(
&descriptor
.derive(ChildNumber::from_normal_idx(i)?)
.script_pubkey(deriv_ctx),
&descriptor.as_derived(i, &self.secp).script_pubkey(),
keychain,
i,
)?;
@@ -1025,7 +1016,6 @@ where
}
fn get_available_utxos(&self) -> Result<Vec<(UTXO, usize)>, Error> {
let deriv_ctx = descriptor_to_pk_ctx(&self.secp);
Ok(self
.list_unspent()?
.into_iter()
@@ -1034,7 +1024,7 @@ where
(
utxo,
self.get_descriptor_for_keychain(keychain)
.max_satisfaction_weight(deriv_ctx)
.max_satisfaction_weight()
.unwrap(),
)
})
@@ -1174,19 +1164,19 @@ where
};
let (desc, _) = self._get_descriptor_for_keychain(keychain);
psbt_input.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?);
let derived_descriptor = desc.as_derived(child, &self.secp);
psbt_input.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp)?;
psbt_input.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp);
psbt_input.witness_script = derived_descriptor.psbt_witness_script(&self.secp);
psbt_input.redeem_script = derived_descriptor.psbt_redeem_script();
psbt_input.witness_script = derived_descriptor.psbt_witness_script();
let prev_output = input.previous_output;
if let Some(prev_tx) = self.database.borrow().get_raw_tx(&prev_output.txid)? {
if derived_descriptor.is_witness() {
if desc.is_witness() {
psbt_input.witness_utxo =
Some(prev_tx.output[prev_output.vout as usize].clone());
}
if !derived_descriptor.is_witness() || params.force_non_witness_utxo {
if !desc.is_witness() || params.force_non_witness_utxo {
psbt_input.non_witness_utxo = Some(prev_tx);
}
}
@@ -1207,11 +1197,12 @@ where
.get_path_from_script_pubkey(&tx_output.script_pubkey)?
{
let (desc, _) = self._get_descriptor_for_keychain(keychain);
psbt_output.hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
let derived_descriptor = desc.as_derived(child, &self.secp);
psbt_output.bip32_derivation = derived_descriptor.get_hd_keypaths(&self.secp)?;
if params.include_output_redeem_witness_script {
let derived_descriptor = desc.derive(ChildNumber::from_normal_idx(child)?);
psbt_output.witness_script = derived_descriptor.psbt_witness_script(&self.secp);
psbt_output.redeem_script = derived_descriptor.psbt_redeem_script(&self.secp);
psbt_output.witness_script = derived_descriptor.psbt_witness_script();
psbt_output.redeem_script = derived_descriptor.psbt_redeem_script();
};
}
}
@@ -1237,8 +1228,10 @@ where
// merge hd_keypaths
let desc = self.get_descriptor_for_keychain(keychain);
let mut hd_keypaths = desc.get_hd_keypaths(child, &self.secp)?;
psbt_input.hd_keypaths.append(&mut hd_keypaths);
let mut hd_keypaths = desc
.as_derived(child, &self.secp)
.get_hd_keypaths(&self.secp)?;
psbt_input.bip32_derivation.append(&mut hd_keypaths);
}
}
}
@@ -1283,9 +1276,9 @@ where
let mut run_setup = false;
let max_address = match self.descriptor.is_fixed() {
true => 0,
false => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
let max_address = match self.descriptor.is_deriveable() {
false => 0,
true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
};
if self
.database
@@ -1298,9 +1291,9 @@ where
}
if let Some(change_descriptor) = &self.change_descriptor {
let max_address = match change_descriptor.is_fixed() {
true => 0,
false => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
let max_address = match change_descriptor.is_deriveable() {
false => 0,
true => max_address_param.unwrap_or(CACHE_ADDR_BATCH_SIZE),
};
if self
@@ -1946,9 +1939,9 @@ mod test {
.drain_wallet();
let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.inputs[0].hd_keypaths.len(), 1);
assert_eq!(psbt.inputs[0].bip32_derivation.len(), 1);
assert_eq!(
psbt.inputs[0].hd_keypaths.values().nth(0).unwrap(),
psbt.inputs[0].bip32_derivation.values().nth(0).unwrap(),
&(
Fingerprint::from_str("d34db33f").unwrap(),
DerivationPath::from_str("m/44'/0'/0'/0/0").unwrap()
@@ -1972,9 +1965,9 @@ mod test {
.drain_wallet();
let (psbt, _) = builder.finish().unwrap();
assert_eq!(psbt.outputs[0].hd_keypaths.len(), 1);
assert_eq!(psbt.outputs[0].bip32_derivation.len(), 1);
assert_eq!(
psbt.outputs[0].hd_keypaths.values().nth(0).unwrap(),
psbt.outputs[0].bip32_derivation.values().nth(0).unwrap(),
&(
Fingerprint::from_str("d34db33f").unwrap(),
DerivationPath::from_str("m/44'/0'/0'/0/5").unwrap()
@@ -3188,8 +3181,8 @@ mod test {
.drain_wallet();
let (mut psbt, _) = builder.finish().unwrap();
psbt.inputs[0].hd_keypaths.clear();
assert_eq!(psbt.inputs[0].hd_keypaths.len(), 0);
psbt.inputs[0].bip32_derivation.clear();
assert_eq!(psbt.inputs[0].bip32_derivation.len(), 0);
let (signed_psbt, finalized) = wallet.sign(psbt, None).unwrap();
assert_eq!(finalized, true);
@@ -3233,7 +3226,7 @@ mod test {
"wpkh(025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357)",
)
.unwrap()
.script_pubkey(miniscript::NullCtx),
.script_pubkey(),
});
psbt.inputs.push(dud_input);
psbt.global.unsigned_tx.input.push(bitcoin::TxIn::default());

View File

@@ -220,7 +220,7 @@ impl Signer for DescriptorXKey<ExtendedPrivKey> {
}
let (public_key, deriv_path) = match psbt.inputs[input_index]
.hd_keypaths
.bip32_derivation
.iter()
.filter_map(|(pk, &(fingerprint, ref path))| {
if self.matches(&(fingerprint, path.clone()), &secp).is_some() {
@@ -562,7 +562,7 @@ mod signers_container_tests {
use crate::descriptor;
use crate::descriptor::ToWalletDescriptor;
use crate::keys::{DescriptorKey, ToDescriptorKey};
use bitcoin::secp256k1::All;
use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32;
use bitcoin::util::psbt::PartiallySignedTransaction;
use bitcoin::Network;
@@ -574,10 +574,12 @@ mod signers_container_tests {
// This happens usually when a set of signers is created from a descriptor with private keys.
#[test]
fn signers_with_same_ordering() {
let secp = Secp256k1::new();
let (prvkey1, _, _) = setup_keys(TPRV0_STR);
let (prvkey2, _, _) = setup_keys(TPRV1_STR);
let desc = descriptor!(sh(multi(2, prvkey1, prvkey2))).unwrap();
let (_, keymap) = desc.to_wallet_descriptor(Network::Testnet).unwrap();
let (_, keymap) = desc.to_wallet_descriptor(&secp, Network::Testnet).unwrap();
let signers = SignersContainer::from(keymap);
assert_eq!(signers.ids().len(), 2);

View File

@@ -57,6 +57,8 @@ use std::marker::PhantomData;
use bitcoin::util::psbt::PartiallySignedTransaction as PSBT;
use bitcoin::{OutPoint, Script, SigHashType, Transaction};
use miniscript::descriptor::DescriptorTrait;
use super::coin_selection::{CoinSelectionAlgorithm, DefaultCoinSelectionAlgorithm};
use crate::{database::BatchDatabase, Error, Wallet};
use crate::{
@@ -276,7 +278,6 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderConte
/// These have priority over the "unspendable" utxos, meaning that if a utxo is present both in
/// the "utxos" and the "unspendable" list, it will be spent.
pub fn add_utxos(&mut self, outpoints: &[OutPoint]) -> Result<&mut Self, Error> {
let deriv_ctx = crate::wallet::descriptor_to_pk_ctx(self.wallet.secp_ctx());
let utxos = outpoints
.iter()
.map(|outpoint| self.wallet.get_utxo(*outpoint)?.ok_or(Error::UnknownUTXO))
@@ -284,7 +285,7 @@ impl<'a, B, D: BatchDatabase, Cs: CoinSelectionAlgorithm<D>, Ctx: TxBuilderConte
for utxo in utxos {
let descriptor = self.wallet.get_descriptor_for_keychain(utxo.keychain);
let satisfaction_weight = descriptor.max_satisfaction_weight(deriv_ctx).unwrap();
let satisfaction_weight = descriptor.max_satisfaction_weight().unwrap();
self.params.utxos.push((utxo, satisfaction_weight));
}

View File

@@ -23,9 +23,7 @@
// SOFTWARE.
use bitcoin::secp256k1::{All, Secp256k1};
use bitcoin::util::bip32;
use miniscript::descriptor::DescriptorPublicKeyCtx;
use miniscript::{MiniscriptKey, Satisfier, ToPublicKey};
// De-facto standard "dust limit" (even though it should change based on the output type)
@@ -110,7 +108,7 @@ pub(crate) fn check_nlocktime(nlocktime: u32, required: u32) -> bool {
true
}
impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for After {
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for After {
fn check_after(&self, n: u32) -> bool {
if let Some(current_height) = self.current_height {
current_height >= n
@@ -140,7 +138,7 @@ impl Older {
}
}
impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx, Pk> for Older {
impl<Pk: MiniscriptKey + ToPublicKey> Satisfier<Pk> for Older {
fn check_older(&self, n: u32) -> bool {
if let Some(current_height) = self.current_height {
// TODO: test >= / >
@@ -152,12 +150,6 @@ impl<ToPkCtx: Copy, Pk: MiniscriptKey + ToPublicKey<ToPkCtx>> Satisfier<ToPkCtx,
}
pub(crate) type SecpCtx = Secp256k1<All>;
pub(crate) fn descriptor_to_pk_ctx(secp: &SecpCtx) -> DescriptorPublicKeyCtx<'_, All> {
// Create a `to_pk_ctx` with a dummy derivation index, since we always use this on descriptor
// that have already been derived with `Descriptor::derive()`, so the child number added here
// is ignored.
DescriptorPublicKeyCtx::new(secp, bip32::ChildNumber::Normal { index: 0 })
}
pub struct ChunksIterator<I: Iterator> {
iter: I,