[wallet] Add an option to change the assumed current height
This commit is contained in:
parent
45aa001e10
commit
e32559a06a
@ -161,6 +161,15 @@ fn main() {
|
|||||||
.takes_value(true)
|
.takes_value(true)
|
||||||
.number_of_values(1)
|
.number_of_values(1)
|
||||||
.required(true),
|
.required(true),
|
||||||
|
)
|
||||||
|
.arg(
|
||||||
|
Arg::with_name("assume_height")
|
||||||
|
.long("assume_height")
|
||||||
|
.value_name("HEIGHT")
|
||||||
|
.help("Assume the blockchain has reached a specific height. This affects the transaction finalization, if there are timelocks in the descriptor")
|
||||||
|
.takes_value(true)
|
||||||
|
.number_of_values(1)
|
||||||
|
.required(false),
|
||||||
))
|
))
|
||||||
.subcommand(
|
.subcommand(
|
||||||
SubCommand::with_name("broadcast")
|
SubCommand::with_name("broadcast")
|
||||||
@ -322,7 +331,10 @@ fn main() {
|
|||||||
} else if let Some(sub_matches) = matches.subcommand_matches("sign") {
|
} else if let Some(sub_matches) = matches.subcommand_matches("sign") {
|
||||||
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
|
let psbt = base64::decode(sub_matches.value_of("psbt").unwrap()).unwrap();
|
||||||
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
|
let psbt: PartiallySignedTransaction = deserialize(&psbt).unwrap();
|
||||||
let (psbt, finalized) = wallet.sign(psbt).unwrap();
|
let assume_height = sub_matches
|
||||||
|
.value_of("assume_height")
|
||||||
|
.and_then(|s| Some(s.parse().unwrap()));
|
||||||
|
let (psbt, finalized) = wallet.sign(psbt, assume_height).unwrap();
|
||||||
|
|
||||||
println!("PSBT: {}", base64::encode(&serialize(&psbt)));
|
println!("PSBT: {}", base64::encode(&serialize(&psbt)));
|
||||||
println!("Finalized: {}", finalized);
|
println!("Finalized: {}", finalized);
|
||||||
|
@ -29,6 +29,10 @@ impl<T: Read + Write> Blockchain for ElectrumBlockchain<T> {
|
|||||||
fn offline() -> Self {
|
fn offline() -> Self {
|
||||||
ElectrumBlockchain(None)
|
ElectrumBlockchain(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_online(&self) -> bool {
|
||||||
|
self.0.is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Read + Write> OnlineBlockchain for ElectrumBlockchain<T> {
|
impl<T: Read + Write> OnlineBlockchain for ElectrumBlockchain<T> {
|
||||||
@ -190,6 +194,8 @@ impl<T: Read + Write> OnlineBlockchain for ElectrumBlockchain<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_height(&mut self) -> Result<usize, Error> {
|
fn get_height(&mut self) -> Result<usize, Error> {
|
||||||
|
// TODO: unsubscribe when added to the client, or is there a better call to use here?
|
||||||
|
|
||||||
Ok(self
|
Ok(self
|
||||||
.0
|
.0
|
||||||
.as_mut()
|
.as_mut()
|
||||||
|
@ -18,6 +18,8 @@ pub enum Capability {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait Blockchain {
|
pub trait Blockchain {
|
||||||
|
fn is_online(&self) -> bool;
|
||||||
|
|
||||||
fn offline() -> Self;
|
fn offline() -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -26,6 +28,10 @@ impl Blockchain for OfflineBlockchain {
|
|||||||
fn offline() -> Self {
|
fn offline() -> Self {
|
||||||
OfflineBlockchain
|
OfflineBlockchain
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn is_online(&self) -> bool {
|
||||||
|
false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait OnlineBlockchain: Blockchain {
|
pub trait OnlineBlockchain: Blockchain {
|
||||||
|
@ -31,14 +31,13 @@ use crate::types::*;
|
|||||||
|
|
||||||
pub type OfflineWallet<D> = Wallet<OfflineBlockchain, D>;
|
pub type OfflineWallet<D> = Wallet<OfflineBlockchain, D>;
|
||||||
|
|
||||||
//#[cfg(feature = "electrum")]
|
|
||||||
//pub type ElectrumWallet<S, D> = Wallet<crate::blockchain::ElectrumBlockchain<electrum_client::Client<S>>, D>;
|
|
||||||
|
|
||||||
pub struct Wallet<B: Blockchain, D: BatchDatabase> {
|
pub struct Wallet<B: Blockchain, D: BatchDatabase> {
|
||||||
descriptor: ExtendedDescriptor,
|
descriptor: ExtendedDescriptor,
|
||||||
change_descriptor: Option<ExtendedDescriptor>,
|
change_descriptor: Option<ExtendedDescriptor>,
|
||||||
network: Network,
|
network: Network,
|
||||||
|
|
||||||
|
current_height: Option<u32>,
|
||||||
|
|
||||||
client: RefCell<B>,
|
client: RefCell<B>,
|
||||||
database: RefCell<D>,
|
database: RefCell<D>,
|
||||||
}
|
}
|
||||||
@ -82,6 +81,8 @@ where
|
|||||||
change_descriptor,
|
change_descriptor,
|
||||||
network,
|
network,
|
||||||
|
|
||||||
|
current_height: None,
|
||||||
|
|
||||||
client: RefCell::new(B::offline()),
|
client: RefCell::new(B::offline()),
|
||||||
database: RefCell::new(database),
|
database: RefCell::new(database),
|
||||||
})
|
})
|
||||||
@ -342,7 +343,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: define an enum for signing errors
|
// TODO: define an enum for signing errors
|
||||||
pub fn sign(&self, mut psbt: PSBT) -> Result<(PSBT, bool), Error> {
|
pub fn sign(&self, mut psbt: PSBT, assume_height: Option<u32>) -> Result<(PSBT, bool), Error> {
|
||||||
// this helps us doing our job later
|
// this helps us doing our job later
|
||||||
self.add_hd_keypaths(&mut psbt)?;
|
self.add_hd_keypaths(&mut psbt)?;
|
||||||
|
|
||||||
@ -482,7 +483,7 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
// attempt to finalize
|
// attempt to finalize
|
||||||
let finalized = self.finalize_psbt(tx.clone(), &mut psbt);
|
let finalized = self.finalize_psbt(tx.clone(), &mut psbt, assume_height)?;
|
||||||
|
|
||||||
Ok((psbt, finalized))
|
Ok((psbt, finalized))
|
||||||
}
|
}
|
||||||
@ -635,7 +636,12 @@ where
|
|||||||
Ok((answer, paths, selected_amount, fee_val))
|
Ok((answer, paths, selected_amount, fee_val))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finalize_psbt(&self, mut tx: Transaction, psbt: &mut PSBT) -> bool {
|
fn finalize_psbt(
|
||||||
|
&self,
|
||||||
|
mut tx: Transaction,
|
||||||
|
psbt: &mut PSBT,
|
||||||
|
assume_height: Option<u32>,
|
||||||
|
) -> Result<bool, Error> {
|
||||||
for (n, input) in tx.input.iter_mut().enumerate() {
|
for (n, input) in tx.input.iter_mut().enumerate() {
|
||||||
// safe to run only on the descriptor because we assume the change descriptor also has
|
// safe to run only on the descriptor because we assume the change descriptor also has
|
||||||
// the same structure
|
// the same structure
|
||||||
@ -643,18 +649,33 @@ where
|
|||||||
debug!("reconstructed descriptor is {:?}", desc);
|
debug!("reconstructed descriptor is {:?}", desc);
|
||||||
|
|
||||||
let desc = match desc {
|
let desc = match desc {
|
||||||
Err(_) => return false,
|
Err(_) => return Ok(false),
|
||||||
Ok(desc) => desc,
|
Ok(desc) => desc,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// if the height is None in the database it means it's still unconfirmed, so consider
|
||||||
|
// that as a very high value
|
||||||
|
let create_height = self
|
||||||
|
.database
|
||||||
|
.borrow()
|
||||||
|
.get_tx(&input.previous_output.txid, false)?
|
||||||
|
.and_then(|tx| Some(tx.height.unwrap_or(std::u32::MAX)));
|
||||||
|
let current_height = assume_height.or(self.current_height);
|
||||||
|
|
||||||
|
debug!(
|
||||||
|
"Input #{} - {}, using `create_height` = {:?}, `current_height` = {:?}",
|
||||||
|
n, input.previous_output, create_height, current_height
|
||||||
|
);
|
||||||
|
|
||||||
// TODO: use height once we sync headers
|
// TODO: use height once we sync headers
|
||||||
let satisfier = PSBTSatisfier::new(&psbt.inputs[n], true, None, None);
|
let satisfier =
|
||||||
|
PSBTSatisfier::new(&psbt.inputs[n], false, create_height, current_height);
|
||||||
|
|
||||||
match desc.satisfy(input, satisfier) {
|
match desc.satisfy(input, satisfier) {
|
||||||
Ok(_) => continue,
|
Ok(_) => continue,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!("satisfy error {:?} for input {}", e, n);
|
debug!("satisfy error {:?} for input {}", e, n);
|
||||||
return false;
|
return Ok(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -665,7 +686,7 @@ where
|
|||||||
psbt_input.final_script_witness = Some(input.witness);
|
psbt_input.final_script_witness = Some(input.witness);
|
||||||
}
|
}
|
||||||
|
|
||||||
true
|
Ok(true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -679,7 +700,7 @@ where
|
|||||||
change_descriptor: Option<&str>,
|
change_descriptor: Option<&str>,
|
||||||
network: Network,
|
network: Network,
|
||||||
mut database: D,
|
mut database: D,
|
||||||
client: B,
|
mut client: B,
|
||||||
) -> Result<Self, Error> {
|
) -> Result<Self, Error> {
|
||||||
database.check_descriptor_checksum(
|
database.check_descriptor_checksum(
|
||||||
ScriptType::External,
|
ScriptType::External,
|
||||||
@ -703,11 +724,15 @@ where
|
|||||||
None => None,
|
None => None,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let current_height = Some(client.get_height()? as u32);
|
||||||
|
|
||||||
Ok(Wallet {
|
Ok(Wallet {
|
||||||
descriptor,
|
descriptor,
|
||||||
change_descriptor,
|
change_descriptor,
|
||||||
network,
|
network,
|
||||||
|
|
||||||
|
current_height,
|
||||||
|
|
||||||
client: RefCell::new(client),
|
client: RefCell::new(client),
|
||||||
database: RefCell::new(database),
|
database: RefCell::new(database),
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user