Merge #177: Check for key duplicates when creating a descriptor
9cc748cd2acb3338f230378b827104fee4e56318 descriptors: make the satisfaction size helpers part of the multi desc (Antoine Poinsot) 929c79a56a82566359bb9c19587d4ddf9ed2e04d descriptors: check for duplicates when creating a descriptor (Antoine Poinsot) Pull request description: Fixes #149. Also contains a drive-by fix for the sat size API in the second commit. ACKs for top commit: darosior: self-ACK 9cc748cd2acb3338f230378b827104fee4e56318 Tree-SHA512: 72be90ff49895546d5c83060650418243d43d48c19d781a209dc12cb6bcff0d880ffe0aa34b8f124b9fea6c0629c7e20328579e72072cad8e39a45be4b176471
This commit is contained in:
commit
4aeae407c7
@ -30,6 +30,7 @@ fn wu_to_vb(vb: usize) -> usize {
|
||||
pub enum DescCreationError {
|
||||
InsaneTimelock(u32),
|
||||
InvalidKey(Box<descriptor::DescriptorPublicKey>),
|
||||
DuplicateKey(Box<descriptor::DescriptorPublicKey>),
|
||||
Miniscript(miniscript::Error),
|
||||
IncompatibleDesc,
|
||||
DerivedKeyParsing,
|
||||
@ -46,6 +47,9 @@ impl std::fmt::Display for DescCreationError {
|
||||
key
|
||||
)
|
||||
}
|
||||
Self::DuplicateKey(key) => {
|
||||
write!(f, "Duplicate key '{}'.", key)
|
||||
}
|
||||
Self::Miniscript(e) => write!(f, "Miniscript error: '{}'.", e),
|
||||
Self::IncompatibleDesc => write!(f, "Descriptor is not compatible."),
|
||||
Self::DerivedKeyParsing => write!(f, "Parsing derived key,"),
|
||||
@ -347,6 +351,19 @@ impl MultipathDescriptor {
|
||||
return Err(DescCreationError::InvalidKey((**key).clone().into()));
|
||||
}
|
||||
|
||||
// Check for key duplicates. They are invalid in (nonmalleable) miniscripts.
|
||||
let owner_xpub = match owner_key {
|
||||
descriptor::DescriptorPublicKey::MultiXPub(ref multi_xpub) => multi_xpub.xkey,
|
||||
_ => unreachable!("Just checked it was a multixpub above"),
|
||||
};
|
||||
let heir_xpub = match heir_key {
|
||||
descriptor::DescriptorPublicKey::MultiXPub(ref multi_xpub) => multi_xpub.xkey,
|
||||
_ => unreachable!("Just checked it was a multixpub above"),
|
||||
};
|
||||
if owner_xpub == heir_xpub {
|
||||
return Err(DescCreationError::DuplicateKey(owner_key.into()));
|
||||
}
|
||||
|
||||
let owner_pk = Miniscript::from_ast(Terminal::Check(sync::Arc::from(
|
||||
Miniscript::from_ast(Terminal::PkK(owner_key)).expect("pk_k is a valid Miniscript"),
|
||||
)))
|
||||
@ -455,6 +472,20 @@ impl MultipathDescriptor {
|
||||
assert!(csv.is_height_locked());
|
||||
csv.to_consensus_u32()
|
||||
}
|
||||
|
||||
/// Get the maximum size in WU of a satisfaction for this descriptor.
|
||||
pub fn max_sat_weight(&self) -> usize {
|
||||
self.multi_desc
|
||||
.max_satisfaction_weight()
|
||||
.expect("Cannot fail for P2WSH")
|
||||
}
|
||||
|
||||
/// Get the maximum size in virtual bytes of the whole input in a transaction spending
|
||||
/// a coin with this Script.
|
||||
pub fn spender_input_size(&self) -> usize {
|
||||
// txid + vout + nSequence + empty scriptSig + witness
|
||||
32 + 4 + 4 + 1 + wu_to_vb(self.max_sat_weight())
|
||||
}
|
||||
}
|
||||
|
||||
impl InheritanceDescriptor {
|
||||
@ -514,20 +545,6 @@ impl InheritanceDescriptor {
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
/// Get the maximum size in WU of a satisfaction for this descriptor.
|
||||
pub fn max_sat_weight(&self) -> usize {
|
||||
self.0
|
||||
.max_satisfaction_weight()
|
||||
.expect("Cannot fail for P2WSH")
|
||||
}
|
||||
|
||||
/// Get the maximum size in virtual bytes of the whole input in a transaction spending
|
||||
/// a coin with this Script.
|
||||
pub fn spender_input_size(&self) -> usize {
|
||||
// txid + vout + nSequence + empty scriptSig + witness
|
||||
32 + 4 + 4 + 1 + wu_to_vb(self.max_sat_weight())
|
||||
}
|
||||
}
|
||||
|
||||
/// Map of a raw public key to the xpub used to derive it and its derivation path
|
||||
@ -613,6 +630,17 @@ mod tests {
|
||||
MultipathDescriptor::new(owner_key.clone(), heir_key, timelock).unwrap_err();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub661MyMwAqRbcFfxf71L4Dx4w5TmyNXrBicTEAM7vLzumxangwATWWgdJPb6xH1JHcJH9S3jNZx3fCnkkB1WyqrqGgavj1rehHcbythmruvZ/<0;1;2>/*'").unwrap();
|
||||
MultipathDescriptor::new(owner_key, heir_key, timelock).unwrap_err();
|
||||
|
||||
// You can't pass duplicate keys, even if they are encoded differently.
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
MultipathDescriptor::new(owner_key, heir_key, timelock).unwrap_err();
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("[00aabb44]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
MultipathDescriptor::new(owner_key, heir_key, timelock).unwrap_err();
|
||||
let owner_key = descriptor::DescriptorPublicKey::from_str("[00aabb44]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
let heir_key = descriptor::DescriptorPublicKey::from_str("[11223344/2/98]xpub6Eze7yAT3Y1wGrnzedCNVYDXUqa9NmHVWck5emBaTbXtURbe1NWZbK9bsz1TiVE7Cz341PMTfYgFw1KdLWdzcM1UMFTcdQfCYhhXZ2HJvTW/<0;1>/*").unwrap();
|
||||
MultipathDescriptor::new(owner_key, heir_key, timelock).unwrap_err();
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -651,15 +679,8 @@ mod tests {
|
||||
let receive_desc = desc.receive_descriptor();
|
||||
let change_desc = desc.change_descriptor();
|
||||
|
||||
// Receive and change are the same descriptor.
|
||||
assert_eq!(receive_desc.max_sat_weight(), change_desc.max_sat_weight());
|
||||
assert_eq!(
|
||||
receive_desc.spender_input_size(),
|
||||
change_desc.spender_input_size()
|
||||
);
|
||||
|
||||
// Derived or not the expected maximum satisfaction size should be the same for
|
||||
// the same descriptor.
|
||||
// the change and receive descriptor.
|
||||
assert_eq!(
|
||||
receive_desc.derive(999.into(), &secp).max_sat_weight(),
|
||||
change_desc.derive(999.into(), &secp).max_sat_weight()
|
||||
@ -681,7 +702,7 @@ mod tests {
|
||||
.map(|item| bitcoin::VarInt(stack.len() as u64).len() + item.len())
|
||||
.sum::<usize>();
|
||||
assert_eq!(
|
||||
receive_desc.spender_input_size(),
|
||||
desc.spender_input_size(),
|
||||
32 + 4 + 1 + 4 + wu_to_vb(witness_size),
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user