elements_miniscript/confidential/
elip151.rs1use bitcoin::hashes::{sha256t_hash_newtype, Hash};
21use bitcoin::secp256k1;
22use bitcoin::Network;
23use elements::encode::Encodable;
24use elements::opcodes;
25use elements::script::Builder;
26
27use crate::confidential::{Descriptor as ConfidentialDescriptor, Key};
28use crate::descriptor::{DescriptorSecretKey, SinglePriv};
29use crate::extensions::{Extension, ParseableExt};
30use crate::{Descriptor as OrdinaryDescriptor, DescriptorPublicKey, Error};
31
32sha256t_hash_newtype! {
33 pub struct Elip151Tag = hash_str("Deterministic-View-Key/1.0");
34 #[hash_newtype(forward)]
36 pub struct Elip151Hash(_);
37}
38
39impl Key {
40 pub fn from_elip151<T: Extension + ParseableExt>(
41 descriptor: &OrdinaryDescriptor<DescriptorPublicKey, T>,
42 ) -> Result<Self, Error> {
43 if !descriptor.has_wildcard() {
44 return Err(Error::Unexpected(
45 "Descriptors without wildcards are not supported in elip151".into(),
46 ));
47 }
48
49 let script_pubkeys: Vec<_> = descriptor
51 .clone()
52 .into_single_descriptors()
53 .expect("valid descriptor")
54 .iter()
55 .map(|descriptor| {
56 descriptor
58 .at_derivation_index((1 << 31) - 1)
59 .expect("index not hardened, not multi-path")
60 .script_pubkey()
61 })
62 .collect();
63
64 let mut eng = Elip151Hash::engine();
65 for script_pubkey in script_pubkeys {
66 Builder::new()
67 .push_opcode(opcodes::all::OP_INVALIDOPCODE)
68 .into_script()
69 .consensus_encode(&mut eng)
70 .expect("engines don't error");
71 script_pubkey
72 .consensus_encode(&mut eng)
73 .expect("engines don't error");
74 }
75 let hash_bytes = Elip151Hash::from_engine(eng).to_byte_array();
76
77 let scalar = secp256k1::scalar::Scalar::from_be_bytes(hash_bytes).expect("bytes from hash");
79 let secret_key =
80 secp256k1::SecretKey::from_slice(&scalar.to_be_bytes()).expect("bytes from scalar");
81
82 let network = Network::Bitcoin;
84 Ok(Key::View(DescriptorSecretKey::Single(SinglePriv {
85 origin: None,
86 key: bitcoin::key::PrivateKey::new(secret_key, network),
87 })))
88 }
89}
90
91impl<T: Extension + ParseableExt> ConfidentialDescriptor<DescriptorPublicKey, T> {
92 pub fn with_elip151_descriptor_blinding_key(
93 descriptor: OrdinaryDescriptor<DescriptorPublicKey, T>,
94 ) -> Result<Self, Error> {
95 Ok(ConfidentialDescriptor {
96 key: Key::from_elip151(&descriptor)?,
97 descriptor,
98 })
99 }
100}
101
102#[cfg(test)]
103mod test {
104 use super::*;
105 use crate::descriptor::checksum::desc_checksum;
106 use bitcoin::hashes::{sha256, HashEngine, sha256t::Tag};
107 use std::str::FromStr;
108
109 const MIDSTATE_ELIP151: [u8; 32] = [
111 0x49, 0x81, 0x61, 0xd8, 0x52, 0x45, 0xf7, 0xaa, 0xd8, 0x24, 0x27, 0xb5, 0x64, 0x69, 0xe7,
112 0xd6, 0x98, 0x17, 0xeb, 0x0f, 0x27, 0x14, 0x6f, 0x4e, 0x7b, 0x95, 0xb3, 0x6e, 0x46, 0xc1,
113 0xb5, 0x61,
114 ];
115
116 #[test]
117 fn tagged_hash_elip151() {
118 let mut engine = sha256::Hash::engine();
120 let tag_hash = sha256::Hash::hash(b"Deterministic-View-Key/1.0");
121 engine.input(&tag_hash[..]);
122 engine.input(&tag_hash[..]);
123 assert_eq!(MIDSTATE_ELIP151, engine.midstate().to_byte_array());
124
125 let expected = "dcd8403dcf5af960f69fa41d114931a840877dfb5378046018f78ea894a36ebd";
127 assert_eq!(Elip151Hash::from_engine(Elip151Tag::engine()).to_string(), expected);
128 assert_eq!(Elip151Hash::hash(&[]).to_string(), expected);
129 }
130
131 fn add_checksum(desc: &str) -> String {
132 if desc.find('#').is_some() {
133 desc.into()
134 } else {
135 format!("{}#{}", desc, desc_checksum(desc).unwrap())
136 }
137 }
138
139 fn confidential_descriptor(
140 desc: &str,
141 ) -> Result<ConfidentialDescriptor<DescriptorPublicKey>, Error> {
142 let desc = add_checksum(desc);
143 let desc = OrdinaryDescriptor::<DescriptorPublicKey>::from_str(&desc)?;
144 ConfidentialDescriptor::with_elip151_descriptor_blinding_key(desc)
145 }
146
147 fn _first_address(desc: &ConfidentialDescriptor<DescriptorPublicKey>) -> String {
148 let single_desc = if desc.descriptor.is_multipath() {
149 let descriptor = desc
150 .descriptor
151 .clone()
152 .into_single_descriptors()
153 .unwrap()
154 .first()
155 .unwrap()
156 .clone();
157 ConfidentialDescriptor {
158 key: desc.key.clone(),
159 descriptor,
160 }
161 } else {
162 desc.clone()
163 };
164 let definite_desc = single_desc.at_derivation_index(0).unwrap();
165 let secp = elements::secp256k1_zkp::Secp256k1::new();
166 let params = &elements::AddressParams::LIQUID;
167 definite_desc.address(&secp, params).unwrap().to_string()
168 }
169
170 #[test]
171 fn test_vectors_elip151() {
172 let xpub = "xpub661MyMwAqRbcFkPHucMnrGNzDwb6teAX1RbKQmqtEF8kK3Z7LZ59qafCjB9eCRLiTVG3uxBxgKvRgbubRhqSKXnGGb1aoaqLrpMBDrVxga8";
173 let pubkey = "03d902f35f560e0470c63313c7369168d9d7df2d49bf295fd9fb7cb109ccee0494";
174
175 let mut _i = 0;
176 for (desc, key) in [
177 (&format!("elwpkh({xpub}/<0;1>/*)"), "b3baf94d60cf8423cd257283575997a2c00664ced3e8de00f8726703142b1989"),
178 (&format!("elwpkh({xpub}/0/*)"), "de9c5fb624154624146a8aea0489b30f05c720eed6b493b1f3ab63405a11bf37"),
179 (&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1>/*))"), "7fcc1b9a20bbf611d157016192a7d28e353033cfa6a4885b3c48fa5ff9ce1881"),
180 (&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<1;2>/*))"), "ff0a08050417f0ca95fb6ef7df979ae464739cb79b8c8f4b05408e0ac681a527"),
181 ] {
182 let conf_desc = confidential_descriptor(desc).unwrap();
183 let elip151_desc = add_checksum(&format!("ct(elip151,{})", desc));
184 let conf_desc_elip151 =
185 ConfidentialDescriptor::<DescriptorPublicKey>::from_str(&elip151_desc).unwrap();
186 assert_eq!(conf_desc, conf_desc_elip151);
187 assert_eq!(conf_desc.key.to_string(), key);
188
189 }
201
202 _i = 0;
203 let text = "Descriptors without wildcards are not supported in elip151".to_string();
204 for (invalid_desc, expected_err) in [
205 (&format!("elwpkh({xpub})"), Error::Unexpected(text.to_string())),
206 (&format!("elwpkh({pubkey})"), Error::Unexpected(text.to_string())),
207 (&format!("elwsh(multi(2,{xpub}/<0;1>/*,{xpub}/0/<0;1;2>/*))"), Error::MultipathDescLenMismatch),
208 ] {
209 let err = confidential_descriptor(invalid_desc).unwrap_err();
210 assert_eq!(err, expected_err);
211 }
218
219 }
220}