Skip to main content

elements_miniscript/confidential/
elip151.rs

1// Miniscript
2// Written in 2023 by Leonardo Comandini
3//
4// To the extent possible under law, the author(s) have dedicated all
5// copyright and related and neighboring rights to this software to
6// the public domain worldwide. This software is distributed without
7// any warranty.
8//
9// You should have received a copy of the CC0 Public Domain Dedication
10// along with this software.
11// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
12//
13
14//! ELIP151
15//!
16//! Implementation of the ELIP151 protocol, documented at
17//! <https://github.com/ElementsProject/ELIPs/blob/main/elip-0151.md>
18//!
19
20use 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    /// ELIP-151 Deterministic descriptor blinding keys
35    #[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        // Handle multi-path
50        let script_pubkeys: Vec<_> = descriptor
51            .clone()
52            .into_single_descriptors()
53            .expect("valid descriptor")
54            .iter()
55            .map(|descriptor| {
56                // Remove wildcards
57                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        // This computes mod n
78        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        // Single view keys are displayed as hex (not WIF) so we can choose any netowrk here
83        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    /// The SHA-256 initial midstate value for the [`Elip151Hash`].
110    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        // Check that cached midstate is computed correctly, code from rust-bitcoin
119        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        // Test empty hash
126        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            // Uncomment this and below to regenerate test vectors; to see the output, run
190            // cargo test test_vectors_elip151 -- --nocapture
191            /*
192            _i = _i + 1;
193            println!("* Test vector {}", _i);
194            println!("** Ordinary descriptor: <code>{}</code>", add_checksum(desc));
195            println!("** Derived descriptor blinding key: <code>{}</code>", conf_desc.key);
196            println!("** Derived confidential descriptor: <code>{}</code>", conf_desc);
197            println!("** Derived confidential descriptor (equivalent version): <code>{}</code>", elip151_desc);
198            println!("** First address: <code>{}</code>", _first_address(&conf_desc))
199            */
200        }
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            /*
212            _i = _i + 1;
213            println!("* Invalid Test vector {}", _i);
214            println!("** Ordinary descriptor: <code>{}</code>", add_checksum(invalid_desc));
215            println!("** Invalid confidential descriptor: <code>{}</code>", add_checksum(&format!("ct(elip151,{})", invalid_desc)));
216            */
217        }
218
219    }
220}