Skip to content

Commit

Permalink
Insert pay to contract keychain kind when the contract is stored.
Browse files Browse the repository at this point in the history
  • Loading branch information
rantan committed Sep 2, 2024
1 parent fce8132 commit 5416d18
Show file tree
Hide file tree
Showing 6 changed files with 76 additions and 1 deletion.
5 changes: 5 additions & 0 deletions crates/wallet/src/descriptor/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ pub enum Error {
Hex(tapyrus::hex::HexToBytesError),
/// The provided wallet descriptors are identical
ExternalAndInternalAreTheSame,
/// The keychain kind is not supported
UnsupportedKeychainKind,
}

impl From<crate::keys::KeyError> for Error {
Expand Down Expand Up @@ -84,6 +86,9 @@ impl fmt::Display for Error {
Self::ExternalAndInternalAreTheSame => {
write!(f, "External and internal descriptors are the same")
}
Error::UnsupportedKeychainKind => {
write!(f, "The keychain kind is not supported")
}
}
}
}
Expand Down
6 changes: 6 additions & 0 deletions crates/wallet/src/descriptor/template.rs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,9 @@ macro_rules! expand_make_bipxx {
KeychainKind::Internal => {
derivation_path.push(bip32::ChildNumber::from_normal_idx(1)?)
}
KeychainKind::PayToContract { .. } => {
return Err(DescriptorError::UnsupportedKeychainKind);
}
};

let derivation_path: bip32::DerivationPath = derivation_path.into();
Expand All @@ -224,6 +227,9 @@ macro_rules! expand_make_bipxx {
let derivation_path: bip32::DerivationPath = match keychain {
KeychainKind::External => vec![bip32::ChildNumber::from_normal_idx(0)?].into(),
KeychainKind::Internal => vec![bip32::ChildNumber::from_normal_idx(1)?].into(),
KeychainKind::PayToContract { .. } => {
return Err(DescriptorError::UnsupportedKeychainKind);
}
};

let source_path = bip32::DerivationPath::from(vec![
Expand Down
8 changes: 8 additions & 0 deletions crates/wallet/src/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ use tdk_chain::ConfirmationTime;
use serde::{Deserialize, Serialize};

/// Types of keychains
#[repr(u8)]
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
pub enum KeychainKind {
/// External keychain, used for deriving recipient addresses.
External = 0,
/// Internal keychain, used for deriving change addresses.
Internal = 1,
/// Pay to Contract keychain, used for deriving contract addresses.
PayToContract {
/// The contract's public key
p2c_pk: tapyrus::PublicKey,
},
}

impl KeychainKind {
Expand All @@ -33,6 +39,7 @@ impl KeychainKind {
match self {
KeychainKind::External => b'e',
KeychainKind::Internal => b'i',
KeychainKind::PayToContract { .. } => b'c',
}
}
}
Expand All @@ -42,6 +49,7 @@ impl AsRef<[u8]> for KeychainKind {
match self {
KeychainKind::External => b"e",
KeychainKind::Internal => b"i",
KeychainKind::PayToContract { .. } => b"c",
}
}
}
Expand Down
31 changes: 31 additions & 0 deletions crates/wallet/src/wallet/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1474,6 +1474,7 @@ impl Wallet {
let signers = match keychain {
KeychainKind::External => Arc::make_mut(&mut self.signers),
KeychainKind::Internal => Arc::make_mut(&mut self.change_signers),
KeychainKind::PayToContract { .. } => Arc::make_mut(&mut self.signers),
};

signers.add_external(signer.id(&self.secp), ordering, signer);
Expand Down Expand Up @@ -1501,6 +1502,7 @@ impl Wallet {
match keychain {
KeychainKind::External => Arc::clone(&self.signers),
KeychainKind::Internal => Arc::clone(&self.change_signers),
KeychainKind::PayToContract { .. } => Arc::clone(&self.signers),
}
}

Expand Down Expand Up @@ -2187,6 +2189,7 @@ impl Wallet {
let signers = match keychain {
KeychainKind::External => &self.signers,
KeychainKind::Internal => &self.change_signers,
KeychainKind::PayToContract { .. } => &self.signers,
};

self.public_descriptor(keychain).extract_policy(
Expand Down Expand Up @@ -2875,6 +2878,34 @@ impl Wallet {

self.contracts
.insert(contract_id.clone(), new_contract.clone());

// Insert p2c public key descriptor.
let p2c_pk = self
.pay_to_contract_key(&new_contract.payment_base, new_contract.contract.clone())
.unwrap();
let keychain_kind = KeychainKind::PayToContract {
p2c_pk: p2c_pk.clone(),
};
let p2c_descriptor = Descriptor::new_pkh(DescriptorPublicKey::Single(SinglePub {
key: SinglePubKey::FullKey(p2c_pk),
origin: None,
}))
.unwrap();
let txout_index_changeset = self
.indexed_graph
.index
.insert_descriptor(keychain_kind, p2c_descriptor);
let (_, reveal_changeset) = self
.indexed_graph
.index
.reveal_to_target(&keychain_kind, 0)
.unwrap();
changeset
.indexed_tx_graph
.indexer
.append(txout_index_changeset);
changeset.indexed_tx_graph.indexer.append(reveal_changeset);

self.persist
.stage_and_commit(changeset)
.map_err(|e| CreateContractError::Error {
Expand Down
1 change: 1 addition & 0 deletions crates/wallet/src/wallet/tx_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,7 @@ impl<'a, Cs> TxBuilder<'a, Cs> {
let to_update = match keychain {
KeychainKind::Internal => &mut self.params.internal_policy_path,
KeychainKind::External => &mut self.params.external_policy_path,
KeychainKind::PayToContract { .. } => &mut self.params.external_policy_path,
};

*to_update = Some(policy_path);
Expand Down
26 changes: 25 additions & 1 deletion crates/wallet/tests/wallet.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3862,10 +3862,11 @@ fn test_store_contract() {
let mut wallet = Wallet::new(desc, change_desc, db, Network::Dev).expect("must init wallet");
let descriptor = wallet.get_descriptor_for_keychain(KeychainKind::External);
let payment_base = descriptor_to_public_key(descriptor).unwrap();
let contract = vec![0x00, 0x01, 0x02, 0x03];

let result = wallet.store_contract(
"contract_id".to_string(),
vec![0x00, 0x01, 0x02, 0x03],
contract.clone(),
payment_base.clone(),
true,
);
Expand All @@ -3884,6 +3885,29 @@ fn test_store_contract() {

let result = wallet.update_contract("invalid_id".to_string(), false);
assert!(result.is_err());

// Check the descriptor for the pay to contract is exists.
let public_key = wallet
.pay_to_contract_key(&payment_base, contract.clone())
.unwrap();
let _descriptor =
wallet.get_descriptor_for_keychain(KeychainKind::PayToContract { p2c_pk: public_key });

// Check the sync request includes the spk for the pay to contract.
let mut sync_request = wallet.start_sync_with_revealed_spks();
let p2c_address = wallet
.create_pay_to_contract_address(&payment_base, contract.clone(), None)
.unwrap();
let p2c_spk = p2c_address.script_pubkey();
assert!(sync_request.spks.any(|spk| { spk == p2c_spk }));

// Check the full sync request includes the spk for the pay to contract
let mut full_sync_request = wallet.start_full_scan();
let mut spks = full_sync_request
.spks_by_keychain
.get_mut(&KeychainKind::PayToContract { p2c_pk: public_key });
assert!(spks.is_some());
assert!(spks.unwrap().any(|(i, spk)| { spk == p2c_spk }));
}

#[test]
Expand Down

0 comments on commit 5416d18

Please sign in to comment.