• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

stacks-network / stacks-core / 24140301216

08 Apr 2026 02:18PM UTC coverage: 46.817% (-38.9%) from 85.712%
24140301216

Pull #6959

github

279acf
web-flow
Merge efbee1783 into 882e27245
Pull Request #6959: Perf/cache epoch version in ClarityDatabase

66 of 149 new or added lines in 8 files covered. (44.3%)

85999 existing lines in 334 files now uncovered.

102056 of 217989 relevant lines covered (46.82%)

12315981.16 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

34.91
/stacks-node/src/keychain.rs
1
use stacks::burnchains::BurnchainSigner;
2
use stacks::chainstate::stacks::{
3
    StacksPrivateKey, StacksPublicKey, StacksTransactionSigner, TransactionAuth,
4
};
5
use stacks_common::address::{
6
    AddressHashMode, C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
7
};
8
use stacks_common::types::chainstate::StacksAddress;
9
use stacks_common::util::hash::{Hash160, Sha256Sum};
10
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
11
use stacks_common::util::vrf::{VRFPrivateKey, VRFProof, VRFPublicKey, VRF};
12

13
use super::operations::BurnchainOpSigner;
14

15
/// A wrapper around a node's seed, coupled with operations for using it
16
#[derive(Clone)]
17
pub struct Keychain {
18
    secret_state: Vec<u8>,
19
    nakamoto_mining_key: Secp256k1PrivateKey,
20
}
21

22
impl Keychain {
23
    /// Create a secret key from some state.
24
    /// Returns the bytes that can be fed into StacksPrivateKey
25
    fn make_secret_key_bytes(seed: &[u8]) -> Vec<u8> {
308,860✔
26
        let mut re_hashed_seed = seed.to_vec();
308,860✔
27
        loop {
28
            match StacksPrivateKey::from_slice(&re_hashed_seed[..]) {
310,482✔
29
                Ok(_sk) => {
308,860✔
30
                    break;
308,860✔
31
                }
32
                Err(_) => {
33
                    re_hashed_seed = Sha256Sum::from_data(&re_hashed_seed[..])
1,622✔
34
                        .as_bytes()
1,622✔
35
                        .to_vec()
1,622✔
36
                }
37
            }
38
        }
39
        re_hashed_seed
308,860✔
40
    }
308,860✔
41

42
    /// Create a secret key from our secret state
43
    fn get_secret_key(&self) -> StacksPrivateKey {
281,247✔
44
        let sk_bytes = Keychain::make_secret_key_bytes(&self.secret_state);
281,247✔
45
        StacksPrivateKey::from_slice(&sk_bytes[..]).expect("FATAL: Keychain::make_secret_key_bytes() returned bytes that could not be parsed into a secp256k1 secret key!")
281,247✔
46
    }
281,247✔
47

48
    /// Get the public key hash of the nakamoto mining key (i.e., Hash160(pubkey))
49
    pub fn get_nakamoto_pkh(&self) -> Hash160 {
1,776✔
50
        let pk = Secp256k1PublicKey::from_private(&self.nakamoto_mining_key);
1,776✔
51
        Hash160::from_node_public_key(&pk)
1,776✔
52
    }
1,776✔
53

54
    /// Get the secret key of the nakamoto mining key
55
    pub fn get_nakamoto_sk(&self) -> &Secp256k1PrivateKey {
2,321✔
56
        &self.nakamoto_mining_key
2,321✔
57
    }
2,321✔
58

59
    /// Set the secret key of the nakamoto mining key
60
    pub fn set_nakamoto_sk(&mut self, mining_key: Secp256k1PrivateKey) {
239✔
61
        self.nakamoto_mining_key = mining_key;
239✔
62
    }
239✔
63

64
    /// Create a default keychain from the seed, with a default nakamoto mining key derived
65
    ///  from the same seed (
66
    pub fn default(seed: Vec<u8>) -> Keychain {
1,649✔
67
        let secret_state = Self::make_secret_key_bytes(&seed);
1,649✔
68
        // re-hash secret_state to use as a default seed for the nakamoto mining key
69
        let nakamoto_mining_key =
1,649✔
70
            Secp256k1PrivateKey::from_seed(Sha256Sum::from_data(&secret_state).as_bytes());
1,649✔
71
        Keychain {
1,649✔
72
            secret_state,
1,649✔
73
            nakamoto_mining_key,
1,649✔
74
        }
1,649✔
75
    }
1,649✔
76

77
    /// Generate a VRF keypair for this burn block height.
78
    /// The keypair is unique to this burn block height.
79
    pub fn make_vrf_keypair(&self, block_height: u64) -> (VRFPublicKey, VRFPrivateKey) {
33,470✔
80
        let mut seed = {
33,470✔
81
            let mut secret_state = self.secret_state.clone();
33,470✔
82
            secret_state.extend_from_slice(&block_height.to_be_bytes());
33,470✔
83
            Sha256Sum::from_data(&secret_state)
33,470✔
84
        };
85

86
        // Not every 256-bit number is a valid Ed25519 secret key.
87
        // As such, we continuously generate seeds through re-hashing until one works.
88
        let sk = loop {
33,470✔
89
            match VRFPrivateKey::from_bytes(seed.as_bytes()) {
33,470✔
90
                Some(sk) => break sk,
33,470✔
91
                None => seed = Sha256Sum::from_data(seed.as_bytes()),
×
92
            }
93
        };
94
        let pk = VRFPublicKey::from_private(&sk);
33,470✔
95
        (pk, sk)
33,470✔
96
    }
33,470✔
97

98
    /// Generate a Stacks keypair for this burn block height.
99
    /// The keypair is unique to this burn block height.
100
    pub fn make_stacks_keypair(
25,964✔
101
        &self,
25,964✔
102
        block_height: u64,
25,964✔
103
        salt: &[u8],
25,964✔
104
    ) -> (StacksPublicKey, StacksPrivateKey) {
25,964✔
105
        let seed = {
25,964✔
106
            let mut secret_state = self.secret_state.clone();
25,964✔
107
            secret_state.extend_from_slice(&block_height.to_be_bytes());
25,964✔
108
            secret_state.extend_from_slice(salt);
25,964✔
109
            Sha256Sum::from_data(&secret_state)
25,964✔
110
        };
111

112
        let sk_bytes = Keychain::make_secret_key_bytes(&seed.0);
25,964✔
113
        let sk = StacksPrivateKey::from_slice(&sk_bytes[..]).expect("FATAL: Keychain::make_secret_key_bytes() returned bytes that could not be parsed into a secp256k1 secret key!");
25,964✔
114
        let pk = StacksPublicKey::from_private(&sk);
25,964✔
115

116
        (pk, sk)
25,964✔
117
    }
25,964✔
118

119
    /// Generate a VRF proof over a given byte message.
120
    /// `block_height` must be the _same_ block height called to make_vrf_keypair()
121
    pub fn generate_proof(&self, block_height: u64, bytes: &[u8; 32]) -> Option<VRFProof> {
33,195✔
122
        let (pk, sk) = self.make_vrf_keypair(block_height);
33,195✔
123
        let Some(proof) = VRF::prove(&sk, bytes.as_ref()) else {
33,195✔
124
            error!(
×
125
                "Failed to generate proof with keypair, will be unable to mine.";
126
                "block_height" => block_height,
×
127
                "pk" => ?pk
128
            );
129
            return None;
×
130
        };
131

132
        // Ensure that the proof is valid by verifying
133
        let is_valid = VRF::verify(&pk, &proof, bytes.as_ref())
33,195✔
134
            .inspect_err(|e| {
33,195✔
135
                error!(
×
136
                    "Failed to validate generated proof, will be unable to mine.";
137
                    "block_height" => block_height,
×
138
                    "pk" => ?pk,
139
                    "err" => %e,
140
                );
141
            })
×
142
            .ok()?;
33,195✔
143
        if !is_valid {
33,195✔
144
            error!(
8✔
145
                "Generated invalidat proof, will be unable to mine.";
146
                "block_height" => block_height,
×
147
                "pk" => ?pk,
148
            );
149
            None
8✔
150
        } else {
151
            Some(proof)
33,187✔
152
        }
153
    }
33,195✔
154

155
    /// Generate a microblock signing key for this burnchain block height.
156
    /// `salt` can be any byte string; in practice, it's the parent Stacks block's block ID hash.
157
    pub fn make_microblock_secret_key(
25,953✔
158
        &mut self,
25,953✔
159
        burn_block_height: u64,
25,953✔
160
        salt: &[u8],
25,953✔
161
    ) -> StacksPrivateKey {
25,953✔
162
        let (_, mut sk) = self.make_stacks_keypair(burn_block_height, salt);
25,953✔
163
        sk.set_compress_public(true);
25,953✔
164

165
        debug!("Microblock keypair rotated";
25,953✔
166
               "burn_block_height" => %burn_block_height,
167
               "pubkey_hash" => %Hash160::from_node_public_key(&StacksPublicKey::from_private(&sk)).to_string()
×
168
        );
169
        sk
25,953✔
170
    }
25,953✔
171

172
    pub fn get_pub_key(&self) -> Secp256k1PublicKey {
624✔
173
        let sk = self.get_secret_key();
624✔
174
        StacksPublicKey::from_private(&sk)
624✔
175
    }
624✔
176

177
    /// Get the Stacks address for the inner secret state
178
    pub fn get_address(&self, is_mainnet: bool) -> StacksAddress {
25,112✔
179
        let sk = self.get_secret_key();
25,112✔
180
        let pk = StacksPublicKey::from_private(&sk);
25,112✔
181

182
        let version = if is_mainnet {
25,112✔
183
            C32_ADDRESS_VERSION_MAINNET_SINGLESIG
25,108✔
184
        } else {
185
            C32_ADDRESS_VERSION_TESTNET_SINGLESIG
4✔
186
        };
187
        StacksAddress::from_public_keys(version, &AddressHashMode::SerializeP2PKH, 1, &vec![pk])
25,112✔
188
            .expect("FATAL: could not produce address from secret key")
25,112✔
189
    }
25,112✔
190

191
    /// Get a BurnchainSigner representation of this keychain
192
    pub fn get_burnchain_signer(&self) -> BurnchainSigner {
25,108✔
193
        BurnchainSigner(format!("{}", &self.get_address(true)))
25,108✔
194
    }
25,108✔
195

196
    /// Convenience wrapper around make_stacks_keypair
197
    pub fn get_microblock_key(&self, block_height: u64) -> StacksPrivateKey {
11✔
198
        self.make_stacks_keypair(block_height, &[]).1
11✔
199
    }
11✔
200

201
    /// Sign a transaction as if we were the origin
202
    pub fn sign_as_origin(&self, tx_signer: &mut StacksTransactionSigner) {
28,635✔
203
        let sk = self.get_secret_key();
28,635✔
204
        tx_signer
28,635✔
205
            .sign_origin(&sk)
28,635✔
206
            .expect("FATAL: failed to sign transaction origin");
28,635✔
207
    }
28,635✔
208

209
    /// Create a transaction authorization struct from this keychain's secret state
210
    pub fn get_transaction_auth(&self) -> Option<TransactionAuth> {
200,802✔
211
        TransactionAuth::from_p2pkh(&self.get_secret_key())
200,802✔
212
    }
200,802✔
213

214
    /// Get the origin address that this keychain represents
215
    pub fn origin_address(&self, is_mainnet: bool) -> Option<StacksAddress> {
172,167✔
216
        match self.get_transaction_auth() {
172,167✔
217
            Some(auth) => {
172,167✔
218
                let address = if is_mainnet {
172,167✔
UNCOV
219
                    auth.origin().address_mainnet()
×
220
                } else {
221
                    auth.origin().address_testnet()
172,167✔
222
                };
223
                Some(address)
172,167✔
224
            }
225
            None => None,
×
226
        }
227
    }
172,167✔
228

229
    /// Create a BurnchainOpSigner representation of this keychain
230
    pub fn generate_op_signer(&self) -> BurnchainOpSigner {
26,074✔
231
        BurnchainOpSigner::new(self.get_secret_key())
26,074✔
232
    }
26,074✔
233
}
234

235
#[cfg(test)]
236
#[allow(dead_code)]
237
mod tests {
238
    use std::collections::HashMap;
239

240
    use stacks::burnchains::PrivateKey;
241
    use stacks::chainstate::stacks::{
242
        StacksPrivateKey, StacksPublicKey, StacksTransaction, StacksTransactionSigner,
243
        TokenTransferMemo, TransactionAuth, TransactionPayload, TransactionPostConditionMode,
244
        TransactionVersion,
245
    };
246
    use stacks_common::address::AddressHashMode;
247
    use stacks_common::types::chainstate::StacksAddress;
248
    use stacks_common::util::hash::{Hash160, Sha256Sum};
249
    use stacks_common::util::vrf::{VRFPrivateKey, VRFProof, VRFPublicKey, VRF};
250

251
    use super::Keychain;
252
    use crate::operations::BurnchainOpSigner;
253
    use crate::stacks_common::types::Address;
254

255
    /// Legacy implementation; kept around for testing
256
    #[derive(Clone)]
257
    pub struct KeychainOld {
258
        secret_keys: Vec<StacksPrivateKey>,
259
        threshold: u16,
260
        hash_mode: AddressHashMode,
261
        pub hashed_secret_state: Sha256Sum,
262
        microblocks_secret_keys: Vec<StacksPrivateKey>,
263
        vrf_secret_keys: Vec<VRFPrivateKey>,
264
        vrf_map: HashMap<VRFPublicKey, VRFPrivateKey>,
265
    }
266

267
    impl KeychainOld {
UNCOV
268
        pub fn new(
×
UNCOV
269
            secret_keys: Vec<StacksPrivateKey>,
×
UNCOV
270
            threshold: u16,
×
UNCOV
271
            hash_mode: AddressHashMode,
×
UNCOV
272
        ) -> KeychainOld {
×
273
            // Compute hashed secret state
UNCOV
274
            let hashed_secret_state = {
×
UNCOV
275
                let mut buf: Vec<u8> = secret_keys.iter().flat_map(|sk| sk.to_bytes()).collect();
×
UNCOV
276
                buf.extend_from_slice(&[
×
UNCOV
277
                    (threshold >> 8) as u8,
×
UNCOV
278
                    (threshold & 0xff) as u8,
×
UNCOV
279
                    hash_mode as u8,
×
UNCOV
280
                ]);
×
UNCOV
281
                Sha256Sum::from_data(&buf[..])
×
282
            };
283

UNCOV
284
            Self {
×
UNCOV
285
                hash_mode,
×
UNCOV
286
                hashed_secret_state,
×
UNCOV
287
                microblocks_secret_keys: vec![],
×
UNCOV
288
                secret_keys,
×
UNCOV
289
                threshold,
×
UNCOV
290
                vrf_secret_keys: vec![],
×
UNCOV
291
                vrf_map: HashMap::new(),
×
UNCOV
292
            }
×
UNCOV
293
        }
×
294

UNCOV
295
        pub fn default(seed: Vec<u8>) -> KeychainOld {
×
UNCOV
296
            let mut re_hashed_seed = seed;
×
UNCOV
297
            let secret_key = loop {
×
UNCOV
298
                match StacksPrivateKey::from_slice(&re_hashed_seed[..]) {
×
UNCOV
299
                    Ok(sk) => break sk,
×
300
                    Err(_) => {
UNCOV
301
                        re_hashed_seed = Sha256Sum::from_data(&re_hashed_seed[..])
×
UNCOV
302
                            .as_bytes()
×
UNCOV
303
                            .to_vec()
×
304
                    }
305
                }
306
            };
307

UNCOV
308
            let threshold = 1;
×
UNCOV
309
            let hash_mode = AddressHashMode::SerializeP2PKH;
×
310

UNCOV
311
            KeychainOld::new(vec![secret_key], threshold, hash_mode)
×
UNCOV
312
        }
×
313

314
        pub fn rotate_vrf_keypair(&mut self, block_height: u64) -> VRFPublicKey {
×
315
            let mut seed = {
×
316
                let mut secret_state = self.hashed_secret_state.to_bytes().to_vec();
×
317
                secret_state.extend_from_slice(&block_height.to_be_bytes());
×
318
                Sha256Sum::from_data(&secret_state)
×
319
            };
320

321
            // Not every 256-bit number is a valid Ed25519 secret key.
322
            // As such, we continuously generate seeds through re-hashing until one works.
323
            let sk = loop {
×
324
                match VRFPrivateKey::from_bytes(seed.as_bytes()) {
×
325
                    Some(sk) => break sk,
×
326
                    None => seed = Sha256Sum::from_data(seed.as_bytes()),
×
327
                }
328
            };
329
            let pk = VRFPublicKey::from_private(&sk);
×
330

331
            self.vrf_secret_keys.push(sk.clone());
×
332
            self.vrf_map.insert(pk.clone(), sk);
×
333
            pk
×
334
        }
×
335

336
        pub fn rotate_microblock_keypair(&mut self, burn_block_height: u64) -> StacksPrivateKey {
×
337
            let mut secret_state = match self.microblocks_secret_keys.last() {
×
338
                // First key is the hash of the secret state
339
                None => self.hashed_secret_state.to_bytes().to_vec(),
×
340
                // Next key is the hash of the last
341
                Some(last_sk) => last_sk.to_bytes().to_vec(),
×
342
            };
343

344
            secret_state.extend_from_slice(&burn_block_height.to_be_bytes());
×
345

346
            let mut seed = Sha256Sum::from_data(&secret_state);
×
347

348
            // Not every 256-bit number is a valid secp256k1 secret key.
349
            // As such, we continuously generate seeds through re-hashing until one works.
350
            let mut sk = loop {
×
351
                match StacksPrivateKey::from_slice(&seed.to_bytes()[..]) {
×
352
                    Ok(sk) => break sk,
×
353
                    Err(_) => seed = Sha256Sum::from_data(seed.as_bytes()),
×
354
                }
355
            };
356
            sk.set_compress_public(true);
×
357
            self.microblocks_secret_keys.push(sk.clone());
×
358

359
            debug!("Microblock keypair rotated";
×
360
                   "burn_block_height" => %burn_block_height,
361
                   "pubkey_hash" => %Hash160::from_node_public_key(&StacksPublicKey::from_private(&sk)).to_string(),);
×
362

363
            sk
×
364
        }
×
365

366
        pub fn get_microblock_key(&self) -> Option<StacksPrivateKey> {
×
367
            self.microblocks_secret_keys.last().cloned()
×
368
        }
×
369

UNCOV
370
        pub fn sign_as_origin(&self, tx_signer: &mut StacksTransactionSigner) {
×
UNCOV
371
            let num_keys = if self.secret_keys.len() < self.threshold as usize {
×
372
                self.secret_keys.len()
×
373
            } else {
UNCOV
374
                self.threshold as usize
×
375
            };
376

UNCOV
377
            for i in 0..num_keys {
×
UNCOV
378
                tx_signer.sign_origin(&self.secret_keys[i]).unwrap();
×
UNCOV
379
            }
×
UNCOV
380
        }
×
381

382
        /// Given a VRF public key, generates a VRF Proof
383
        pub fn generate_proof(&self, vrf_pk: &VRFPublicKey, bytes: &[u8; 32]) -> Option<VRFProof> {
×
384
            // Retrieve the corresponding VRF secret key
385
            let vrf_sk = match self.vrf_map.get(vrf_pk) {
×
386
                Some(vrf_pk) => vrf_pk,
×
387
                None => {
388
                    warn!("No VRF secret key on file for {vrf_pk:?}");
×
389
                    return None;
×
390
                }
391
            };
392

393
            // Generate the proof
394
            let proof = VRF::prove(vrf_sk, bytes.as_ref())?;
×
395
            // Ensure that the proof is valid by verifying
396
            let is_valid = VRF::verify(vrf_pk, &proof, bytes.as_ref()).unwrap_or(false);
×
397
            assert!(is_valid);
×
398
            Some(proof)
×
399
        }
×
400

401
        /// Given the keychain's secret keys, computes and returns the corresponding Stack address.
UNCOV
402
        pub fn get_address(&self, is_mainnet: bool) -> StacksAddress {
×
UNCOV
403
            let public_keys = self
×
UNCOV
404
                .secret_keys
×
UNCOV
405
                .iter()
×
UNCOV
406
                .map(StacksPublicKey::from_private)
×
UNCOV
407
                .collect();
×
UNCOV
408
            let version = if is_mainnet {
×
UNCOV
409
                self.hash_mode.to_version_mainnet()
×
410
            } else {
UNCOV
411
                self.hash_mode.to_version_testnet()
×
412
            };
UNCOV
413
            StacksAddress::from_public_keys(
×
UNCOV
414
                version,
×
UNCOV
415
                &self.hash_mode,
×
UNCOV
416
                self.threshold as usize,
×
UNCOV
417
                &public_keys,
×
418
            )
UNCOV
419
            .unwrap()
×
UNCOV
420
        }
×
421

UNCOV
422
        pub fn get_transaction_auth(&self) -> Option<TransactionAuth> {
×
UNCOV
423
            match self.hash_mode {
×
424
                AddressHashMode::SerializeP2PKH => {
UNCOV
425
                    TransactionAuth::from_p2pkh(&self.secret_keys[0])
×
426
                }
427
                AddressHashMode::SerializeP2SH => {
428
                    TransactionAuth::from_p2sh(&self.secret_keys, self.threshold)
×
429
                }
430
                AddressHashMode::SerializeP2WPKH => {
431
                    TransactionAuth::from_p2wpkh(&self.secret_keys[0])
×
432
                }
433
                AddressHashMode::SerializeP2WSH => {
434
                    TransactionAuth::from_p2wsh(&self.secret_keys, self.threshold)
×
435
                }
436
            }
UNCOV
437
        }
×
438

UNCOV
439
        pub fn origin_address(&self, is_mainnet: bool) -> Option<StacksAddress> {
×
UNCOV
440
            match self.get_transaction_auth() {
×
UNCOV
441
                Some(auth) => {
×
UNCOV
442
                    let address = if is_mainnet {
×
UNCOV
443
                        auth.origin().address_mainnet()
×
444
                    } else {
UNCOV
445
                        auth.origin().address_testnet()
×
446
                    };
UNCOV
447
                    Some(address)
×
448
                }
449
                None => None,
×
450
            }
UNCOV
451
        }
×
452

453
        pub fn generate_op_signer(&self) -> BurnchainOpSigner {
×
454
            BurnchainOpSigner::new(self.secret_keys[0].clone())
×
455
        }
×
456
    }
457

458
    #[test]
UNCOV
459
    fn test_origin_address() {
×
UNCOV
460
        let seeds = [
×
UNCOV
461
            [0u8; 32],
×
UNCOV
462
            [
×
UNCOV
463
                0xc2, 0x7e, 0x1d, 0x7e, 0x9a, 0x0d, 0x47, 0xfa, 0xa5, 0x10, 0xbe, 0x50, 0x9b, 0xce,
×
UNCOV
464
                0xd4, 0x95, 0x99, 0x64, 0x40, 0x34, 0xbd, 0x5a, 0xf2, 0x2b, 0x51, 0x9c, 0x21, 0x19,
×
UNCOV
465
                0xbd, 0xaa, 0x5d, 0x62,
×
UNCOV
466
            ],
×
UNCOV
467
        ];
×
468

UNCOV
469
        for seed in seeds {
×
UNCOV
470
            let k1 = Keychain::default(seed.to_vec());
×
UNCOV
471
            let k2 = KeychainOld::default(seed.to_vec());
×
472

UNCOV
473
            assert_eq!(k1.origin_address(true), k2.origin_address(true));
×
UNCOV
474
            assert_eq!(k1.origin_address(false), k2.origin_address(false));
×
475
        }
UNCOV
476
    }
×
477

478
    #[test]
UNCOV
479
    fn test_get_address() {
×
UNCOV
480
        let seeds = [
×
UNCOV
481
            [0u8; 32],
×
UNCOV
482
            [
×
UNCOV
483
                0xc2, 0x7e, 0x1d, 0x7e, 0x9a, 0x0d, 0x47, 0xfa, 0xa5, 0x10, 0xbe, 0x50, 0x9b, 0xce,
×
UNCOV
484
                0xd4, 0x95, 0x99, 0x64, 0x40, 0x34, 0xbd, 0x5a, 0xf2, 0x2b, 0x51, 0x9c, 0x21, 0x19,
×
UNCOV
485
                0xbd, 0xaa, 0x5d, 0x62,
×
UNCOV
486
            ],
×
UNCOV
487
        ];
×
488

UNCOV
489
        for seed in seeds {
×
UNCOV
490
            let k1 = Keychain::default(seed.to_vec());
×
UNCOV
491
            let k2 = KeychainOld::default(seed.to_vec());
×
492

UNCOV
493
            assert_eq!(k1.get_address(true), k2.get_address(true));
×
UNCOV
494
            assert_eq!(k1.get_address(false), k2.get_address(false));
×
495
        }
UNCOV
496
    }
×
497

498
    #[test]
UNCOV
499
    fn test_get_transaction_auth() {
×
UNCOV
500
        let seeds = [
×
UNCOV
501
            [0u8; 32],
×
UNCOV
502
            [
×
UNCOV
503
                0xc2, 0x7e, 0x1d, 0x7e, 0x9a, 0x0d, 0x47, 0xfa, 0xa5, 0x10, 0xbe, 0x50, 0x9b, 0xce,
×
UNCOV
504
                0xd4, 0x95, 0x99, 0x64, 0x40, 0x34, 0xbd, 0x5a, 0xf2, 0x2b, 0x51, 0x9c, 0x21, 0x19,
×
UNCOV
505
                0xbd, 0xaa, 0x5d, 0x62,
×
UNCOV
506
            ],
×
UNCOV
507
        ];
×
508

UNCOV
509
        for seed in seeds {
×
UNCOV
510
            let k1 = Keychain::default(seed.to_vec());
×
UNCOV
511
            let k2 = KeychainOld::default(seed.to_vec());
×
512

UNCOV
513
            assert_eq!(k1.get_transaction_auth(), k2.get_transaction_auth());
×
514
        }
UNCOV
515
    }
×
516

517
    #[test]
UNCOV
518
    fn test_sign_as_origin() {
×
UNCOV
519
        let seeds = [
×
UNCOV
520
            [0u8; 32],
×
UNCOV
521
            [
×
UNCOV
522
                0xc2, 0x7e, 0x1d, 0x7e, 0x9a, 0x0d, 0x47, 0xfa, 0xa5, 0x10, 0xbe, 0x50, 0x9b, 0xce,
×
UNCOV
523
                0xd4, 0x95, 0x99, 0x64, 0x40, 0x34, 0xbd, 0x5a, 0xf2, 0x2b, 0x51, 0x9c, 0x21, 0x19,
×
UNCOV
524
                0xbd, 0xaa, 0x5d, 0x62,
×
UNCOV
525
            ],
×
UNCOV
526
        ];
×
527

UNCOV
528
        for seed in seeds {
×
UNCOV
529
            let k1 = Keychain::default(seed.to_vec());
×
UNCOV
530
            let k2 = KeychainOld::default(seed.to_vec());
×
531

UNCOV
532
            let recv_addr =
×
UNCOV
533
                StacksAddress::from_string("SP1Z4P459B2M5XC2PMM2CSCNZ6824DN5GZG2XYWFH").unwrap();
×
534

UNCOV
535
            let mut tx_stx_transfer_1 = StacksTransaction::new(
×
UNCOV
536
                TransactionVersion::Testnet,
×
UNCOV
537
                k1.get_transaction_auth().unwrap(),
×
UNCOV
538
                TransactionPayload::TokenTransfer(
×
UNCOV
539
                    recv_addr.clone().into(),
×
UNCOV
540
                    123,
×
UNCOV
541
                    TokenTransferMemo([0u8; 34]),
×
UNCOV
542
                ),
×
543
            );
UNCOV
544
            let mut tx_stx_transfer_2 = StacksTransaction::new(
×
UNCOV
545
                TransactionVersion::Testnet,
×
UNCOV
546
                k2.get_transaction_auth().unwrap(),
×
UNCOV
547
                TransactionPayload::TokenTransfer(
×
UNCOV
548
                    recv_addr.into(),
×
UNCOV
549
                    123,
×
UNCOV
550
                    TokenTransferMemo([0u8; 34]),
×
UNCOV
551
                ),
×
552
            );
553

UNCOV
554
            tx_stx_transfer_1.chain_id = 0x80000000;
×
UNCOV
555
            tx_stx_transfer_1.post_condition_mode = TransactionPostConditionMode::Allow;
×
UNCOV
556
            tx_stx_transfer_1.set_tx_fee(0);
×
557

UNCOV
558
            tx_stx_transfer_2.chain_id = 0x80000000;
×
UNCOV
559
            tx_stx_transfer_2.post_condition_mode = TransactionPostConditionMode::Allow;
×
UNCOV
560
            tx_stx_transfer_2.set_tx_fee(0);
×
561

UNCOV
562
            let mut signer_1 = StacksTransactionSigner::new(&tx_stx_transfer_1);
×
UNCOV
563
            k1.sign_as_origin(&mut signer_1);
×
UNCOV
564
            let tx_1 = signer_1.get_tx().unwrap();
×
565

UNCOV
566
            let mut signer_2 = StacksTransactionSigner::new(&tx_stx_transfer_2);
×
UNCOV
567
            k2.sign_as_origin(&mut signer_2);
×
UNCOV
568
            let tx_2 = signer_2.get_tx().unwrap();
×
569

UNCOV
570
            assert_eq!(tx_1, tx_2);
×
571
        }
UNCOV
572
    }
×
573
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc