feat: Integration test with signer reboot

This commit is contained in:
Mårten Blankfors
2024-03-15 14:13:28 +01:00
parent f9e9791a35
commit ea0766efe3
5 changed files with 151 additions and 139 deletions

115
Cargo.lock generated
View File

@@ -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",

View File

@@ -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

View File

@@ -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");

View 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() {

View File

@@ -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();
}