Skip to content

Commit

Permalink
Filter spend transaction bip32 derivation
Browse files Browse the repository at this point in the history
Because of the use of revault_tx v0.4
bip32_derivations are already in psbt.

In order to use dummysigner or hw storing
multiple keys, Derivations are filtered
in order to keep only derivations with
targeted xpubs fingerprints.

close revault#205
  • Loading branch information
edouardparis committed Nov 7, 2021
1 parent d28a89f commit 3f5b7c4
Show file tree
Hide file tree
Showing 5 changed files with 94 additions and 118 deletions.
14 changes: 4 additions & 10 deletions src/app/state/manager.rs
Original file line number Diff line number Diff line change
Expand Up @@ -627,17 +627,11 @@ impl<C: Client + Send + Sync + 'static> State<C> for ManagerCreateSendTransactio
if let Some((psbt, _)) = &self.psbt {
self.step = ManagerSendStep::Sign {
signer: Signer::new(SpendTransactionTarget::new(
ctx.revaultd.config.manager_config.as_ref().unwrap().xpub,
&self
.vaults
&ctx.revaultd
.config
.managers_xpubs()
.iter()
.filter_map(|v| {
if v.selected {
Some(v.vault.derivation_index)
} else {
None
}
})
.map(|xpub| xpub.master_fingerprint())
.collect(),
psbt.clone(),
)),
Expand Down
107 changes: 41 additions & 66 deletions src/app/state/sign.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::{collections::BTreeMap, sync::Arc, time::Duration};

use bitcoin::{
secp256k1,
util::{bip32::ExtendedPubKey, psbt::PartiallySignedTransaction as Psbt},
util::{
bip32::{Fingerprint, KeySource},
psbt::PartiallySignedTransaction as Psbt,
},
PublicKey,
};
use revaultd::revault_tx::miniscript::DescriptorPublicKey;
use std::{str::FromStr, sync::Arc, time::Duration};
use tokio::sync::Mutex;

use iced::{time, Command, Element, Subscription};
Expand All @@ -24,36 +27,30 @@ pub struct RevocationTransactionsTarget {

impl RevocationTransactionsTarget {
pub fn new(
xpub: ExtendedPubKey,
derivation_index: u32,
fingerprints: &Vec<Fingerprint>,
mut cancel_tx: Psbt,
mut emergency_tx: Psbt,
emergency_tx: Psbt,
mut emergency_unvault_tx: Psbt,
) -> Self {
let xpub = DescriptorPublicKey::from_str(&format!("{}/*", xpub)).unwrap();
let xpub = xpub.derive(derivation_index);
let curve = secp256k1::Secp256k1::verification_only();

let pubkey = xpub.derive_public_key(&curve).unwrap();
let fingerprint = xpub.master_fingerprint();
let derivation_path = xpub.full_derivation_path();

cancel_tx.inputs.iter_mut().for_each(|input| {
input
.bip32_derivation
.insert(pubkey, (fingerprint, derivation_path.clone()));
});
emergency_tx.inputs.iter_mut().for_each(|input| {
input
.bip32_derivation
.insert(pubkey, (fingerprint, derivation_path.clone()));
});
emergency_unvault_tx.inputs.iter_mut().for_each(|input| {
input
.bip32_derivation
.insert(pubkey, (fingerprint, derivation_path.clone()));
});
for input in &mut cancel_tx.inputs {
let mut new_derivation: BTreeMap<PublicKey, KeySource> = BTreeMap::new();
for (key, source) in &input.bip32_derivation {
if fingerprints.contains(&source.0) {
new_derivation.insert(*key, source.clone());
}
}
input.bip32_derivation = new_derivation;
}

for input in &mut emergency_unvault_tx.inputs {
let mut new_derivation: BTreeMap<PublicKey, KeySource> = BTreeMap::new();
for (key, source) in &input.bip32_derivation {
if fingerprints.contains(&source.0) {
new_derivation.insert(*key, source.clone());
}
}
input.bip32_derivation = new_derivation;
}
Self {
cancel_tx,
emergency_tx,
Expand All @@ -68,21 +65,7 @@ pub struct UnvaultTransactionTarget {
}

impl UnvaultTransactionTarget {
pub fn new(xpub: ExtendedPubKey, derivation_index: u32, mut unvault_tx: Psbt) -> Self {
let xpub = DescriptorPublicKey::from_str(&format!("{}/*", xpub)).unwrap();
let xpub = xpub.derive(derivation_index);
let curve = secp256k1::Secp256k1::verification_only();

let pubkey = xpub.derive_public_key(&curve).unwrap();
let fingerprint = xpub.master_fingerprint();
let derivation_path = xpub.full_derivation_path();

unvault_tx.inputs.iter_mut().for_each(|input| {
input
.bip32_derivation
.insert(pubkey, (fingerprint, derivation_path.clone()));
});

pub fn new(unvault_tx: Psbt) -> Self {
Self { unvault_tx }
}
}
Expand All @@ -93,27 +76,19 @@ pub struct SpendTransactionTarget {
}

impl SpendTransactionTarget {
pub fn new(xpub: ExtendedPubKey, derivation_indexes: &Vec<u32>, mut spend_tx: Psbt) -> Self {
let xpub = DescriptorPublicKey::from_str(&format!("{}/*", xpub)).unwrap();
let curve = secp256k1::Secp256k1::verification_only();

let derived_keys: Vec<DescriptorPublicKey> = derivation_indexes
.iter()
.map(|index| xpub.clone().derive(*index))
.collect();

spend_tx
.inputs
.iter_mut()
.enumerate()
.for_each(|(i, input)| {
let key = &derived_keys[i];
input.bip32_derivation.insert(
key.derive_public_key(&curve).unwrap(),
(key.master_fingerprint(), key.full_derivation_path()),
);
});

/// Creates a new SpendTransactionTarget to sign with only the corresponding keys of the given
/// xpubs. The bip32_derivation of the psbt is filtered to possess only the given xpub
/// fingerprints.
pub fn new(fingerprints: &Vec<Fingerprint>, mut spend_tx: Psbt) -> Self {
for input in &mut spend_tx.inputs {
let mut new_derivation: BTreeMap<PublicKey, KeySource> = BTreeMap::new();
for (key, source) in &input.bip32_derivation {
if fingerprints.contains(&source.0) {
new_derivation.insert(*key, source.clone());
}
}
input.bip32_derivation = new_derivation;
}
Self { spend_tx }
}
}
Expand Down
10 changes: 5 additions & 5 deletions src/app/state/spend_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ impl<C: Client + Send + Sync + 'static> State<C> for SpendTransactionState {
Message::SpendTx(msg) => {
return self
.action
.update(ctx, &self.deposits, &mut self.psbt, msg)
.update(ctx, &mut self.psbt, msg)
.map(Message::SpendTx);
}
_ => {}
Expand Down Expand Up @@ -182,7 +182,6 @@ impl SpendTransactionAction {
fn update<C: Client + Send + Sync + 'static>(
&mut self,
ctx: &Context<C>,
deposits: &Vec<model::Vault>,
psbt: &mut Psbt,
message: SpendTxMessage,
) -> Command<SpendTxMessage> {
Expand Down Expand Up @@ -230,10 +229,11 @@ impl SpendTransactionAction {
processing: false,
warning: None,
signer: Signer::new(SpendTransactionTarget::new(
ctx.revaultd.config.manager_config.as_ref().unwrap().xpub,
&deposits
&ctx.revaultd
.config
.managers_xpubs()
.iter()
.map(|deposit| deposit.derivation_index)
.map(|xpub| xpub.master_fingerprint())
.collect(),
psbt.clone(),
)),
Expand Down
47 changes: 10 additions & 37 deletions src/app/state/vault.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,20 +81,11 @@ impl Vault {
Err(e) => self.warning = Error::from(e).into(),
},
VaultMessage::UnvaultTransaction(res) => match res {
Ok(tx) => {
self.section = VaultSection::new_delegate_section(
ctx,
self.vault.derivation_index,
tx.unvault_tx,
)
}
Ok(tx) => self.section = VaultSection::new_delegate_section(tx.unvault_tx),
Err(e) => self.warning = Error::from(e).into(),
},
VaultMessage::RevocationTransactions(res) => match res {
Ok(tx) => {
self.section =
VaultSection::new_ack_section(ctx, self.vault.derivation_index, tx)
}
Ok(tx) => self.section = VaultSection::new_ack_section(ctx, tx),
Err(e) => self.warning = Error::from(e).into(),
},
VaultMessage::SelectRevault => {
Expand Down Expand Up @@ -203,22 +194,9 @@ impl VaultSection {
}
}

pub fn new_delegate_section<C: Client>(
ctx: &Context<C>,
derivation_index: u32,
unvault_tx: Psbt,
) -> Self {
pub fn new_delegate_section(unvault_tx: Psbt) -> Self {
Self::Delegate {
signer: Signer::new(UnvaultTransactionTarget::new(
ctx.revaultd
.config
.stakeholder_config
.as_ref()
.unwrap()
.xpub,
derivation_index,
unvault_tx,
)),
signer: Signer::new(UnvaultTransactionTarget::new(unvault_tx)),
processing: false,
view: DelegateVaultView::new(),
warning: None,
Expand All @@ -234,20 +212,15 @@ impl VaultSection {
}
}

pub fn new_ack_section<C: Client>(
ctx: &Context<C>,
derivation_index: u32,
txs: RevocationTransactions,
) -> Self {
pub fn new_ack_section<C: Client>(ctx: &Context<C>, txs: RevocationTransactions) -> Self {
Self::Secure {
signer: Signer::new(RevocationTransactionsTarget::new(
ctx.revaultd
&ctx.revaultd
.config
.stakeholder_config
.as_ref()
.unwrap()
.xpub,
derivation_index,
.stakeholders_xpubs()
.iter()
.map(|xpub| xpub.master_fingerprint())
.collect(),
txs.cancel_tx,
txs.emergency_tx,
txs.emergency_unvault_tx,
Expand Down
34 changes: 34 additions & 0 deletions src/daemon/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
use bitcoin::{util::bip32, Network};
use revaultd::revault_tx::{
miniscript::DescriptorPublicKey,
scripts::{DepositDescriptor, UnvaultDescriptor},
};
use serde::{Deserialize, Serialize};
use std::{
net::SocketAddr,
path::{Path, PathBuf},
str::FromStr,
};

// This file is adapted from github.com/re-vault/revaultd:
Expand Down Expand Up @@ -97,6 +102,35 @@ impl Config {
Ok(config)
}

pub fn stakeholders_xpubs(&self) -> Vec<DescriptorPublicKey> {
DepositDescriptor::from_str(&self.scripts_config.deposit_descriptor)
.expect("This a valid deposit descriptor")
.xpubs()
}

pub fn managers_xpubs(&self) -> Vec<DescriptorPublicKey> {
// The managers' xpubs are all the xpubs from the Unvault descriptor except the
// Stakehodlers' ones and the Cosigning Servers' ones.
let stk_xpubs = self.stakeholders_xpubs();
UnvaultDescriptor::from_str(&self.scripts_config.unvault_descriptor)
.expect("This a valid unvault descriptor")
.xpubs()
.into_iter()
.filter_map(|xpub| {
match xpub {
DescriptorPublicKey::SinglePub(_) => None, // Cosig
DescriptorPublicKey::XPub(_) => {
if stk_xpubs.contains(&xpub) {
None // Stakeholder
} else {
Some(xpub) // Manager
}
}
}
})
.collect()
}

/// default revaultd socket path is .revault/bitcoin/revaultd_rpc
pub fn socket_path(&self) -> Result<PathBuf, ConfigError> {
let mut path = if let Some(ref datadir) = self.data_dir {
Expand Down

0 comments on commit 3f5b7c4

Please sign in to comment.