From bf5ed23c84133a8f9458c1f4b40339e7d158d28d Mon Sep 17 00:00:00 2001 From: Brice Dobry Date: Sat, 6 Apr 2024 15:48:09 -0400 Subject: [PATCH 1/9] chore: add debug log for `DKGResult` message --- stacks-signer/src/signer.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index 4d23a92c0..4a4c0dde6 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -996,6 +996,10 @@ impl Signer { /// Process a dkg result by broadcasting a vote to the stacks node fn process_dkg(&mut self, stacks_client: &StacksClient, dkg_public_key: &Point) { let mut dkg_results_bytes = vec![]; + debug!( + "{self}: Received DKG result. Broadcasting vote to the stacks node..."; + "dkg_public_key" => %dkg_public_key + ); if let Err(e) = SignerMessage::serialize_dkg_result( &mut dkg_results_bytes, dkg_public_key, From d0f9d44e7a338694a329c0b1264d521afa2d9641 Mon Sep 17 00:00:00 2001 From: Brice Dobry Date: Mon, 8 Apr 2024 17:41:01 -0400 Subject: [PATCH 2/9] fix: check for approved aggregate key again before triggering again --- stacks-signer/src/signer.rs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index 4a4c0dde6..0c824c19d 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -1321,11 +1321,21 @@ impl Signer { ); return Ok(()); } - debug!("{self}: Vote for DKG failed. Triggering a DKG round."; + + // Try again to get the approved key, in case it has reached the threshold weight + // after we last checked. + self.approved_aggregate_public_key = + stacks_client.get_approved_aggregate_key(self.reward_cycle)?; + if self.approved_aggregate_public_key.is_some() { + self.coordinator + .set_aggregate_public_key(self.approved_aggregate_public_key); + return Ok(false); + } + debug!("{self}: Vote for DKG failed."; "voting_round" => self.coordinator.current_dkg_id, "aggregate_key" => %aggregate_key, "round_weight" => round_weight, - "threshold_weight" => threshold_weight + "threshold_weight" => threshold_weight, ); } else { debug!("{self}: Triggering a DKG round."); From 4da4cda863addd03af39011007a17c9c00e8da84 Mon Sep 17 00:00:00 2001 From: Jacinta Ferrant Date: Mon, 8 Apr 2024 16:08:43 -0700 Subject: [PATCH 3/9] First check if we should queue dkg before checking the contract to minimize unnecessary DKG triggering Signed-off-by: Jacinta Ferrant --- stacks-signer/src/runloop.rs | 9 +-- stacks-signer/src/signer.rs | 107 +++++++++++++++++++++-------------- 2 files changed, 66 insertions(+), 50 deletions(-) diff --git a/stacks-signer/src/runloop.rs b/stacks-signer/src/runloop.rs index 449165009..0f94a66cf 100644 --- a/stacks-signer/src/runloop.rs +++ b/stacks-signer/src/runloop.rs @@ -388,14 +388,9 @@ impl SignerRunLoop, RunLoopCommand> for RunLoop { if event_parity == Some(other_signer_parity) { continue; } - if signer.approved_aggregate_public_key.is_none() { - if let Err(e) = retry_with_exponential_backoff(|| { - signer - .update_dkg(&self.stacks_client, current_reward_cycle) - .map_err(backoff::Error::transient) - }) { - error!("{signer}: failed to update DKG: {e}"); + if let Err(e) = signer.refresh_dkg(&self.stacks_client) { + error!("{signer}: failed to refresh DKG: {e}"); } } signer.refresh_coordinator(); diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index 0c824c19d..884858458 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -124,13 +124,22 @@ pub enum Command { }, } +/// The specific operations that a signer can perform +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum Operation { + /// A DKG operation + Dkg, + /// A Sign operation + Sign, +} + /// The Signer state #[derive(PartialEq, Eq, Debug, Clone)] pub enum State { /// The signer is idle, waiting for messages and commands Idle, /// The signer is executing a DKG or Sign round - OperationInProgress, + OperationInProgress(Operation), } /// The stacks signer registered for the reward cycle @@ -332,8 +341,8 @@ impl Signer { } /// Update operation - fn update_operation(&mut self) { - self.state = State::OperationInProgress; + fn update_operation(&mut self, operation: Operation) { + self.state = State::OperationInProgress(operation); self.coordinator_selector.last_message_time = Some(Instant::now()); } @@ -367,6 +376,7 @@ impl Signer { Ok(msg) => { let ack = self.stackerdb.send_message_with_retry(msg.into()); debug!("{self}: ACK: {ack:?}",); + self.update_operation(Operation::Dkg); } Err(e) => { error!("{self}: Failed to start DKG: {e:?}",); @@ -412,6 +422,7 @@ impl Signer { .unwrap_or_else(|e| { error!("{self}: Failed to insert block in DB: {e:?}"); }); + self.update_operation(Operation::Sign); } Err(e) => { error!("{self}: Failed to start signing block: {e:?}",); @@ -420,7 +431,6 @@ impl Signer { } } } - self.update_operation(); } /// Attempt to process the next command in the queue, and update state accordingly @@ -444,9 +454,12 @@ impl Signer { debug!("{self}: Nothing to process. Waiting for command...",); } } - State::OperationInProgress => { + State::OperationInProgress(op) => { // We cannot execute the next command until the current one is finished... - debug!("{self}: Waiting for coordinator {coordinator_id:?} operation to finish. Coordinator state = {:?}", self.coordinator.state); + debug!( + "{self}: Waiting for {op:?} operation to finish. Coordinator state = {:?}", + self.coordinator.state + ); } } } @@ -680,9 +693,26 @@ impl Signer { self.process_operation_results(stacks_client, &operation_results); self.send_operation_results(res, operation_results); self.finish_operation(); - } else if !packets.is_empty() && self.coordinator.state != CoordinatorState::Idle { - // We have received a message and are in the middle of an operation. Update our state accordingly - self.update_operation(); + } else if !packets.is_empty() { + // We have received a message. Update our state accordingly + // Let us be extra explicit in case a new state type gets added to wsts' state machine + match &self.coordinator.state { + CoordinatorState::Idle => {} + CoordinatorState::DkgPublicDistribute + | CoordinatorState::DkgPublicGather + | CoordinatorState::DkgPrivateDistribute + | CoordinatorState::DkgPrivateGather + | CoordinatorState::DkgEndDistribute + | CoordinatorState::DkgEndGather => { + self.update_operation(Operation::Dkg); + } + CoordinatorState::NonceRequest(_, _) + | CoordinatorState::NonceGather(_, _) + | CoordinatorState::SigShareRequest(_, _) + | CoordinatorState::SigShareGather(_, _) => { + self.update_operation(Operation::Sign); + } + } } debug!("{self}: Saving signer state"); @@ -1239,16 +1269,14 @@ impl Signer { } } - /// Update the DKG for the provided signer info, triggering it if required - pub fn update_dkg( - &mut self, - stacks_client: &StacksClient, - current_reward_cycle: u64, - ) -> Result<(), ClientError> { - let reward_cycle = self.reward_cycle; + /// Refresh DKG value and queue DKG command if necessary + pub fn refresh_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> { + // First check if we should queue DKG based on contract vote state and stackerdb transactions + let should_queue = self.should_queue_dkg(stacks_client)?; + // Before queueing the command, check one last time if DKG has been approved (could have happend in the meantime) let old_dkg = self.approved_aggregate_public_key; self.approved_aggregate_public_key = - stacks_client.get_approved_aggregate_key(reward_cycle)?; + stacks_client.get_approved_aggregate_key(self.reward_cycle)?; if self.approved_aggregate_public_key.is_some() { // TODO: this will never work as is. We need to have stored our party shares on the side etc for this particular aggregate key. // Need to update state to store the necessary info, check against it to see if we have participated in the winning round and @@ -1256,13 +1284,28 @@ impl Signer { self.coordinator .set_aggregate_public_key(self.approved_aggregate_public_key); if old_dkg != self.approved_aggregate_public_key { - debug!( + warn!( "{self}: updated DKG value to {:?}.", self.approved_aggregate_public_key ); } - return Ok(()); - }; + if matches!(self.state, State::OperationInProgress(Operation::Dkg)) { + debug!( + "{self}: DKG has already been set. Aborting DKG operation {}.", + self.coordinator.current_dkg_id + ); + self.finish_operation(); + } + } else if should_queue { + info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); + self.commands.push_front(Command::Dkg); + } + Ok(()) + } + + /// Should DKG be queued to the current signer's command queue + /// This assumes that no key has been approved by the contract yet + pub fn should_queue_dkg(&mut self, stacks_client: &StacksClient) -> Result { if self.state != State::Idle || Some(self.signer_id) != self.get_coordinator(current_reward_cycle).0 { @@ -1321,32 +1364,10 @@ impl Signer { ); return Ok(()); } - - // Try again to get the approved key, in case it has reached the threshold weight - // after we last checked. - self.approved_aggregate_public_key = - stacks_client.get_approved_aggregate_key(self.reward_cycle)?; - if self.approved_aggregate_public_key.is_some() { - self.coordinator - .set_aggregate_public_key(self.approved_aggregate_public_key); - return Ok(false); - } - debug!("{self}: Vote for DKG failed."; - "voting_round" => self.coordinator.current_dkg_id, - "aggregate_key" => %aggregate_key, - "round_weight" => round_weight, - "threshold_weight" => threshold_weight, - ); } else { debug!("{self}: Triggering a DKG round."); } - if self.commands.front() != Some(&Command::Dkg) { - info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); - self.commands.push_front(Command::Dkg); - } else { - debug!("{self}: DKG command already queued..."); - } - Ok(()) + Ok(true) } /// Process the event From cd7c78af71a52736d1f4923925c1e3a969c04b55 Mon Sep 17 00:00:00 2001 From: Brice Dobry Date: Mon, 8 Apr 2024 22:21:35 -0400 Subject: [PATCH 4/9] chore: clarify comment --- stacks-signer/src/signer.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index 884858458..0bf6dcf60 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -1273,7 +1273,10 @@ impl Signer { pub fn refresh_dkg(&mut self, stacks_client: &StacksClient) -> Result<(), ClientError> { // First check if we should queue DKG based on contract vote state and stackerdb transactions let should_queue = self.should_queue_dkg(stacks_client)?; - // Before queueing the command, check one last time if DKG has been approved (could have happend in the meantime) + // Before queueing the command, check one last time if DKG has been + // approved. It could have happened after the last call to + // `get_approved_aggregate_key` but before the theshold check in + // `should_queue_dkg`. let old_dkg = self.approved_aggregate_public_key; self.approved_aggregate_public_key = stacks_client.get_approved_aggregate_key(self.reward_cycle)?; From da120a8c259cbc9c6a722f004fc86f21b6db0c49 Mon Sep 17 00:00:00 2001 From: Brice Dobry Date: Tue, 9 Apr 2024 09:35:37 -0400 Subject: [PATCH 5/9] fix: resolve merge conflicts --- stacks-signer/src/signer.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index 0bf6dcf60..ff2c2f342 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -1301,7 +1301,12 @@ impl Signer { } } else if should_queue { info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); - self.commands.push_front(Command::Dkg); + if self.commands.front() != Some(&Command::Dkg) { + info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); + self.commands.push_front(Command::Dkg); + } else { + debug!("{self}: DKG command already queued..."); + } } Ok(()) } @@ -1310,10 +1315,10 @@ impl Signer { /// This assumes that no key has been approved by the contract yet pub fn should_queue_dkg(&mut self, stacks_client: &StacksClient) -> Result { if self.state != State::Idle - || Some(self.signer_id) != self.get_coordinator(current_reward_cycle).0 + || Some(self.signer_id) != self.get_coordinator(self.reward_cycle).0 { // We are not the coordinator or we are in the middle of an operation. Do not attempt to queue DKG - return Ok(()); + return Ok(false); } debug!("{self}: Checking if old DKG vote transaction exists in StackerDB..."); // Have I already voted, but the vote is still pending in StackerDB? Check stackerdb for the same round number and reward cycle vote transaction @@ -1329,14 +1334,13 @@ impl Signer { NakamotoSigners::parse_vote_for_aggregate_public_key(transaction).unwrap_or_else(|| panic!("BUG: {self}: Received an invalid {SIGNERS_VOTING_FUNCTION_NAME} transaction in an already filtered list: {transaction:?}")); if Some(params.aggregate_key) == self.coordinator.aggregate_public_key && params.voting_round == self.coordinator.current_dkg_id - && reward_cycle == self.reward_cycle { debug!("{self}: Not triggering a DKG round. Already have a pending vote transaction."; "txid" => %transaction.txid(), "aggregate_key" => %params.aggregate_key, "voting_round" => params.voting_round ); - return Ok(()); + return Ok(false); } } if let Some(aggregate_key) = stacks_client.get_vote_for_aggregate_public_key( @@ -1352,7 +1356,7 @@ impl Signer { "voting_round" => self.coordinator.current_dkg_id, "aggregate_key" => %aggregate_key ); - return Ok(()); + return Ok(false); }; let threshold_weight = stacks_client.get_vote_threshold_weight(self.reward_cycle)?; if round_weight < threshold_weight { @@ -1365,7 +1369,7 @@ impl Signer { "round_weight" => round_weight, "threshold_weight" => threshold_weight ); - return Ok(()); + return Ok(false); } } else { debug!("{self}: Triggering a DKG round."); From 344388b61d1fa6991bde1ace4c4f078ff831864a Mon Sep 17 00:00:00 2001 From: Brice Dobry Date: Fri, 12 Apr 2024 10:57:22 -0400 Subject: [PATCH 6/9] chore: address PR nits --- stacks-signer/src/signer.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/stacks-signer/src/signer.rs b/stacks-signer/src/signer.rs index ff2c2f342..a504e56ff 100644 --- a/stacks-signer/src/signer.rs +++ b/stacks-signer/src/signer.rs @@ -1292,15 +1292,13 @@ impl Signer { self.approved_aggregate_public_key ); } - if matches!(self.state, State::OperationInProgress(Operation::Dkg)) { + if let State::OperationInProgress(Operation::Dkg) = self.state { debug!( - "{self}: DKG has already been set. Aborting DKG operation {}.", - self.coordinator.current_dkg_id + "{self}: DKG has already been set. Aborting DKG operation {self.coordinator.current_dkg_id}." ); self.finish_operation(); } } else if should_queue { - info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); if self.commands.front() != Some(&Command::Dkg) { info!("{self} is the current coordinator and must trigger DKG. Queuing DKG command..."); self.commands.push_front(Command::Dkg); From 70f287c27ff94366fbd379fa0af324239e86dbe8 Mon Sep 17 00:00:00 2001 From: Marzi Date: Mon, 8 Apr 2024 19:02:52 -0400 Subject: [PATCH 7/9] Sanitize signer chunk data before writing to terminal --- Cargo.lock | 1 + stacks-signer/Cargo.toml | 1 + stacks-signer/src/main.rs | 41 +++++++++++++++++++++++++++++++++++---- 3 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9cfd1ad9a..0df61365a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,6 +3456,7 @@ dependencies = [ "polynomial", "rand 0.8.5", "rand_core 0.6.4", + "regex", "reqwest", "rusqlite", "secp256k1", diff --git a/stacks-signer/Cargo.toml b/stacks-signer/Cargo.toml index 57b2e8080..c5f3943e4 100644 --- a/stacks-signer/Cargo.toml +++ b/stacks-signer/Cargo.toml @@ -43,6 +43,7 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } wsts = { workspace = true } rand = { workspace = true } url = "2.1.0" +regex = "1.10.3" [dev-dependencies] clarity = { path = "../clarity", features = ["testing"] } diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 285dc7f7e..6c89f5ac6 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -71,11 +71,12 @@ fn stackerdb_session(host: &str, contract: QualifiedContractIdentifier) -> Stack /// Write the chunk to stdout fn write_chunk_to_stdout(chunk_opt: Option>) { if let Some(chunk) = chunk_opt.as_ref() { - let bytes = io::stdout().write(chunk).unwrap(); - if bytes < chunk.len() { + let sanitized_chunk = sanitize_chunk(chunk); + let bytes = io::stdout().write(&sanitized_chunk).unwrap(); + if bytes < sanitized_chunk.len() { print!( "Failed to write complete chunk to stdout. Missing {} bytes", - chunk.len() - bytes + sanitized_chunk.len() - bytes ); } } @@ -176,7 +177,9 @@ fn handle_list_chunks(args: StackerDBArgs) { debug!("Listing chunks..."); let mut session = stackerdb_session(&args.host, args.contract); let chunk_list = session.list_chunks().unwrap(); - println!("{}", serde_json::to_string(&chunk_list).unwrap()); + let chunk_list_json = serde_json::to_string(&chunk_list).unwrap(); + let sanitized_output = sanitize_json_output(&chunk_list_json); + println!("{}", sanitized_output); } fn handle_put_chunk(args: PutChunkArgs) { @@ -366,6 +369,33 @@ fn write_file(dir: &Path, filename: &str, contents: &str) { println!("Created file: {}", filename); } +/// Helper function for sanitizing StackerDB chunks to ensure safe terminal output +fn sanitize_chunk(data: &[u8]) -> Vec { + let mut result = Vec::new(); + let mut i = 0; + while i < data.len() { + // skipping ANSI escape sequence + if i + 1 < data.len() && data[i] == 0x1B && data[i + 1] == b'[' { + i += 2; + while i < data.len() && !(data[i] >= 0x40 && data[i] <= 0x7E) { + i += 1; + } + i += 1; + } else { + result.push(data[i]); + i += 1; + } + } + result +} + +/// Helper function for sanitizing JSON String to ensure safe terminal output +fn sanitize_json_output(input: &str) -> String { + // regex to remove ANSI escape sequences + let re = regex::Regex::new(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]").unwrap(); + re.replace_all(input, "").to_string() +} + fn main() { let cli = Cli::parse(); @@ -552,4 +582,7 @@ pub mod tests { assert!(verify_result.is_ok()); assert!(verify_result.unwrap()); } + + #[test] + fn test_proper_data_sanitization() {} } From aaec5634f36db68723dc147141385aa79a14045f Mon Sep 17 00:00:00 2001 From: Marzi Date: Mon, 8 Apr 2024 19:19:47 -0400 Subject: [PATCH 8/9] Remove empty test --- stacks-signer/src/main.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index 6c89f5ac6..afe16ff4b 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -582,7 +582,4 @@ pub mod tests { assert!(verify_result.is_ok()); assert!(verify_result.unwrap()); } - - #[test] - fn test_proper_data_sanitization() {} } From 8874fd29a20c160a438abde770956dd53d533765 Mon Sep 17 00:00:00 2001 From: Marzi Date: Tue, 9 Apr 2024 14:39:33 -0400 Subject: [PATCH 9/9] Hex encode chunk output before writing to terminal. Remove output sanitization --- Cargo.lock | 1 - stacks-signer/Cargo.toml | 1 - stacks-signer/src/cli.rs | 6 +++--- stacks-signer/src/main.rs | 40 +++++++-------------------------------- 4 files changed, 10 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 0df61365a..9cfd1ad9a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3456,7 +3456,6 @@ dependencies = [ "polynomial", "rand 0.8.5", "rand_core 0.6.4", - "regex", "reqwest", "rusqlite", "secp256k1", diff --git a/stacks-signer/Cargo.toml b/stacks-signer/Cargo.toml index c5f3943e4..57b2e8080 100644 --- a/stacks-signer/Cargo.toml +++ b/stacks-signer/Cargo.toml @@ -43,7 +43,6 @@ tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } wsts = { workspace = true } rand = { workspace = true } url = "2.1.0" -regex = "1.10.3" [dev-dependencies] clarity = { path = "../clarity", features = ["testing"] } diff --git a/stacks-signer/src/cli.rs b/stacks-signer/src/cli.rs index 481de7106..0d305382a 100644 --- a/stacks-signer/src/cli.rs +++ b/stacks-signer/src/cli.rs @@ -44,11 +44,11 @@ pub struct Cli { /// Subcommands for the stacks signer binary #[derive(clap::Subcommand, Debug)] pub enum Command { - /// Get a chunk from the stacker-db instance + /// Get a chunk from the stacker-db instance in hex encoding GetChunk(GetChunkArgs), - /// Get the latest chunk from the stacker-db instance + /// Get the latest chunk from the stacker-db instance in hex encoding GetLatestChunk(GetLatestChunkArgs), - /// List chunks from the stacker-db instance + /// List chunks from the stacker-db instance in hex encoding ListChunks(StackerDBArgs), /// Upload a chunk to the stacker-db instance PutChunk(PutChunkArgs), diff --git a/stacks-signer/src/main.rs b/stacks-signer/src/main.rs index afe16ff4b..c7f3fcd68 100644 --- a/stacks-signer/src/main.rs +++ b/stacks-signer/src/main.rs @@ -71,12 +71,13 @@ fn stackerdb_session(host: &str, contract: QualifiedContractIdentifier) -> Stack /// Write the chunk to stdout fn write_chunk_to_stdout(chunk_opt: Option>) { if let Some(chunk) = chunk_opt.as_ref() { - let sanitized_chunk = sanitize_chunk(chunk); - let bytes = io::stdout().write(&sanitized_chunk).unwrap(); - if bytes < sanitized_chunk.len() { + let hexed_string = to_hex(chunk); + let hexed_chunk = hexed_string.as_bytes(); + let bytes = io::stdout().write(&hexed_chunk).unwrap(); + if bytes < hexed_chunk.len() { print!( "Failed to write complete chunk to stdout. Missing {} bytes", - sanitized_chunk.len() - bytes + hexed_chunk.len() - bytes ); } } @@ -178,8 +179,8 @@ fn handle_list_chunks(args: StackerDBArgs) { let mut session = stackerdb_session(&args.host, args.contract); let chunk_list = session.list_chunks().unwrap(); let chunk_list_json = serde_json::to_string(&chunk_list).unwrap(); - let sanitized_output = sanitize_json_output(&chunk_list_json); - println!("{}", sanitized_output); + let hexed_json = to_hex(chunk_list_json.as_bytes()); + println!("{}", hexed_json); } fn handle_put_chunk(args: PutChunkArgs) { @@ -369,33 +370,6 @@ fn write_file(dir: &Path, filename: &str, contents: &str) { println!("Created file: {}", filename); } -/// Helper function for sanitizing StackerDB chunks to ensure safe terminal output -fn sanitize_chunk(data: &[u8]) -> Vec { - let mut result = Vec::new(); - let mut i = 0; - while i < data.len() { - // skipping ANSI escape sequence - if i + 1 < data.len() && data[i] == 0x1B && data[i + 1] == b'[' { - i += 2; - while i < data.len() && !(data[i] >= 0x40 && data[i] <= 0x7E) { - i += 1; - } - i += 1; - } else { - result.push(data[i]); - i += 1; - } - } - result -} - -/// Helper function for sanitizing JSON String to ensure safe terminal output -fn sanitize_json_output(input: &str) -> String { - // regex to remove ANSI escape sequences - let re = regex::Regex::new(r"(?:\x1B[@-_]|[\x80-\x9F])[0-?]*[ -/]*[@-~]").unwrap(); - re.replace_all(input, "").to_string() -} - fn main() { let cli = Cli::parse();