1mod bip32;
4mod bip44;
5mod ext_pk;
6mod ext_sk;
7mod id;
8mod networks;
9mod pk;
10mod sig;
11mod sk;
12
13use super::*;
14
15use digest::generic_array::{typenum::U20, GenericArray};
16use digest::FixedOutput;
17use ripemd::Ripemd160;
18use sha2::{Digest, Sha256};
19
20fn hash160<B: AsRef<[u8]>>(input: B) -> GenericArray<u8, U20> {
21 let mut inner_hasher = Sha256::default();
22 inner_hasher.update(input);
23 let mut outer_hasher = Ripemd160::default();
24 outer_hasher.update(inner_hasher.finalize_fixed());
25 outer_hasher.finalize_fixed()
26}
27
28const CHECKSUM_LEN: usize = 4;
29
30pub fn to_base58check<D: AsRef<[u8]>>(data: D) -> String {
34 let data = data.as_ref();
35 let mut inner_hasher = Sha256::default();
36 inner_hasher.update(data);
37 let mut outer_hasher = Sha256::default();
38 outer_hasher.update(inner_hasher.finalize_fixed());
39 let hash = outer_hasher.finalize_fixed();
40 let checksum = &hash[..CHECKSUM_LEN];
41 let mut bytes = Vec::with_capacity(data.len() + checksum.len());
42 bytes.extend_from_slice(data);
43 bytes.extend_from_slice(checksum);
44
45 let prefixed_enc = multibase::encode(multibase::Base::Base58Btc, &bytes);
48 prefixed_enc[1..].to_owned()
49}
50
51pub fn from_base58check<S: AsRef<str>>(s: S) -> Result<Vec<u8>> {
54 let mut to_decode = String::new();
55 to_decode.push(multibase::Base::Base58Btc.code());
56 to_decode += s.as_ref();
57 let (_base, checked_data) = multibase::decode(&to_decode)?;
58 let (data, actual_checksum) = checked_data.split_at(checked_data.len() - CHECKSUM_LEN);
59
60 let mut inner_hasher = Sha256::default();
61 inner_hasher.update(data);
62 let mut outer_hasher = Sha256::default();
63 outer_hasher.update(inner_hasher.finalize_fixed());
64 let hash = outer_hasher.finalize_fixed();
65 let expected_checksum = &hash[..CHECKSUM_LEN];
66
67 ensure!(expected_checksum == actual_checksum, "Incorrect checksum");
68
69 Ok(data.to_vec())
70}
71
72#[derive(Clone, Debug)]
75pub struct Secp256k1;
76
77impl Secp256k1 {
78 fn hash_message<D: AsRef<[u8]>>(data: D) -> secp::Message {
79 let mut hasher = Sha256::default();
80 hasher.update(data.as_ref());
81 let mut hash = [0u8; secp::util::MESSAGE_SIZE];
82 hash.copy_from_slice(hasher.finalize_fixed().as_slice());
83 secp::Message::parse(&hash)
84 }
85}
86
87pub use bip32::*;
88pub use bip44::*;
89pub use cc::{ChainCode, CHAIN_CODE_SIZE};
90pub use ext_pk::SecpExtPublicKey;
91pub use ext_sk::SecpExtPrivateKey;
92pub use id::{SecpKeyId, KEY_ID_SIZE, KEY_ID_VERSION1};
93pub use networks::{ark, btc, hyd, iop};
94pub use pk::{SecpPublicKey, PUBLIC_KEY_SIZE, PUBLIC_KEY_UNCOMPRESSED_SIZE};
95pub use sig::{SecpSignature, SIGNATURE_SIZE, SIGNATURE_VERSION1};
96pub use sk::{SecpPrivateKey, PRIVATE_KEY_SIZE};
97
98impl AsymmetricCrypto for Secp256k1 {
99 type KeyId = SecpKeyId;
100 type PublicKey = SecpPublicKey;
101 type PrivateKey = SecpPrivateKey;
102 type Signature = SecpSignature;
103}
104
105impl KeyDerivationCrypto for Secp256k1 {
106 type ExtendedPrivateKey = SecpExtPrivateKey;
107 type ExtendedPublicKey = SecpExtPublicKey;
108
109 fn master(seed: &Seed) -> SecpExtPrivateKey {
110 SecpExtPrivateKey::from_seed(seed.as_bytes())
111 }
112}
113
114pub const VERSION_SIZE: usize = 1;
118
119pub const SLIP10_SEED_HASH_SALT: &[u8] = b"Bitcoin seed";
122
123#[allow(non_camel_case_types)]
127#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
128pub enum Bip178 {
129 Uncompressed,
133 Compressed,
136 P2PKH_Only,
139 P2WPKH,
142 P2WPKH_P2SH,
145}
146
147impl Bip178 {
148 pub fn to_wif_suffix(self) -> &'static [u8] {
150 use Bip178::*;
151 match self {
152 Uncompressed => b"",
153 Compressed => b"\x01",
154 P2PKH_Only => b"\x10",
155 P2WPKH => b"\x11",
156 P2WPKH_P2SH => b"\x12",
157 }
158 }
159
160 pub fn from_wif_suffix(data: &[u8]) -> Result<Self> {
162 use Bip178::*;
163 match data {
164 b"" => Ok(Uncompressed),
165 b"\x01" => Ok(Compressed),
166 b"\x10" => Ok(P2PKH_Only),
167 b"\x11" => Ok(P2WPKH),
168 b"\x12" => Ok(P2WPKH_P2SH),
169 _ => Err(anyhow!("Unknown wif suffix {}", hex::encode(data))),
170 }
171 }
172}
173
174#[cfg(test)]
175mod test {
176
177 #[test]
178 fn invalid_private_key() {
179 use super::SecpPrivateKey;
180 let sk_bytes =
181 hex::decode("0000000000000000000000000000000000000000000000000000000000000000")
182 .unwrap();
183 let err = SecpPrivateKey::from_bytes(sk_bytes).err().unwrap();
184 assert!(err.to_string().contains("Invalid secret key"))
185 }
186
187 mod sign_verify {
188 use crate::secp256k1::{SecpPrivateKey, SecpSignature};
189 use crate::{PrivateKey, PublicKey};
190
191 fn test(sk_hex: &str, msg: &[u8], sig_hex: &str) {
192 let sk_bytes = hex::decode(sk_hex).unwrap();
193 let sk = SecpPrivateKey::from_bytes(sk_bytes).unwrap();
194
195 let sig = sk.sign(msg);
196 let sig_bytes = sig.to_bytes();
197 assert_eq!(hex::encode(&sig_bytes), sig_hex);
198
199 let sig2 = SecpSignature::from_bytes(&sig_bytes).unwrap();
200 let pk = sk.public_key();
201 assert!(pk.verify(msg, &sig2));
202 }
203
204 #[test]
205 fn test_1() {
206 test(
207 "0000000000000000000000000000000000000000000000000000000000000001",
208 b"Satoshi Nakamoto",
209 "01934b1ea10a4b3c1757e2b0c017d0b6143ce3c9a7e6a4a49860d7a6ab210ee3d82442ce9d2b916064108014783e923ec36b49743e2ffa1c4496f01a512aafd9e5",
210 );
211 }
212 }
213
214 mod slip10_test_vectors {
218 use crate::{
219 secp256k1::{btc, Network, Secp256k1, SecpExtPrivateKey},
220 ExtendedPrivateKey, KeyDerivationCrypto, Seed,
221 };
222 struct TestDerivation {
223 xprv: SecpExtPrivateKey,
224 }
225
226 impl TestDerivation {
227 fn new(seed_hex: &str) -> Self {
228 let seed_bytes = hex::decode(seed_hex).unwrap();
229 let seed = Seed::from_bytes(&seed_bytes).unwrap();
230 let master = Secp256k1::master(&seed);
231 Self { xprv: master }
232 }
233
234 fn assert_state(&self, xpub_str: &str, xprv_str: &str) {
235 let xpub = self.xprv.neuter();
236
237 assert_eq!(xpub.to_xpub(&btc::Mainnet.bip32_xpub()), xpub_str);
238 assert_eq!(self.xprv.to_xprv(&btc::Mainnet.bip32_xprv()), xprv_str);
239 }
240
241 fn derive_hardened(&mut self, idx: i32) {
242 let xprv = self.xprv.derive_hardened_child(idx).unwrap();
243 self.xprv = xprv;
244 }
245
246 fn derive_normal(&mut self, idx: i32) {
247 let xprv = self.xprv.derive_normal_child(idx).unwrap();
248 self.xprv = xprv;
249 }
250 }
251
252 #[test]
253 fn test_vector_2() {
254 let mut t = TestDerivation::new("fffcf9f6f3f0edeae7e4e1dedbd8d5d2cfccc9c6c3c0bdbab7b4b1aeaba8a5a29f9c999693908d8a8784817e7b7875726f6c696663605d5a5754514e4b484542");
255 t.assert_state("xpub661MyMwAqRbcFW31YEwpkMuc5THy2PSt5bDMsktWQcFF8syAmRUapSCGu8ED9W6oDMSgv6Zz8idoc4a6mr8BDzTJY47LJhkJ8UB7WEGuduB", "xprv9s21ZrQH143K31xYSDQpPDxsXRTUcvj2iNHm5NUtrGiGG5e2DtALGdso3pGz6ssrdK4PFmM8NSpSBHNqPqm55Qn3LqFtT2emdEXVYsCzC2U");
256 t.derive_normal(0);
257 t.assert_state("xpub69H7F5d8KSRgmmdJg2KhpAK8SR3DjMwAdkxj3ZuxV27CprR9LgpeyGmXUbC6wb7ERfvrnKZjXoUmmDznezpbZb7ap6r1D3tgFxHmwMkQTPH", "xprv9vHkqa6EV4sPZHYqZznhT2NPtPCjKuDKGY38FBWLvgaDx45zo9WQRUT3dKYnjwih2yJD9mkrocEZXo1ex8G81dwSM1fwqWpWkeS3v86pgKt");
258 t.derive_hardened(2_147_483_647);
259 t.assert_state("xpub6ASAVgeehLbnwdqV6UKMHVzgqAG8Gr6riv3Fxxpj8ksbH9ebxaEyBLZ85ySDhKiLDBrQSARLq1uNRts8RuJiHjaDMBU4Zn9h8LZNnBC5y4a", "xprv9wSp6B7kry3Vj9m1zSnLvN3xH8RdsPP1Mh7fAaR7aRLcQMKTR2vidYEeEg2mUCTAwCd6vnxVrcjfy2kRgVsFawNzmjuHc2YmYRmagcEPdU9");
260 t.derive_normal(1);
261 t.assert_state("xpub6DF8uhdarytz3FWdA8TvFSvvAh8dP3283MY7p2V4SeE2wyWmG5mg5EwVvmdMVCQcoNJxGoWaU9DCWh89LojfZ537wTfunKau47EL2dhHKon", "xprv9zFnWC6h2cLgpmSA46vutJzBcfJ8yaJGg8cX1e5StJh45BBciYTRXSd25UEPVuesF9yog62tGAQtHjXajPPdbRCHuWS6T8XA2ECKADdw4Ef");
262 t.derive_hardened(2_147_483_646);
263 t.assert_state("xpub6ERApfZwUNrhLCkDtcHTcxd75RbzS1ed54G1LkBUHQVHQKqhMkhgbmJbZRkrgZw4koxb5JaHWkY4ALHY2grBGRjaDMzQLcgJvLJuZZvRcEL", "xprvA1RpRA33e1JQ7ifknakTFpgNXPmW2YvmhqLQYMmrj4xJXXWYpDPS3xz7iAxn8L39njGVyuoseXzU6rcxFLJ8HFsTjSyQbLYnMpCqE2VbFWc");
264 t.derive_normal(2);
265 t.assert_state("xpub6FnCn6nSzZAw5Tw7cgR9bi15UV96gLZhjDstkXXxvCLsUXBGXPdSnLFbdpq8p9HmGsApME5hQTZ3emM2rnY5agb9rXpVGyy3bdW6EEgAtqt", "xprvA2nrNbFZABcdryreWet9Ea4LvTJcGsqrMzxHx98MMrotbir7yrKCEXw7nadnHM8Dq38EGfSh6dqA9QWTyefMLEcBYJUuekgW4BYPJcr9E7j");
266 }
267
268 #[test]
269 fn test_vector_3() {
270 let mut t = TestDerivation::new("4b381541583be4423346c643850da4b320e46a87ae3d2a4e6da11eba819cd4acba45d239319ac14f863b8d5ab5a0d0c64d2e8a1e7d1457df2e5a3c51c73235be");
271 t.assert_state("xpub661MyMwAqRbcEZVB4dScxMAdx6d4nFc9nvyvH3v4gJL378CSRZiYmhRoP7mBy6gSPSCYk6SzXPTf3ND1cZAceL7SfJ1Z3GC8vBgp2epUt13", "xprv9s21ZrQH143K25QhxbucbDDuQ4naNntJRi4KUfWT7xo4EKsHt2QJDu7KXp1A3u7Bi1j8ph3EGsZ9Xvz9dGuVrtHHs7pXeTzjuxBrCmmhgC6");
272 t.derive_hardened(0);
273 t.assert_state("xpub68NZiKmJWnxxS6aaHmn81bvJeTESw724CRDs6HbuccFQN9Ku14VQrADWgqbhhTHBaohPX4CjNLf9fq9MYo6oDaPPLPxSb7gwQN3ih19Zm4Y", "xprv9uPDJpEQgRQfDcW7BkF7eTya6RPxXeJCqCJGHuCJ4GiRVLzkTXBAJMu2qaMWPrS7AANYqdq6vcBcBUdJCVVFceUvJFjaPdGZ2y9WACViL4L");
274 }
275 }
276
277 mod btc_key_conversions {
285 use crate::secp256k1::{btc, Bip178, Network, SecpPrivateKey};
286 use crate::{PrivateKey, PublicKey};
287
288 fn test(sk_hex: &str, wif: &str, pk_hex: &str, id_hex: &str, address: &str) {
289 let sk_bytes = hex::decode(sk_hex).unwrap();
290 let sk = SecpPrivateKey::from_bytes(sk_bytes).unwrap();
291
292 let sk_wif = sk.to_wif(&btc::Mainnet.wif(), Bip178::Compressed);
293 assert_eq!(sk_wif, wif);
294
295 let pk = sk.public_key();
296 let pk_bytes = pk.to_bytes();
297 assert_eq!(hex::encode(&pk_bytes), pk_hex);
298
299 let id = pk.key_id();
300 let id_bytes = id.to_bytes();
301 assert_eq!(hex::encode(&id_bytes), id_hex);
302
303 let act_address = id.to_p2pkh_addr(&btc::Mainnet.p2pkh_addr());
304 assert_eq!(act_address, address);
305 }
306
307 #[test]
308 fn test_1() {
309 test(
310 "0000000000000000000000000000000000000000000000000000000000000001",
311 "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU73sVHnoWn",
312 "0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798",
313 "01751e76e8199196d454941c45d1b3a323f1433bd6",
314 "1BgGZ9tcN4rm9KBzDn7KprQz87SZ26SAMH",
315 );
316 }
317 #[test]
318 fn test_2() {
319 test(
320 "0000000000000000000000000000000000000000000000000000000000000002",
321 "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU74NMTptX4",
322 "02c6047f9441ed7d6d3045406e95c07cd85c778e4b8cef3ca7abac09b95c709ee5",
323 "0106afd46bcdfd22ef94ac122aa11f241244a37ecc",
324 "1cMh228HTCiwS8ZsaakH8A8wze1JR5ZsP",
325 );
326 }
327 #[test]
328 fn test_3() {
329 test(
330 "0000000000000000000000000000000000000000000000000000000000000003",
331 "KwDiBf89QgGbjEhKnhXJuH7LrciVrZi3qYjgd9M7rFU74sHUHy8S",
332 "02f9308a019258c31049344f85f89d5229b531c845836f99b08601f113bce036f9",
333 "017dd65592d0ab2fe0d0257d571abf032cd9db93dc",
334 "1CUNEBjYrCn2y1SdiUMohaKUi4wpP326Lb",
335 );
336 }
337 #[test]
338 fn test_4() {
339 test(
340 "aa5e28d6a97a2479a65527f7290311a3624d4cc0fa1578598ee3c2613bf99522",
341 "L2vtCpubwLeqLNYywTUqLLmN6LiijyYWUArxvyw5DyFD8TaxqJyu",
342 "0234f9460f0e4f08393d192b3c5133a6ba099aa0ad9fd54ebccfacdfa239ff49c6",
343 "01db820065e5bd79e976f0dc09f2257e35243879cf",
344 "1M1eigHFbhtWLnc37qXQt1ao2taLhE49yg",
345 );
346 }
347 #[test]
348 fn test_5() {
349 test(
350 "7e2b897b8cebc6361663ad410835639826d590f393d90a9538881735256dfae3",
351 "L1Sy9ysFzZDXh5gXYrgJmbyhnhbJVyptuTypUnD9ofZoV3V2SpUi",
352 "03d74bf844b0862475103d96a611cf2d898447e288d34b360bc885cb8ce7c00575",
353 "015716c6c9146a548ce31092f72ab24b44d8580914",
354 "18wV5EG3Hqocod1RLm9STvbUnSqb1NMo44",
355 );
356 }
357 #[test]
358 fn test_6() {
359 test(
360 "6461e6df0fe7dfd05329f41bf771b86578143d4dd1f7866fb4ca7e97c5fa945d",
361 "Kzaqk53898thvqucDWi4MqC3ogC2s2QmtZL31qjS9MRMvgHFKpDZ",
362 "03e8aecc370aedd953483719a116711963ce201ac3eb21d3f3257bb48668c6a72f",
363 "01e3281990058f008a4b6c658cb735ae2b7327daa5",
364 "1Mi6RjU7ASvudQMZkeobQ1WoiZWAtVhkd6",
365 );
366 }
367 }
368
369 mod blockchain_com_derivation {
370 use crate::secp256k1::*;
371
372 #[test]
373 fn btc_wallet() {
374 let phrase =
375 "hint replace increase neglect egg wood ill alert beef rich install potato";
376 let xpub = "xpub6DVu7eWDWJkczyrDQdo2i99MKdns8idTeVfzwuHCaA3rKfSWaSCnpKigU21vJ2TdCT5MiLgbKWzpxZUx4gFx6zpNWukDzX8yRGU3UKyE9fC";
377 let xpub2 = "xpub6DVu7eWDWJkd4MbGacHiWGFKT2amHnJwQ7SmxWpD2YuacVtaQz7XPsRYxR5PqsGa2TiLYWPi3jsimnruvJ5LvMBxjc96jgZniP7peLHcEdM";
378 let old_addr0 = "17kxMsME7f8CVVWqPadgQNsDEMaHDBpCbv";
379 let bip44_addr0 = "17Y82siUGdY8KEWGpUTW7uF5kj6cEMRYfo";
380 let net = &btc::Mainnet;
381
382 let seed = Bip39::new().short_phrase(phrase).unwrap().password("");
383 let coin = Bip44.network(&seed, net).unwrap();
384 let account: Bip44Account<Secp256k1> = coin.account(0).unwrap();
385
386 assert_eq!(account.neuter().to_xpub(), xpub);
387
388 let old_pk0 = account.node().derive_normal(0).unwrap().neuter();
389
390 assert_eq!(old_pk0.to_p2pkh_addr(net), old_addr0);
391
392 let pk0 = account.key(0).unwrap().neuter();
393
394 assert_eq!(pk0.to_p2pkh_addr(), bip44_addr0);
395
396 let account2 = coin.account(1).unwrap();
397
398 assert_eq!(account2.neuter().to_xpub(), xpub2);
399 }
400 }
401
402 mod coinomi_derivation {
403 use crate::{
404 secp256k1::{btc, hyd},
405 *,
406 };
407
408 #[test]
409 fn hyd_derive() {
410 let mnemonic = "blast cargo razor option vote shoe stock cruel mansion boy spot never album crop reflect kangaroo blouse slam empty shoot cable vital crane manual";
411 let pk0_hex = "02f946d10106f55c755c1f836b63bef35fb0015603e1870c8dbdcacf62f178587e";
412 let addr0 = "hWNN8ymcsLdJivbwbBaPS8X1vekxB2pdwV";
413
414 let seed = Bip39::new().phrase(mnemonic).unwrap().password(Seed::PASSWORD);
415 let account = Bip44.network(&seed, &hyd::Mainnet).unwrap().account(0).unwrap();
416 let key0 = account.key(0).unwrap().neuter();
417
418 assert_eq!(hex::encode(key0.to_public_key().to_bytes()), pk0_hex);
419 assert_eq!(key0.to_p2pkh_addr(), addr0);
420 }
421
422 #[test]
423 fn tbtc_derive() {
424 let mnemonic = "blast cargo razor option vote shoe stock cruel mansion boy spot never album crop reflect kangaroo blouse slam empty shoot cable vital crane manual";
425 let xpub = "tpubDDfA4LQYyG71KmPW65gktxjvzxFAFcdhDAzj4zc6y5hpeX3rZu3nPh1GuvgWCyj4VWKfuFbnnCvFXyTuDLD6mmFA5yVTe2UUcSoNy7kgcYm";
426 let xpub_coinomi_bug = "xpub6DHXY6asFz9Z3yCedthfh3QZfq9WGE8dfYQRiSYxdAwvuEP9VgChFFLSftbigUmphbnmHg5vF76CqnyHpUMrtKns2Nk9xVpF2VCyD7Uej6C";
427 let addr0 = "mgH9VjC6uGTt1cWDDZEXisASAtCE8D6Xar";
428 let net = &btc::Testnet;
429
430 let seed = Bip39::new().phrase(mnemonic).unwrap().password(Seed::PASSWORD);
431 let account = Bip44.network(&seed, net).unwrap().account(0).unwrap().neuter();
432
433 assert_eq!(account.to_xpub(), xpub);
434 assert_eq!(account.node().to_xpub(&btc::Mainnet), xpub_coinomi_bug);
435
436 let key0 = account.key(0).unwrap();
437
438 assert_eq!(key0.to_p2pkh_addr(), addr0);
439 }
440 }
441
442 mod ark_desktop_hyd_address {
443 use super::super::*;
444
445 #[test]
446 fn test() {
447 let phrase = "boss slice draft close detail mix nation casino judge cigar melody catch";
448 let pk_hex = "03fdd041ed3e51d8909c44ef6b9d1268a412161d8e6544c5cd4d87ef78bb49e2f7";
449 let addr = "hFxvDfqQfXHzhvEebTJGkds8DBBGBCKpY9";
450 let addr_dev = "dEatNarXZifEXaqnMFy5uP9Fv8bq7aB3Vn";
451 let addr_test = "tXRoniBUYaGXc495HCdCL9V9qJPgBMywaH";
452
453 let sk = SecpPrivateKey::from_ark_passphrase(phrase).unwrap();
454 let pk = sk.public_key();
455
456 assert_eq!(hex::encode(pk.to_bytes()), pk_hex);
457
458 let key_id = pk.ark_key_id();
459
460 assert_eq!(key_id.to_p2pkh_addr(&hyd::Mainnet.p2pkh_addr()), addr);
461 assert_eq!(key_id.to_p2pkh_addr(&hyd::Devnet.p2pkh_addr()), addr_dev);
462 assert_eq!(key_id.to_p2pkh_addr(&hyd::Testnet.p2pkh_addr()), addr_test);
463 }
464 }
465
466 mod ark_key_conversions {
469 use super::super::*;
470
471 fn test(passphrase: &str, pk_hex: &str, main_addr: &str, dev_addr: &str) {
472 let sk = SecpPrivateKey::from_ark_passphrase(passphrase).unwrap();
473 let pk = sk.public_key();
474 assert_eq!(hex::encode(pk.to_bytes()), pk_hex);
475
476 let key_id = pk.ark_key_id();
477
478 assert_eq!(key_id.to_p2pkh_addr(&ark::Mainnet.p2pkh_addr()), main_addr);
479 assert_eq!(key_id.to_p2pkh_addr(&ark::Devnet.p2pkh_addr()), dev_addr);
480 }
481
482 #[test]
483 fn test_delegate_1() {
484 test(
485 "clay harbor enemy utility margin pretty hub comic piece aerobic umbrella acquire",
486 "03287bfebba4c7881a0509717e71b34b63f31e40021c321f89ae04f84be6d6ac37",
487 "ANBkoGqWeTSiaEVgVzSKZd3jS7UWzv9PSo",
488 "DBYyh2vXcigrJGUHfvmYxVxEqeH7vomw6x",
489 );
490 }
491
492 #[test]
493 fn test_delegate_2() {
494 test(
495 "venue below waste gather spin cruise title still boost mother flash tuna",
496 "02def27da9336e7fbf63131b8d7e5c9f45b296235db035f1f4242c507398f0f21d",
497 "AbfQq8iRSf9TFQRzQWo33dHYU7HFMS17Zd",
498 "DR2ditoSQvPaySQbaT8GSWC3se5rLyfq4T",
499 );
500 }
501 }
502}