mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-01-12 22:43:42 +08:00
feat: Integration test with signer reboot
This commit is contained in:
115
Cargo.lock
generated
115
Cargo.lock
generated
@@ -498,28 +498,6 @@ version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b"
|
||||
|
||||
[[package]]
|
||||
name = "bindgen"
|
||||
version = "0.64.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4243e6031260db77ede97ad86c27e501d646a27ab57b59a574f725d98ab1fb4"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"cexpr",
|
||||
"clang-sys",
|
||||
"lazy_static",
|
||||
"lazycell",
|
||||
"log",
|
||||
"peeking_take_while",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"regex",
|
||||
"rustc-hash",
|
||||
"shlex",
|
||||
"syn 1.0.109",
|
||||
"which",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
@@ -632,15 +610,6 @@ dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cexpr"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "0.1.10"
|
||||
@@ -692,17 +661,6 @@ dependencies = [
|
||||
"inout",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clang-sys"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1"
|
||||
dependencies = [
|
||||
"glob",
|
||||
"libc",
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "2.34.0"
|
||||
@@ -1499,12 +1457,6 @@ version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
|
||||
[[package]]
|
||||
name = "glob"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||
|
||||
[[package]]
|
||||
name = "gloo-timers"
|
||||
version = "0.2.6"
|
||||
@@ -1925,12 +1877,6 @@ version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lazycell"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.153"
|
||||
@@ -1957,16 +1903,6 @@ dependencies = [
|
||||
"rle-decode-fast",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c571b676ddfc9a8c12f1f3d3085a7b163966a8fd8098a90640953ce5f6170161"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libredox"
|
||||
version = "0.0.1"
|
||||
@@ -2089,12 +2025,6 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.2"
|
||||
@@ -2194,16 +2124,6 @@ dependencies = [
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nu-ansi-term"
|
||||
version = "0.46.0"
|
||||
@@ -2287,7 +2207,6 @@ version = "7.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3a64d160b891178fb9d43d1a58ddcafb6502daeb54d810e5e92a7c3c9bfacc07"
|
||||
dependencies = [
|
||||
"bindgen",
|
||||
"bitvec",
|
||||
"bs58 0.4.0",
|
||||
"cc",
|
||||
@@ -2336,12 +2255,6 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae"
|
||||
|
||||
[[package]]
|
||||
name = "peeking_take_while"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
|
||||
|
||||
[[package]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.1"
|
||||
@@ -2977,12 +2890,6 @@ version = "0.1.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hash"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
|
||||
|
||||
[[package]]
|
||||
name = "rustc-hex"
|
||||
version = "2.1.0"
|
||||
@@ -3344,12 +3251,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "signature"
|
||||
version = "2.2.0"
|
||||
@@ -4470,18 +4371,6 @@ version = "0.25.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1"
|
||||
|
||||
[[package]]
|
||||
name = "which"
|
||||
version = "4.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7"
|
||||
dependencies = [
|
||||
"either",
|
||||
"home",
|
||||
"once_cell",
|
||||
"rustix 0.38.31",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.2.8"
|
||||
@@ -4706,8 +4595,8 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wsts"
|
||||
version = "8.1.0"
|
||||
source = "git+https://github.com/stacks-network/wsts.git?branch=feat/public-sign-ids#99c1ed3d528d98585ba4b50084e8a6c37f8f5793"
|
||||
version = "9.0.0"
|
||||
source = "git+https://github.com/stacks-network/wsts.git?rev=af2b907#af2b907e00fdd840d1a9abeb84bd0ddb4fc2a528"
|
||||
dependencies = [
|
||||
"aes-gcm 0.10.3",
|
||||
"bs58 0.5.0",
|
||||
|
||||
@@ -20,8 +20,7 @@ rand_core = "0.6"
|
||||
rand = "0.8"
|
||||
rand_chacha = "0.3.1"
|
||||
tikv-jemallocator = "0.5.4"
|
||||
# wsts = { version = "8.1", default-features = false }
|
||||
wsts = { git = "https://github.com/stacks-network/wsts.git", branch = "feat/public-sign-ids" }
|
||||
wsts = { git = "https://github.com/stacks-network/wsts.git", default-features = false, rev = "af2b907" }
|
||||
|
||||
# Use a bit more than default optimization for
|
||||
# dev builds to speed up test execution
|
||||
|
||||
@@ -338,19 +338,23 @@ pub fn build_signer_config_tomls(
|
||||
timeout: Option<Duration>,
|
||||
network: &Network,
|
||||
password: &str,
|
||||
mut port_start: usize,
|
||||
) -> Vec<String> {
|
||||
let mut signer_config_tomls = vec![];
|
||||
|
||||
let mut port = 30000;
|
||||
let run_stamp = rand::random::<u16>();
|
||||
let db_dir = format!(
|
||||
"/tmp/stacks-node-tests/integrations-signers/{:#X}",
|
||||
run_stamp,
|
||||
);
|
||||
fs::create_dir_all(&db_dir).unwrap();
|
||||
for (ix, stacks_private_key) in stacks_private_keys.iter().enumerate() {
|
||||
let endpoint = format!("localhost:{}", port);
|
||||
port += 1;
|
||||
for stacks_private_key in stacks_private_keys {
|
||||
let endpoint = format!("localhost:{}", port_start);
|
||||
port_start += 1;
|
||||
|
||||
let stacks_public_key = StacksPublicKey::from_private(stacks_private_key).to_hex();
|
||||
let db_dir = std::env::temp_dir().join(format!(
|
||||
"stacks-node-tests/integrations-signers/signer_{stacks_public_key}"
|
||||
));
|
||||
let db_path = db_dir.join("signerdb.sqlite");
|
||||
let db_path = db_path.display();
|
||||
|
||||
fs::create_dir_all(&db_dir).unwrap();
|
||||
|
||||
let stacks_private_key = stacks_private_key.to_hex();
|
||||
let mut signer_config_toml = format!(
|
||||
r#"
|
||||
@@ -359,7 +363,7 @@ node_host = "{node_host}"
|
||||
endpoint = "{endpoint}"
|
||||
network = "{network}"
|
||||
auth_password = "{password}"
|
||||
db_path = "{db_dir}/{ix}.sqlite"
|
||||
db_path = "{db_path}"
|
||||
"#
|
||||
);
|
||||
|
||||
@@ -394,7 +398,8 @@ mod tests {
|
||||
let network = Network::Testnet;
|
||||
let password = "melon";
|
||||
|
||||
let config_tomls = build_signer_config_tomls(&[pk], node_host, None, &network, password);
|
||||
let config_tomls =
|
||||
build_signer_config_tomls(&[pk], node_host, None, &network, password, 3000);
|
||||
|
||||
let config =
|
||||
RawConfigFile::load_from_str(&config_tomls[0]).expect("Failed to parse config file");
|
||||
|
||||
@@ -292,6 +292,7 @@ fn handle_generate_files(args: GenerateFilesArgs) {
|
||||
args.timeout.map(Duration::from_millis),
|
||||
&args.network,
|
||||
&args.password,
|
||||
3000,
|
||||
);
|
||||
debug!("Built {:?} signer config tomls.", signer_config_tomls.len());
|
||||
for (i, file_contents) in signer_config_tomls.iter().enumerate() {
|
||||
|
||||
@@ -114,6 +114,7 @@ impl SignerTest {
|
||||
Some(Duration::from_millis(128)), // Timeout defaults to 5 seconds. Let's override it to 128 milliseconds.
|
||||
&Network::Testnet,
|
||||
password,
|
||||
3000,
|
||||
);
|
||||
|
||||
let mut running_signers = Vec::new();
|
||||
@@ -426,22 +427,11 @@ impl SignerTest {
|
||||
.expect("failed to recv dkg results");
|
||||
for result in results {
|
||||
match result {
|
||||
OperationResult::Sign(sig) => {
|
||||
panic!("Received Signature ({},{})", &sig.R, &sig.z);
|
||||
}
|
||||
OperationResult::SignTaproot(proof) => {
|
||||
panic!("Received SchnorrProof ({},{})", &proof.r, &proof.s);
|
||||
}
|
||||
OperationResult::DkgError(dkg_error) => {
|
||||
panic!("Received DkgError {:?}", dkg_error);
|
||||
}
|
||||
OperationResult::SignError(sign_error) => {
|
||||
panic!("Received SignError {}", sign_error);
|
||||
}
|
||||
OperationResult::Dkg(point) => {
|
||||
info!("Received aggregate_group_key {point}");
|
||||
aggregate_public_key = Some(point);
|
||||
}
|
||||
other => panic!("{}", operation_panic_message(&other)),
|
||||
}
|
||||
}
|
||||
if aggregate_public_key.is_some() || dkg_now.elapsed() > timeout {
|
||||
@@ -703,6 +693,45 @@ impl SignerTest {
|
||||
]
|
||||
}
|
||||
|
||||
/// Kills the signer runloop at index `signer_idx`
|
||||
/// and returns the private key of the killed signer.
|
||||
///
|
||||
/// # Panics
|
||||
/// Panics if `signer_idx` is out of bounds
|
||||
fn stop_signer(&mut self, signer_idx: usize) -> StacksPrivateKey {
|
||||
let running_signer = self.running_signers.remove(signer_idx);
|
||||
self.signer_cmd_senders.remove(signer_idx);
|
||||
self.result_receivers.remove(signer_idx);
|
||||
let signer_key = self.signer_stacks_private_keys.remove(signer_idx);
|
||||
|
||||
running_signer.stop();
|
||||
signer_key
|
||||
}
|
||||
|
||||
/// (Re)starts a new signer runloop with the given private key
|
||||
fn restart_signer(&mut self, signer_idx: usize, signer_private_key: StacksPrivateKey) {
|
||||
let signer_config = build_signer_config_tomls(
|
||||
&[signer_private_key],
|
||||
&self.running_nodes.conf.node.rpc_bind,
|
||||
Some(Duration::from_millis(128)), // Timeout defaults to 5 seconds. Let's override it to 128 milliseconds.
|
||||
&Network::Testnet,
|
||||
"12345", // It worked sir, we have the combination! -Great, what's the combination?
|
||||
3000 + signer_idx,
|
||||
)
|
||||
.pop()
|
||||
.unwrap();
|
||||
|
||||
let (cmd_send, cmd_recv) = channel();
|
||||
let (res_send, res_recv) = channel();
|
||||
|
||||
info!("Restarting signer");
|
||||
let signer = spawn_signer(&signer_config, cmd_recv, res_send);
|
||||
|
||||
self.result_receivers.insert(signer_idx, res_recv);
|
||||
self.signer_cmd_senders.insert(signer_idx, cmd_send);
|
||||
self.running_signers.insert(signer_idx, signer);
|
||||
}
|
||||
|
||||
fn shutdown(self) {
|
||||
self.running_nodes
|
||||
.coord_channel
|
||||
@@ -851,6 +880,26 @@ fn setup_stx_btc_node(
|
||||
}
|
||||
}
|
||||
|
||||
fn operation_panic_message(result: &OperationResult) -> String {
|
||||
match result {
|
||||
OperationResult::Sign(sig) => {
|
||||
format!("Received Signature ({},{})", sig.R, sig.z)
|
||||
}
|
||||
OperationResult::SignTaproot(proof) => {
|
||||
format!("Received SchnorrProof ({},{})", proof.r, proof.s)
|
||||
}
|
||||
OperationResult::DkgError(dkg_error) => {
|
||||
format!("Received DkgError {:?}", dkg_error)
|
||||
}
|
||||
OperationResult::SignError(sign_error) => {
|
||||
format!("Received SignError {}", sign_error)
|
||||
}
|
||||
OperationResult::Dkg(point) => {
|
||||
format!("Received aggregate_group_key {point}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
/// Test the signer can respond to external commands to perform DKG
|
||||
@@ -1266,3 +1315,72 @@ fn stackerdb_filter_bad_transactions() {
|
||||
}
|
||||
signer_test.shutdown();
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
/// Test that signers will be able to continue their operations even if one signer is restarted.
|
||||
///
|
||||
/// Test Setup:
|
||||
/// The test spins up three stacks signers, one miner Nakamoto node, and a corresponding bitcoind.
|
||||
/// The stacks node is advanced to epoch 2.5, triggering a DKG round. The stacks node is then advanced
|
||||
/// to Epoch 3.0 boundary to allow block signing.
|
||||
///
|
||||
/// Test Execution:
|
||||
/// The signers sign one block as usual.
|
||||
/// Then, one of the signers is restarted.
|
||||
/// Finally, the signers sign another block with the restarted signer.
|
||||
///
|
||||
/// Test Assertion:
|
||||
/// The signers are able to produce a valid signature after one of them is restarted.
|
||||
fn stackerdb_sign_after_signer_reboot() {
|
||||
if env::var("BITCOIND_TEST") != Ok("1".into()) {
|
||||
return;
|
||||
}
|
||||
|
||||
tracing_subscriber::registry()
|
||||
.with(fmt::layer())
|
||||
.with(EnvFilter::from_default_env())
|
||||
.init();
|
||||
|
||||
info!("------------------------- Test Setup -------------------------");
|
||||
let mut signer_test = SignerTest::new(3);
|
||||
let timeout = Duration::from_secs(200);
|
||||
let short_timeout = Duration::from_secs(30);
|
||||
|
||||
let key = signer_test.boot_to_epoch_3(timeout);
|
||||
|
||||
info!("------------------------- Test Mine Block -------------------------");
|
||||
|
||||
signer_test.mine_nakamoto_block(timeout);
|
||||
let proposed_signer_signature_hash = signer_test.wait_for_validate_ok_response(short_timeout);
|
||||
let signature =
|
||||
signer_test.wait_for_confirmed_block(&proposed_signer_signature_hash, short_timeout);
|
||||
|
||||
assert!(
|
||||
signature.verify(&key, proposed_signer_signature_hash.0.as_slice()),
|
||||
"Signature verification failed"
|
||||
);
|
||||
|
||||
info!("------------------------- Restart one Signer -------------------------");
|
||||
let signer_key = signer_test.stop_signer(2);
|
||||
debug!(
|
||||
"Removed signer 2 with key: {:?}, {}",
|
||||
signer_key,
|
||||
signer_key.to_hex()
|
||||
);
|
||||
signer_test.restart_signer(2, signer_key);
|
||||
|
||||
info!("------------------------- Test Mine Block after restart -------------------------");
|
||||
|
||||
signer_test.mine_nakamoto_block(timeout);
|
||||
let proposed_signer_signature_hash = signer_test.wait_for_validate_ok_response(short_timeout);
|
||||
let frost_signature =
|
||||
signer_test.wait_for_confirmed_block(&proposed_signer_signature_hash, short_timeout);
|
||||
|
||||
assert!(
|
||||
frost_signature.verify(&key, proposed_signer_signature_hash.0.as_slice()),
|
||||
"Signature verification failed"
|
||||
);
|
||||
|
||||
signer_test.shutdown();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user