Skip to content

Commit 93b9ee1

Browse files
committed
Merge branch 'next' into 4388-nakamoto-stacks-signer-should-store-its-party-shares-on-the-side-to-enable-restart
2 parents 42c58ad + 93ff0e8 commit 93b9ee1

File tree

9 files changed

+172
-18
lines changed

9 files changed

+172
-18
lines changed

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

clarity/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ stacks_common = { package = "stacks-common", path = "../stacks-common" }
3131
rstest = "0.17.0"
3232
rstest_reuse = "0.5.0"
3333
hashbrown = { workspace = true }
34+
mutants = "0.0.3"
3435

3536
[dependencies.serde_json]
3637
version = "1.0"

clarity/src/vm/ast/traits_resolver/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ impl TraitsResolver {
4545
TraitsResolver {}
4646
}
4747

48+
#[cfg_attr(test, mutants::skip)]
4849
pub fn run(&mut self, contract_ast: &mut ContractAST) -> ParseResult<()> {
4950
let mut referenced_traits = HashMap::new();
5051

clarity/src/vm/costs/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -786,6 +786,9 @@ impl TrackerData {
786786
/// `apply_updates` - tells this function to look for any changes in the cost voting contract
787787
/// which would need to be applied. if `false`, just load the last computed cost state in this
788788
/// fork.
789+
/// TODO: #4587 add test for Err cases
790+
/// Or keep the skip and remove the comment
791+
#[cfg_attr(test, mutants::skip)]
789792
fn load_costs(&mut self, clarity_db: &mut ClarityDatabase, apply_updates: bool) -> Result<()> {
790793
clarity_db.begin();
791794
let epoch_id = clarity_db

clarity/src/vm/types/mod.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1529,6 +1529,10 @@ impl TupleData {
15291529
self.data_map.is_empty()
15301530
}
15311531

1532+
///TODO: #4587 create default for TupleData, then check if the mutation tests are caught for the case:
1533+
/// Ok((Default::default()))
1534+
/// Or keep the skip and remove the comment
1535+
#[cfg_attr(test, mutants::skip)]
15321536
pub fn from_data(data: Vec<(ClarityName, Value)>) -> Result<TupleData> {
15331537
let mut type_map = BTreeMap::new();
15341538
let mut data_map = BTreeMap::new();
@@ -1545,6 +1549,10 @@ impl TupleData {
15451549
Self::new(TupleTypeSignature::try_from(type_map)?, data_map)
15461550
}
15471551

1552+
///TODO: #4587 create default for TupleData, then check if the mutation tests are caught for the case:
1553+
/// Ok((Default::default()))
1554+
/// Or keep the skip and remove the comment
1555+
#[cfg_attr(test, mutants::skip)]
15481556
pub fn from_data_typed(
15491557
epoch: &StacksEpochId,
15501558
data: Vec<(ClarityName, Value)>,

stacks-signer/src/cli.rs

Lines changed: 133 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,11 @@ use blockstack_lib::chainstate::stacks::address::PoxAddress;
2121
use blockstack_lib::util_lib::signed_structured_data::pox4::Pox4SignatureTopic;
2222
use clap::{Parser, ValueEnum};
2323
use clarity::vm::types::QualifiedContractIdentifier;
24-
use stacks_common::address::b58;
24+
use stacks_common::address::{
25+
b58, AddressHashMode, C32_ADDRESS_VERSION_MAINNET_MULTISIG,
26+
C32_ADDRESS_VERSION_MAINNET_SINGLESIG, C32_ADDRESS_VERSION_TESTNET_MULTISIG,
27+
C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
28+
};
2529
use stacks_common::types::chainstate::StacksPrivateKey;
2630

2731
use crate::config::Network;
@@ -260,12 +264,26 @@ fn parse_contract(contract: &str) -> Result<QualifiedContractIdentifier, String>
260264
QualifiedContractIdentifier::parse(contract).map_err(|e| format!("Invalid contract: {}", e))
261265
}
262266

263-
/// Parse a BTC address argument and return a `PoxAddress`
267+
/// Parse a BTC address argument and return a `PoxAddress`.
268+
/// This function behaves similarly to `PoxAddress::from_b58`, but also handles
269+
/// addresses where the parsed AddressHashMode is None.
264270
pub fn parse_pox_addr(pox_address_literal: &str) -> Result<PoxAddress, String> {
265-
PoxAddress::from_b58(pox_address_literal).map_or_else(
271+
let parsed_addr = PoxAddress::from_b58(pox_address_literal).map_or_else(
266272
|| Err(format!("Invalid pox address: {pox_address_literal}")),
267-
|pox_address| Ok(pox_address),
268-
)
273+
Ok,
274+
);
275+
match parsed_addr {
276+
Ok(PoxAddress::Standard(addr, None)) => match addr.version {
277+
C32_ADDRESS_VERSION_MAINNET_MULTISIG | C32_ADDRESS_VERSION_TESTNET_MULTISIG => Ok(
278+
PoxAddress::Standard(addr, Some(AddressHashMode::SerializeP2SH)),
279+
),
280+
C32_ADDRESS_VERSION_MAINNET_SINGLESIG | C32_ADDRESS_VERSION_TESTNET_SINGLESIG => Ok(
281+
PoxAddress::Standard(addr, Some(AddressHashMode::SerializeP2PKH)),
282+
),
283+
_ => Err(format!("Invalid address version: {}", addr.version)),
284+
},
285+
_ => parsed_addr,
286+
}
269287
}
270288

271289
/// Parse the hexadecimal Stacks private key
@@ -306,13 +324,50 @@ fn parse_network(network: &str) -> Result<Network, String> {
306324
#[cfg(test)]
307325
mod tests {
308326
use blockstack_lib::chainstate::stacks::address::{PoxAddressType20, PoxAddressType32};
327+
use blockstack_lib::util_lib::signed_structured_data::pox4::make_pox_4_signer_key_message_hash;
328+
use clarity::consts::CHAIN_ID_TESTNET;
329+
use clarity::util::hash::Sha256Sum;
309330

310331
use super::*;
311332

333+
/// Helper just to ensure that a the pox address
334+
/// can be turned into a clarity tuple
335+
fn make_message_hash(pox_addr: &PoxAddress) -> Sha256Sum {
336+
make_pox_4_signer_key_message_hash(
337+
pox_addr,
338+
0,
339+
&Pox4SignatureTopic::StackStx,
340+
CHAIN_ID_TESTNET,
341+
0,
342+
0,
343+
0,
344+
)
345+
}
346+
347+
fn clarity_tuple_version(pox_addr: &PoxAddress) -> u8 {
348+
pox_addr
349+
.as_clarity_tuple()
350+
.expect("Failed to generate clarity tuple for pox address")
351+
.get("version")
352+
.expect("Expected version in clarity tuple")
353+
.clone()
354+
.expect_buff(1)
355+
.expect("Expected version to be a u128")
356+
.get(0)
357+
.expect("Expected version to be a uint")
358+
.clone()
359+
}
360+
312361
#[test]
313362
fn test_parse_pox_addr() {
314363
let tr = "bc1p8vg588hldsnv4a558apet4e9ff3pr4awhqj2hy8gy6x2yxzjpmqsvvpta4";
315364
let pox_addr = parse_pox_addr(tr).expect("Failed to parse segwit address");
365+
assert_eq!(tr, pox_addr.clone().to_b58());
366+
make_message_hash(&pox_addr);
367+
assert_eq!(
368+
clarity_tuple_version(&pox_addr),
369+
PoxAddressType32::P2TR.to_u8()
370+
);
316371
match pox_addr {
317372
PoxAddress::Addr32(_, addr_type, _) => {
318373
assert_eq!(addr_type, PoxAddressType32::P2TR);
@@ -322,35 +377,105 @@ mod tests {
322377

323378
let legacy = "1N8GMS991YDY1E696e9SB9EsYY5ckSU7hZ";
324379
let pox_addr = parse_pox_addr(legacy).expect("Failed to parse legacy address");
380+
assert_eq!(legacy, pox_addr.clone().to_b58());
381+
make_message_hash(&pox_addr);
382+
assert_eq!(
383+
clarity_tuple_version(&pox_addr),
384+
AddressHashMode::SerializeP2PKH as u8
385+
);
325386
match pox_addr {
326387
PoxAddress::Standard(stacks_addr, hash_mode) => {
327388
assert_eq!(stacks_addr.version, 22);
328-
assert!(hash_mode.is_none());
389+
assert_eq!(hash_mode, Some(AddressHashMode::SerializeP2PKH));
329390
}
330391
_ => panic!("Invalid parsed address"),
331392
}
332393

333394
let p2sh = "33JNgVMNMC9Xm6mJG9oTVf5zWbmt5xi1Mv";
334395
let pox_addr = parse_pox_addr(p2sh).expect("Failed to parse legacy address");
396+
assert_eq!(p2sh, pox_addr.clone().to_b58());
397+
assert_eq!(
398+
clarity_tuple_version(&pox_addr),
399+
AddressHashMode::SerializeP2SH as u8
400+
);
401+
make_message_hash(&pox_addr);
335402
match pox_addr {
336403
PoxAddress::Standard(stacks_addr, hash_mode) => {
337404
assert_eq!(stacks_addr.version, 20);
338-
assert!(hash_mode.is_none());
405+
assert_eq!(hash_mode, Some(AddressHashMode::SerializeP2SH));
406+
}
407+
_ => panic!("Invalid parsed address"),
408+
}
409+
410+
let testnet_p2pkh = "mnr5asd1MLSutHLL514WZXNpUNN3L98zBc";
411+
let pox_addr = parse_pox_addr(testnet_p2pkh).expect("Failed to parse testnet address");
412+
assert_eq!(
413+
clarity_tuple_version(&pox_addr),
414+
AddressHashMode::SerializeP2PKH as u8
415+
);
416+
assert_eq!(testnet_p2pkh, pox_addr.clone().to_b58());
417+
make_message_hash(&pox_addr);
418+
match pox_addr {
419+
PoxAddress::Standard(stacks_addr, hash_mode) => {
420+
assert_eq!(stacks_addr.version, C32_ADDRESS_VERSION_TESTNET_SINGLESIG);
421+
assert_eq!(hash_mode, Some(AddressHashMode::SerializeP2PKH));
339422
}
340423
_ => panic!("Invalid parsed address"),
341424
}
342425

343426
let wsh = "bc1qvnpcphdctvmql5gdw6chtwvvsl6ra9gwa2nehc99np7f24juc4vqrx29cs";
344427
let pox_addr = parse_pox_addr(wsh).expect("Failed to parse segwit address");
428+
assert_eq!(
429+
clarity_tuple_version(&pox_addr),
430+
PoxAddressType32::P2WSH.to_u8()
431+
);
432+
assert_eq!(wsh, pox_addr.clone().to_b58());
433+
make_message_hash(&pox_addr);
345434
match pox_addr {
346435
PoxAddress::Addr32(_, addr_type, _) => {
347436
assert_eq!(addr_type, PoxAddressType32::P2WSH);
348437
}
349438
_ => panic!("Invalid parsed address"),
350439
}
351440

352-
let wpkh = "BC1QW508D6QEJXTDG4Y5R3ZARVARY0C5XW7KV8F3T4";
441+
let wpkh = "bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4";
353442
let pox_addr = parse_pox_addr(wpkh).expect("Failed to parse segwit address");
443+
assert_eq!(
444+
clarity_tuple_version(&pox_addr),
445+
PoxAddressType20::P2WPKH.to_u8()
446+
);
447+
assert_eq!(wpkh, pox_addr.clone().to_b58());
448+
make_message_hash(&pox_addr);
449+
match pox_addr {
450+
PoxAddress::Addr20(_, addr_type, _) => {
451+
assert_eq!(addr_type, PoxAddressType20::P2WPKH);
452+
}
453+
_ => panic!("Invalid parsed address"),
454+
}
455+
456+
let testnet_tr = "tb1p46cgptxsfwkqpnnj552rkae3nf6l52wxn4snp4vm6mcrz2585hwq6cdwf2";
457+
let pox_addr = parse_pox_addr(testnet_tr).expect("Failed to parse testnet address");
458+
assert_eq!(testnet_tr, pox_addr.clone().to_b58());
459+
make_message_hash(&pox_addr);
460+
assert_eq!(
461+
clarity_tuple_version(&pox_addr),
462+
PoxAddressType32::P2TR.to_u8()
463+
);
464+
match pox_addr {
465+
PoxAddress::Addr32(_, addr_type, _) => {
466+
assert_eq!(addr_type, PoxAddressType32::P2TR);
467+
}
468+
_ => panic!("Invalid parsed address"),
469+
}
470+
471+
let testnet_segwit = "tb1q38eleudmqyg4jrm39dnudj23pv6jcjrksa437s";
472+
let pox_addr = parse_pox_addr(testnet_segwit).expect("Failed to parse testnet address");
473+
assert_eq!(testnet_segwit, pox_addr.clone().to_b58());
474+
make_message_hash(&pox_addr);
475+
assert_eq!(
476+
clarity_tuple_version(&pox_addr),
477+
PoxAddressType20::P2WPKH.to_u8()
478+
);
354479
match pox_addr {
355480
PoxAddress::Addr20(_, addr_type, _) => {
356481
assert_eq!(addr_type, PoxAddressType20::P2WPKH);

stackslib/src/chainstate/nakamoto/mod.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1773,6 +1773,7 @@ impl NakamotoChainState {
17731773
chainstate: &mut StacksChainState,
17741774
for_burn_block_height: u64,
17751775
at_block_id: &StacksBlockId,
1776+
warn_if_not_found: bool,
17761777
) -> Result<Point, ChainstateError> {
17771778
// Get the current reward cycle
17781779
let Some(rc) = sort_handle.pox_constants().block_height_to_reward_cycle(
@@ -1788,18 +1789,22 @@ impl NakamotoChainState {
17881789
return Err(ChainstateError::InvalidStacksBlock(msg));
17891790
};
17901791

1791-
debug!(
1792+
test_debug!(
17921793
"get-approved-aggregate-key at block {}, cycle {}",
1793-
at_block_id, rc
1794+
at_block_id,
1795+
rc
17941796
);
17951797
match chainstate.get_aggregate_public_key_pox_4(sortdb, at_block_id, rc)? {
17961798
Some(key) => Ok(key),
17971799
None => {
1798-
warn!(
1799-
"Failed to get aggregate public key";
1800-
"block_id" => %at_block_id,
1801-
"reward_cycle" => rc,
1802-
);
1800+
// this can happen for a whole host of reasons
1801+
if warn_if_not_found {
1802+
warn!(
1803+
"Failed to get aggregate public key";
1804+
"block_id" => %at_block_id,
1805+
"reward_cycle" => rc,
1806+
);
1807+
}
18031808
Err(ChainstateError::InvalidStacksBlock(
18041809
"Failed to get aggregate public key".into(),
18051810
))
@@ -1828,6 +1833,7 @@ impl NakamotoChainState {
18281833
chainstate,
18291834
block_sn.block_height,
18301835
&aggregate_key_block_header.index_block_hash(),
1836+
true,
18311837
)?;
18321838
Ok(aggregate_public_key)
18331839
}

stackslib/src/net/p2p.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5388,7 +5388,7 @@ impl PeerNetwork {
53885388
.last_key_value()
53895389
.map(|(rc, _)| rc.saturating_add(1))
53905390
.unwrap_or(0);
5391-
let new_agg_pubkeys = (next_agg_pubkey_rc..=sort_tip_rc)
5391+
let mut new_agg_pubkeys: Vec<_> = (next_agg_pubkey_rc..=sort_tip_rc)
53925392
.filter_map(|key_rc| {
53935393
let ih = sortdb.index_handle(&tip_sn.sortition_id);
53945394
let agg_pubkey_opt = if self.get_current_epoch().epoch_id < StacksEpochId::Epoch25 {
@@ -5404,6 +5404,7 @@ impl PeerNetwork {
54045404
chainstate,
54055405
self.burnchain.reward_cycle_to_block_height(key_rc),
54065406
&stacks_tip_block_id,
5407+
false,
54075408
)
54085409
.ok()
54095410
};
@@ -5414,6 +5415,10 @@ impl PeerNetwork {
54145415
})
54155416
.collect();
54165417

5418+
if new_agg_pubkeys.len() == 0 && self.aggregate_public_keys.len() == 0 {
5419+
// special case -- we're before epoch 3.0, so don't waste time doing this again
5420+
new_agg_pubkeys.push((sort_tip_rc, None));
5421+
}
54175422
Ok(new_agg_pubkeys)
54185423
}
54195424

stackslib/src/util_lib/signed_structured_data.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,11 @@ pub mod pox4 {
106106
TupleData::from_data(vec![
107107
(
108108
"pox-addr".into(),
109-
pox_addr.clone().as_clarity_tuple().unwrap().into(),
109+
pox_addr
110+
.clone()
111+
.as_clarity_tuple()
112+
.expect("Error creating signature hash - invalid PoX Address")
113+
.into(),
110114
),
111115
("reward-cycle".into(), Value::UInt(reward_cycle)),
112116
("period".into(), Value::UInt(period)),
@@ -117,7 +121,7 @@ pub mod pox4 {
117121
("auth-id".into(), Value::UInt(auth_id)),
118122
("max-amount".into(), Value::UInt(max_amount)),
119123
])
120-
.unwrap(),
124+
.expect("Error creating signature hash"),
121125
);
122126
structured_data_message_hash(data_tuple, domain_tuple)
123127
}

0 commit comments

Comments
 (0)