Skip to content

Commit c8a4b88

Browse files
authored
Merge branch 'develop' into feat/recover-events
2 parents 8e1e731 + 609ad13 commit c8a4b88

34 files changed

+2462
-748
lines changed

stacks-signer/src/client/stackerdb.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,7 @@ mod tests {
255255
Some(100_000),
256256
None,
257257
Some(9000),
258+
None,
258259
);
259260
let config = GlobalConfig::load_from_str(&signer_config[0]).unwrap();
260261
let signer_config = generate_signer_config(&config, 5);

stacks-signer/src/client/stacks_client.rs

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ use serde::Deserialize;
4545
use serde_json::json;
4646
use slog::{slog_debug, slog_warn};
4747
use stacks_common::codec::StacksMessageCodec;
48-
use stacks_common::consts::{CHAIN_ID_MAINNET, CHAIN_ID_TESTNET};
48+
use stacks_common::consts::CHAIN_ID_MAINNET;
4949
use stacks_common::types::chainstate::{
5050
ConsensusHash, StacksAddress, StacksPrivateKey, StacksPublicKey,
5151
};
@@ -99,7 +99,7 @@ impl From<&GlobalConfig> for StacksClient {
9999
stacks_address: config.stacks_address,
100100
http_origin: format!("http://{}", config.node_host),
101101
tx_version: config.network.to_transaction_version(),
102-
chain_id: config.network.to_chain_id(),
102+
chain_id: config.to_chain_id(),
103103
stacks_node_client: reqwest::blocking::Client::new(),
104104
mainnet: config.network.is_mainnet(),
105105
auth_password: config.auth_password.clone(),
@@ -114,18 +114,14 @@ impl StacksClient {
114114
node_host: String,
115115
auth_password: String,
116116
mainnet: bool,
117+
chain_id: u32,
117118
) -> Self {
118119
let pubkey = StacksPublicKey::from_private(&stacks_private_key);
119120
let tx_version = if mainnet {
120121
TransactionVersion::Mainnet
121122
} else {
122123
TransactionVersion::Testnet
123124
};
124-
let chain_id = if mainnet {
125-
CHAIN_ID_MAINNET
126-
} else {
127-
CHAIN_ID_TESTNET
128-
};
129125
let stacks_address = StacksAddress::p2pkh(mainnet, &pubkey);
130126
Self {
131127
stacks_private_key,
@@ -145,7 +141,13 @@ impl StacksClient {
145141
node_host: String,
146142
auth_password: String,
147143
) -> Result<Self, ClientError> {
148-
let mut stacks_client = Self::new(stacks_private_key, node_host, auth_password, true);
144+
let mut stacks_client = Self::new(
145+
stacks_private_key,
146+
node_host,
147+
auth_password,
148+
true,
149+
CHAIN_ID_MAINNET,
150+
);
149151
let pubkey = StacksPublicKey::from_private(&stacks_private_key);
150152
let info = stacks_client.get_peer_info()?;
151153
if info.network_id == CHAIN_ID_MAINNET {
@@ -154,7 +156,7 @@ impl StacksClient {
154156
stacks_client.tx_version = TransactionVersion::Mainnet;
155157
} else {
156158
stacks_client.mainnet = false;
157-
stacks_client.chain_id = CHAIN_ID_TESTNET;
159+
stacks_client.chain_id = info.network_id;
158160
stacks_client.tx_version = TransactionVersion::Testnet;
159161
}
160162
stacks_client.stacks_address = StacksAddress::p2pkh(stacks_client.mainnet, &pubkey);
@@ -1219,4 +1221,12 @@ mod tests {
12191221
write_response(mock.server, response.as_bytes());
12201222
assert_eq!(h.join().unwrap().unwrap(), reward_cycle as u128);
12211223
}
1224+
1225+
#[test]
1226+
fn get_chain_id_from_config() {
1227+
let mock = MockServerClient::from_config(
1228+
GlobalConfig::load_from_file("./src/tests/conf/signer-custom-chain-id.toml").unwrap(),
1229+
);
1230+
assert_eq!(mock.client.chain_id, 0x80000100);
1231+
}
12221232
}

stacks-signer/src/config.rs

Lines changed: 63 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,6 @@ impl std::fmt::Display for Network {
7676
}
7777

7878
impl Network {
79-
/// Converts a Network enum variant to a corresponding chain id
80-
pub const fn to_chain_id(&self) -> u32 {
81-
match self {
82-
Self::Mainnet => CHAIN_ID_MAINNET,
83-
Self::Testnet | Self::Mocknet => CHAIN_ID_TESTNET,
84-
}
85-
}
86-
8779
/// Convert a Network enum variant to a corresponding address version
8880
pub const fn to_address_version(&self) -> u8 {
8981
match self {
@@ -163,6 +155,8 @@ pub struct GlobalConfig {
163155
pub first_proposal_burn_block_timing: Duration,
164156
/// How much time to wait for a miner to propose a block following a sortition
165157
pub block_proposal_timeout: Duration,
158+
/// An optional custom Chain ID
159+
chain_id: Option<u32>,
166160
}
167161

168162
/// Internal struct for loading up the config file
@@ -190,6 +184,8 @@ struct RawConfigFile {
190184
pub first_proposal_burn_block_timing_secs: Option<u64>,
191185
/// How much time to wait for a miner to propose a block following a sortition in milliseconds
192186
pub block_proposal_timeout_ms: Option<u64>,
187+
/// An optional custom Chain ID
188+
pub chain_id: Option<u32>,
193189
}
194190

195191
impl RawConfigFile {
@@ -278,6 +274,7 @@ impl TryFrom<RawConfigFile> for GlobalConfig {
278274
metrics_endpoint,
279275
first_proposal_burn_block_timing,
280276
block_proposal_timeout,
277+
chain_id: raw_data.chain_id,
281278
})
282279
}
283280
}
@@ -308,13 +305,15 @@ impl GlobalConfig {
308305
Some(endpoint) => endpoint.to_string(),
309306
None => "None".to_string(),
310307
};
308+
let chain_id = format!("{:x}", self.to_chain_id());
311309
format!(
312310
r#"
313311
Stacks node host: {node_host}
314312
Signer endpoint: {endpoint}
315313
Stacks address: {stacks_address}
316314
Public key: {public_key}
317315
Network: {network}
316+
Chain ID: 0x{chain_id}
318317
Database path: {db_path}
319318
Metrics endpoint: {metrics_endpoint}
320319
"#,
@@ -329,6 +328,14 @@ Metrics endpoint: {metrics_endpoint}
329328
metrics_endpoint = metrics_endpoint,
330329
)
331330
}
331+
332+
/// Get the chain ID for the network
333+
pub fn to_chain_id(&self) -> u32 {
334+
self.chain_id.unwrap_or_else(|| match self.network {
335+
Network::Mainnet => CHAIN_ID_MAINNET,
336+
Network::Testnet | Network::Mocknet => CHAIN_ID_TESTNET,
337+
})
338+
}
332339
}
333340

334341
impl Display for GlobalConfig {
@@ -356,6 +363,7 @@ pub fn build_signer_config_tomls(
356363
max_tx_fee_ustx: Option<u64>,
357364
tx_fee_ustx: Option<u64>,
358365
mut metrics_port_start: Option<usize>,
366+
chain_id: Option<u32>,
359367
) -> Vec<String> {
360368
let mut signer_config_tomls = vec![];
361369

@@ -421,6 +429,15 @@ metrics_endpoint = "{metrics_endpoint}"
421429
metrics_port_start = Some(metrics_port + 1);
422430
}
423431

432+
if let Some(chain_id) = chain_id {
433+
signer_config_toml = format!(
434+
r#"
435+
{signer_config_toml}
436+
chain_id = {chain_id}
437+
"#
438+
)
439+
}
440+
424441
signer_config_tomls.push(signer_config_toml);
425442
}
426443

@@ -453,13 +470,16 @@ mod tests {
453470
None,
454471
None,
455472
Some(4000),
473+
None,
456474
);
457475

458476
let config =
459477
RawConfigFile::load_from_str(&config_tomls[0]).expect("Failed to parse config file");
460478

461479
assert_eq!(config.auth_password, "melon");
462480
assert_eq!(config.metrics_endpoint, Some("localhost:4000".to_string()));
481+
let global_config = GlobalConfig::try_from(config).unwrap();
482+
assert_eq!(global_config.to_chain_id(), CHAIN_ID_TESTNET);
463483
}
464484

465485
#[test]
@@ -473,8 +493,10 @@ Signer endpoint: 127.0.0.1:30000
473493
Stacks address: ST3FPN8KBZ3YPBP0ZJGAAHTVFMQDTJCR5QPS7VTNJ
474494
Public key: 03bc489f27da3701d9f9e577c88de5567cf4023111b7577042d55cde4d823a3505
475495
Network: testnet
496+
Chain ID: 0x80000000
476497
Database path: :memory:
477498
Metrics endpoint: 0.0.0.0:9090
499+
Chain ID: 2147483648
478500
"#;
479501

480502
let expected_str_v6 = r#"
@@ -483,6 +505,7 @@ Signer endpoint: [::1]:30000
483505
Stacks address: ST3FPN8KBZ3YPBP0ZJGAAHTVFMQDTJCR5QPS7VTNJ
484506
Public key: 03bc489f27da3701d9f9e577c88de5567cf4023111b7577042d55cde4d823a3505
485507
Network: testnet
508+
Chain ID: 0x80000000
486509
Database path: :memory:
487510
Metrics endpoint: 0.0.0.0:9090
488511
"#;
@@ -531,5 +554,37 @@ db_path = ":memory:"
531554
);
532555
let config = GlobalConfig::load_from_str(&config_toml).unwrap();
533556
assert_eq!(config.stacks_address.to_string(), expected_addr);
557+
assert_eq!(config.to_chain_id(), CHAIN_ID_MAINNET);
558+
}
559+
560+
#[test]
561+
fn test_custom_chain_id() {
562+
let pk = StacksPrivateKey::from_hex(
563+
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
564+
)
565+
.unwrap();
566+
567+
let node_host = "localhost";
568+
let network = Network::Testnet;
569+
let password = "melon";
570+
let config_tomls = build_signer_config_tomls(
571+
&[pk],
572+
node_host,
573+
None,
574+
&network,
575+
password,
576+
rand::random(),
577+
3000,
578+
None,
579+
None,
580+
Some(4000),
581+
Some(0x80000100),
582+
);
583+
584+
let config =
585+
RawConfigFile::load_from_str(&config_tomls[0]).expect("Failed to parse config file");
586+
assert_eq!(config.chain_id, Some(0x80000100));
587+
let global_config = GlobalConfig::try_from(config).unwrap();
588+
assert_eq!(global_config.to_chain_id(), 0x80000100);
534589
}
535590
}

stacks-signer/src/main.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ fn handle_generate_stacking_signature(
121121
&private_key, //
122122
args.reward_cycle.into(),
123123
args.method.topic(),
124-
config.network.to_chain_id(),
124+
config.to_chain_id(),
125125
args.period.into(),
126126
args.max_amount,
127127
args.auth_id,

stacks-signer/src/tests/chainstate.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ use clarity::util::vrf::VRFProof;
3232
use libsigner::BlockProposal;
3333
use slog::slog_info;
3434
use stacks_common::bitvec::BitVec;
35+
use stacks_common::consts::CHAIN_ID_TESTNET;
3536
use stacks_common::info;
3637
use stacks_common::types::chainstate::{
3738
ConsensusHash, StacksBlockId, StacksPrivateKey, StacksPublicKey, TrieHash,
@@ -96,6 +97,7 @@ fn setup_test_environment(
9697
SocketAddrV4::new(Ipv4Addr::new(127, 0, 0, 1), 10000).to_string(),
9798
"FOO".into(),
9899
false,
100+
CHAIN_ID_TESTNET,
99101
);
100102

101103
let signer_db_dir = "/tmp/stacks-node-tests/signer-units/";
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
stacks_private_key = "126e916e77359ccf521e168feea1fcb9626c59dc375cae00c7464303381c7dff01"
2+
node_host = "127.0.0.1:20444"
3+
endpoint = "localhost:30001"
4+
network = "testnet"
5+
auth_password = "12345"
6+
db_path = ":memory:"
7+
chain_id = 0x80000100

stackslib/src/blockstack_cli.rs

Lines changed: 63 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,9 @@ For usage information on those methods, call `blockstack-cli [method] -h`
7373
7474
`blockstack-cli` accepts flag options as well:
7575
76-
--testnet instruct the transaction generator to use a testnet version byte instead of MAINNET (default)
76+
--testnet[=chain-id]
77+
instruct the transaction generator to use a testnet version byte instead of MAINNET (default)
78+
optionally, you can specify a custom chain ID to use for the transaction
7779
7880
";
7981

@@ -185,6 +187,7 @@ enum CliError {
185187
ClarityGeneralError(ClarityError),
186188
Message(String),
187189
Usage,
190+
InvalidChainId(std::num::ParseIntError),
188191
}
189192

190193
impl std::error::Error for CliError {
@@ -204,6 +207,7 @@ impl std::fmt::Display for CliError {
204207
CliError::ClarityGeneralError(e) => write!(f, "Clarity error: {}", e),
205208
CliError::Message(e) => write!(f, "{}", e),
206209
CliError::Usage => write!(f, "{}", USAGE),
210+
CliError::InvalidChainId(e) => write!(f, "Invalid chain ID: {}", e),
207211
}
208212
}
209213
}
@@ -848,18 +852,26 @@ fn main() {
848852
}
849853

850854
fn main_handler(mut argv: Vec<String>) -> Result<String, CliError> {
851-
let tx_version = if let Some(ix) = argv.iter().position(|x| x == "--testnet") {
852-
argv.remove(ix);
853-
TransactionVersion::Testnet
854-
} else {
855-
TransactionVersion::Mainnet
856-
};
855+
let mut tx_version = TransactionVersion::Mainnet;
856+
let mut chain_id = CHAIN_ID_MAINNET;
857+
858+
// Look for the `--testnet` flag
859+
if let Some(ix) = argv.iter().position(|x| x.starts_with("--testnet")) {
860+
let flag = argv.remove(ix);
861+
862+
// Check if `--testnet=<chain_id>` is used
863+
if let Some(custom_chain_id) = flag.split('=').nth(1) {
864+
// Attempt to parse the custom chain ID from hex
865+
chain_id = u32::from_str_radix(custom_chain_id.trim_start_matches("0x"), 16)
866+
.map_err(|err| CliError::InvalidChainId(err))?;
867+
} else {
868+
// Use the default testnet chain ID
869+
chain_id = CHAIN_ID_TESTNET;
870+
}
857871

858-
let chain_id = if tx_version == TransactionVersion::Testnet {
859-
CHAIN_ID_TESTNET
860-
} else {
861-
CHAIN_ID_MAINNET
862-
};
872+
// Set the transaction version to Testnet
873+
tx_version = TransactionVersion::Testnet;
874+
}
863875

864876
if let Some((method, args)) = argv.split_first() {
865877
match method.as_str() {
@@ -1220,4 +1232,43 @@ mod test {
12201232
let result = main_handler(to_string_vec(&header_args)).unwrap();
12211233
eprintln!("result:\n{}", result);
12221234
}
1235+
1236+
#[test]
1237+
fn custom_chain_id() {
1238+
// Standard chain id
1239+
let tt_args = [
1240+
"--testnet",
1241+
"token-transfer",
1242+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1243+
"1",
1244+
"0",
1245+
"ST1A14RBKJ289E3DP89QAZE2RRHDPWP5RHMYFRCHV",
1246+
"10",
1247+
];
1248+
1249+
let result = main_handler(to_string_vec(&tt_args));
1250+
assert!(result.is_ok());
1251+
1252+
let result = result.unwrap();
1253+
let tx = decode_transaction(&[result], TransactionVersion::Testnet).unwrap();
1254+
assert!(tx.contains("chain_id\":2147483648"));
1255+
1256+
// Custom chain id
1257+
let tt_args = [
1258+
"--testnet=0x12345678",
1259+
"token-transfer",
1260+
"043ff5004e3d695060fa48ac94c96049b8c14ef441c50a184a6a3875d2a000f3",
1261+
"1",
1262+
"0",
1263+
"ST1A14RBKJ289E3DP89QAZE2RRHDPWP5RHMYFRCHV",
1264+
"10",
1265+
];
1266+
1267+
let result = main_handler(to_string_vec(&tt_args));
1268+
assert!(result.is_ok());
1269+
1270+
let result = result.unwrap();
1271+
let tx = decode_transaction(&[result], TransactionVersion::Testnet).unwrap();
1272+
assert!(tx.contains("chain_id\":305419896"));
1273+
}
12231274
}

0 commit comments

Comments
 (0)