Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/signers subscribe to burn blocks #4549

Merged
merged 5 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
37 changes: 37 additions & 0 deletions libsigner/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use blockstack_lib::util_lib::boot::boot_code_id;
use clarity::vm::types::serialization::SerializationError;
use clarity::vm::types::QualifiedContractIdentifier;
use serde::{Deserialize, Serialize};
use serde_json::Value;
use stacks_common::codec::{
read_next, read_next_at_most, read_next_exact, write_next, Error as CodecError,
StacksMessageCodec,
Expand Down Expand Up @@ -73,6 +74,8 @@ pub enum SignerEvent {
BlockValidationResponse(BlockValidateResponse),
/// Status endpoint request
StatusCheck,
/// A new burn block event was received with the given burnchain block height
NewBurnBlock(u64),
}

impl StacksMessageCodec for BlockProposalSigners {
Expand Down Expand Up @@ -281,6 +284,8 @@ impl EventReceiver for SignerEventReceiver {
process_stackerdb_event(event_receiver.local_addr, request, is_mainnet)
} else if request.url() == "/proposal_response" {
process_proposal_response(request)
} else if request.url() == "/new_burn_block" {
process_new_burn_block_event(request)
} else {
let url = request.url().to_string();

Expand Down Expand Up @@ -438,6 +443,38 @@ fn process_proposal_response(mut request: HttpRequest) -> Result<SignerEvent, Ev
Ok(SignerEvent::BlockValidationResponse(event))
}

/// Process a new burn block event from the node
fn process_new_burn_block_event(mut request: HttpRequest) -> Result<SignerEvent, EventError> {
debug!("Got burn_block event");
let mut body = String::new();
if let Err(e) = request.as_reader().read_to_string(&mut body) {
error!("Failed to read body: {:?}", &e);

if let Err(e) = request.respond(HttpResponse::empty(200u16)) {
error!("Failed to respond to request: {:?}", &e);
}
return Err(EventError::MalformedRequest(format!(
"Failed to read body: {:?}",
&e
)));
}
#[derive(Debug, Deserialize)]
struct TempBurnBlockEvent {
burn_block_hash: String,
burn_block_height: u64,
reward_recipients: Vec<serde_json::Value>,
reward_slot_holders: Vec<String>,
burn_amount: u64,
}
let temp: TempBurnBlockEvent = serde_json::from_slice(body.as_bytes())
.map_err(|e| EventError::Deserialize(format!("Could not decode body to JSON: {:?}", &e)))?;
let event = SignerEvent::NewBurnBlock(temp.burn_block_height);
if let Err(e) = request.respond(HttpResponse::empty(200u16)) {
error!("Failed to respond to request: {:?}", &e);
}
Ok(event)
}

fn get_signers_db_signer_set_message_id(name: &str) -> Option<(u32, u32)> {
// Splitting the string by '-'
let parts: Vec<&str> = name.split('-').collect();
Expand Down
19 changes: 0 additions & 19 deletions stacks-signer/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ use std::time::Duration;

use clarity::vm::errors::Error as ClarityError;
use clarity::vm::types::serialization::SerializationError;
use libsigner::RPCError;
use libstackerdb::Error as StackerDBError;
use slog::slog_debug;
pub use stackerdb::*;
Expand All @@ -48,9 +47,6 @@ pub enum ClientError {
/// Failed to sign stacker-db chunk
#[error("Failed to sign stacker-db chunk: {0}")]
FailToSign(#[from] StackerDBError),
/// Failed to write to stacker-db due to RPC error
#[error("Failed to write to stacker-db instance: {0}")]
PutChunkFailed(#[from] RPCError),
/// Stacker-db instance rejected the chunk
#[error("Stacker-db rejected the chunk. Reason: {0}")]
PutChunkRejected(String),
Expand All @@ -72,33 +68,18 @@ pub enum ClientError {
/// Failed to parse a Clarity value
#[error("Received a malformed clarity value: {0}")]
MalformedClarityValue(String),
/// Invalid Clarity Name
#[error("Invalid Clarity Name: {0}")]
InvalidClarityName(String),
/// Backoff retry timeout
#[error("Backoff retry timeout occurred. Stacks node may be down.")]
RetryTimeout,
/// Not connected
#[error("Not connected")]
NotConnected,
/// Invalid signing key
#[error("Signing key not represented in the list of signers")]
InvalidSigningKey,
/// Clarity interpreter error
#[error("Clarity interpreter error: {0}")]
ClarityError(#[from] ClarityError),
/// Our stacks address does not belong to a registered signer
#[error("Our stacks address does not belong to a registered signer")]
NotRegistered,
/// Reward set not yet calculated for the given reward cycle
#[error("Reward set not yet calculated for reward cycle: {0}")]
RewardSetNotYetCalculated(u64),
/// Malformed reward set
#[error("Malformed contract data: {0}")]
MalformedContractData(String),
/// No reward set exists for the given reward cycle
#[error("No reward set exists for reward cycle {0}")]
NoRewardSet(u64),
/// Stacks node does not support a feature we need
#[error("Stacks node does not support a required feature: {0}")]
UnsupportedStacksFeature(String),
Expand Down
2 changes: 1 addition & 1 deletion stacks-signer/src/client/stackerdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl StackerDB {
warn!("Failed to send message to stackerdb due to wrong version number. Attempted {}. Expected {}. Retrying...", slot_version, slot_metadata.slot_version);
slot_version = slot_metadata.slot_version;
} else {
warn!("Failed to send message to stackerdb due to wrong version number. Attempted {}. Expected unkown version number. Incrementing and retrying...", slot_version);
warn!("Failed to send message to stackerdb due to wrong version number. Attempted {}. Expected unknown version number. Incrementing and retrying...", slot_version);
}
if let Some(versions) = self.slot_versions.get_mut(&msg_id) {
// NOTE: per the above, this is always executed
Expand Down
24 changes: 16 additions & 8 deletions stacks-signer/src/client/stacks_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ use wsts::curve::point::{Compressed, Point};

use crate::client::{retry_with_exponential_backoff, ClientError};
use crate::config::GlobalConfig;
use crate::runloop::RewardCycleInfo;

/// The Stacks signer client used to communicate with the stacks node
#[derive(Clone, Debug)]
Expand Down Expand Up @@ -363,16 +364,23 @@ impl StacksClient {
Ok(peer_info.burn_block_height)
}

/// Get the current reward cycle from the stacks node
pub fn get_current_reward_cycle(&self) -> Result<u64, ClientError> {
/// Get the current reward cycle info from the stacks node
pub fn get_current_reward_cycle_info(&self) -> Result<RewardCycleInfo, ClientError> {
let pox_data = self.get_pox_data()?;
let blocks_mined = pox_data
.current_burnchain_block_height
.saturating_sub(pox_data.first_burnchain_block_height);
let reward_cycle_length = pox_data
let reward_phase_block_length = pox_data
.reward_phase_block_length
.saturating_add(pox_data.prepare_phase_block_length);
Ok(blocks_mined / reward_cycle_length)
let reward_cycle = blocks_mined / reward_phase_block_length;
Ok(RewardCycleInfo {
reward_cycle,
reward_phase_block_length,
prepare_phase_block_length: pox_data.prepare_phase_block_length,
first_burnchain_block_height: pox_data.first_burnchain_block_height,
last_burnchain_block_height: pox_data.current_burnchain_block_height,
})
}

/// Helper function to retrieve the account info from the stacks node for a specific address
Expand Down Expand Up @@ -735,23 +743,23 @@ mod tests {
fn valid_reward_cycle_should_succeed() {
let mock = MockServerClient::new();
let (pox_data_response, pox_data) = build_get_pox_data_response(None, None, None, None);
let h = spawn(move || mock.client.get_current_reward_cycle());
let h = spawn(move || mock.client.get_current_reward_cycle_info());
write_response(mock.server, pox_data_response.as_bytes());
let current_cycle_id = h.join().unwrap().unwrap();
let current_cycle_info = h.join().unwrap().unwrap();
let blocks_mined = pox_data
.current_burnchain_block_height
.saturating_sub(pox_data.first_burnchain_block_height);
let reward_cycle_length = pox_data
.reward_phase_block_length
.saturating_add(pox_data.prepare_phase_block_length);
let id = blocks_mined / reward_cycle_length;
assert_eq!(current_cycle_id, id);
assert_eq!(current_cycle_info.reward_cycle, id);
}

#[test]
fn invalid_reward_cycle_should_fail() {
let mock = MockServerClient::new();
let h = spawn(move || mock.client.get_current_reward_cycle());
let h = spawn(move || mock.client.get_current_reward_cycle_info());
write_response(
mock.server,
b"HTTP/1.1 200 Ok\n\n{\"current_cycle\":{\"id\":\"fake id\", \"is_pox_active\":false}}",
Expand Down