Add pox contract ID option to config to overwrite node result

Signed-off-by: Jacinta Ferrant <jacinta@trustmachines.co>
This commit is contained in:
Jacinta Ferrant
2023-10-12 11:45:03 -07:00
parent d9fb56d97a
commit aea9eb8af5
6 changed files with 114 additions and 9 deletions

View File

@@ -105,6 +105,8 @@ pub struct Config {
pub endpoint: SocketAddr,
/// smart contract that controls the target stackerdb
pub stackerdb_contract_id: QualifiedContractIdentifier,
/// smart contract that controls the target stackerdb
pub pox_contract_id: Option<QualifiedContractIdentifier>,
/// The Scalar representation of the private key for signer communication
pub message_private_key: Scalar,
/// The signer's Stacks private key
@@ -137,8 +139,11 @@ struct RawConfigFile {
pub node_host: String,
/// endpoint to stackerdb receiver
pub endpoint: String,
/// contract identifier
// FIXME: these contract's should go away in non testing scenarios. Make them both optionals.
/// Stacker db contract identifier
pub stackerdb_contract_id: String,
/// pox contract identifier
pub pox_contract_id: Option<String>,
/// the 32 byte ECDSA private key used to sign blocks, chunks, and transactions
pub message_private_key: String,
/// The hex representation of the signer's Stacks private key used for communicating
@@ -218,6 +223,17 @@ impl TryFrom<RawConfigFile> for Config {
)
})?;
let pox_contract_id = if let Some(id) = raw_data.pox_contract_id.as_ref() {
Some(QualifiedContractIdentifier::parse(id).map_err(|_| {
ConfigError::BadField(
"pox_contract_id".to_string(),
raw_data.pox_contract_id.unwrap_or("".to_string()),
)
})?)
} else {
None
};
let message_private_key =
Scalar::try_from(raw_data.message_private_key.as_str()).map_err(|_| {
ConfigError::BadField(
@@ -269,6 +285,7 @@ impl TryFrom<RawConfigFile> for Config {
node_host,
endpoint,
stackerdb_contract_id,
pox_contract_id,
message_private_key,
stacks_private_key,
stacks_address,

View File

@@ -269,6 +269,7 @@ fn handle_generate_files(args: GenerateFilesArgs) {
args.num_keys,
&args.db_args.host.to_string(),
&args.db_args.contract.to_string(),
None,
args.timeout.map(Duration::from_millis),
);
debug!("Built {:?} signer config tomls.", signer_config_tomls.len());

View File

@@ -36,7 +36,7 @@ pub enum RunLoopCommand {
/// The RunLoop state
#[derive(PartialEq, Debug)]
pub enum State {
// TODO: add a state for startup: such as Unitialized where it indicates we need to replay events/configure the signer
// TODO: Unitialized should indicate we need to replay events/configure the signer
/// The runloop signer is unitialized
Unitialized,
/// The runloop is idle
@@ -180,7 +180,7 @@ impl<C: Coordinatable> RunLoop<C> {
self.state = State::Idle;
}
Err(e) => {
// TODO: is this a fatal error? If we fail at startup to access the stacks client to see if DKG was set...this seems pretty fatal..
// TODO:do a backoff retry here. This could fail because of node spinning up.
panic!(
"Failed to load aggregate public key from stacks client: {:?}",
e
@@ -355,7 +355,7 @@ impl<C: Coordinatable> SignerRunLoop<Vec<OperationResult>, RunLoopCommand> for R
if self.should_run_dkg() {
// Add it to the front of the queue so it is set before any other commands get processed!
debug!("DKG has not run and needs to be queued. Adding it to the front of the queue.");
self.commands.push_back(RunLoopCommand::Dkg);
self.commands.push_front(RunLoopCommand::Dkg);
}
// The process the next command
// Must be called AFTER processing the event as the state may update to IDLE due to said event.

View File

@@ -126,6 +126,8 @@ pub struct StacksClient {
chain_id: u32,
/// The Client used to make HTTP connects
stacks_node_client: reqwest::blocking::Client,
/// The pox contract ID
pox_contract_id: Option<QualifiedContractIdentifier>,
}
impl From<&Config> for StacksClient {
@@ -142,6 +144,7 @@ impl From<&Config> for StacksClient {
tx_version: config.network.to_transaction_version(),
chain_id: config.network.to_chain_id(),
stacks_node_client: reqwest::blocking::Client::new(),
pox_contract_id: config.pox_contract_id.clone(),
}
}
}
@@ -267,6 +270,10 @@ impl StacksClient {
/// Helper function to retrieve the pox contract address and name from the stacks node
fn get_pox_contract(&self) -> Result<(StacksAddress, ContractName), ClientError> {
// Check if we have overwritten the pox contract ID in the config
if let Some(pox_contract) = self.pox_contract_id.clone() {
return Ok((pox_contract.issuer.into(), pox_contract.name));
}
debug!("Retrieving pox contract ID...");
// TODO: we may want to cache the pox contract inside the client itself (calling this function once on init)
let send_request = || {

View File

@@ -14,7 +14,8 @@ pub fn build_signer_config_tomls(
signer_stacks_private_keys: &[StacksPrivateKey],
num_keys: u32,
node_host: &str,
contract_id: &str,
stackerdb_contract_id: &str,
pox_contract_id: Option<&str>,
timeout: Option<Duration>,
) -> Vec<String> {
let num_signers = signer_stacks_private_keys.len() as u32;
@@ -73,7 +74,7 @@ stacks_private_key = "{stacks_private_key}"
node_host = "{node_host}"
endpoint = "{endpoint}"
network = "testnet"
stackerdb_contract_id = "{contract_id}"
stackerdb_contract_id = "{stackerdb_contract_id}"
signer_id = {id}
{signers_array}
"#
@@ -88,8 +89,18 @@ event_timeout = {event_timeout_ms}
"#
)
}
if let Some(pox_contract_id) = pox_contract_id {
signer_config_toml = format!(
r#"
{signer_config_toml}
pox_contract_id = "{pox_contract_id}"
"#
);
}
signer_config_tomls.push(signer_config_toml);
}
signer_config_tomls
}

View File

@@ -60,6 +60,7 @@ fn setup_stx_btc_node(
num_signers: u32,
signer_stacks_private_keys: &[StacksPrivateKey],
stackerdb_contract: &str,
pox_contract: &str,
signer_config_tomls: &Vec<String>,
) -> RunningNodes {
for toml in signer_config_tomls {
@@ -133,7 +134,21 @@ fn setup_stx_btc_node(
submit_tx(&http_origin, &tx);
// mine it
info!("Mine it...");
info!("Mining stackerdb contract...");
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
let tx = make_contract_publish(
&signer_stacks_private_keys[0],
1,
10_000,
"pox-4",
pox_contract,
);
submit_tx(&http_origin, &tx);
// mine it
info!("Mining pox contract...");
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
next_block_and_wait(&mut btc_regtest_controller, &blocks_processed);
@@ -145,6 +160,52 @@ fn setup_stx_btc_node(
}
}
/// Helper function for building our fake pox contract
pub fn build_pox_contract(num_signers: u32) -> String {
let mut pox_contract = String::new(); // "
pox_contract += r#"
;; data vars
;;
(define-data-var bitcoin-wallet-public-key (optional (buff 33)) none)
"#;
pox_contract += &format!("(define-data-var num-signers uint u{num_signers})\n");
pox_contract += r#"
(define-data-var num-voted uint u0)
;; data maps
;;
(define-map vote-per-signer {signer: (principal)} {candidate: (buff 33) })
;; read only functions
;;
(define-read-only (get-bitcoin-wallet-public-key (reward-cycle uint))
(var-get bitcoin-wallet-public-key)
)
(define-read-only (get-bitcoin-wallet-public-key-vote (signer principal) (reward-cycle uint))
(map-get? vote-per-signer signer)
)
;; public functions
;;
(define-public (cast-bitcoin-wallet-public-key-vote (public-key (buff 33) (reward-cycle uint)))
(begin
(var-set num-voted (+ (var-get num-voted) 1))
(map-set vote-per-signer {public_key: tx-sender} {candidate: public-key})
(if (is-eq (var-get num-voted) num-signers)
(begin
(var-set bitcoin-wallet-public-key (some public-key))
(var-get bitcoin-wallet-public-key)
(ok none))
(ok none))
)
)
"#;
pox_contract
}
#[test]
fn test_stackerdb_dkg() {
if env::var("BITCOIND_TEST") != Ok("1".into()) {
@@ -170,9 +231,15 @@ fn test_stackerdb_dkg() {
// Setup the neon node
let (mut conf, _) = neon_integration_test_conf();
// Build our simulated pox-4 stacks contract TODO: replace this with the real deal?
let pox_contract = build_pox_contract(num_signers);
let pox_contract_id = QualifiedContractIdentifier::new(
to_addr(&signer_stacks_private_keys[0]).into(),
"pox-4".into(),
);
// Build the stackerdb contract
let stackerdb_contract = build_stackerdb_contract(&signer_stacks_addresses);
let contract_id =
let stacker_db_contract_id =
QualifiedContractIdentifier::new(signer_stacks_addresses[0].into(), "hello-world".into());
// Setup the signer and coordinator configurations
@@ -180,7 +247,8 @@ fn test_stackerdb_dkg() {
&signer_stacks_private_keys,
num_keys,
&conf.node.rpc_bind,
&contract_id.to_string(),
&stacker_db_contract_id.to_string(),
Some(&pox_contract_id.to_string()),
Some(Duration::from_millis(128)), // Timeout defaults to 5 seconds. Let's override it to 128 milliseconds.
);
@@ -216,6 +284,7 @@ fn test_stackerdb_dkg() {
num_signers,
&signer_stacks_private_keys,
&stackerdb_contract,
&pox_contract,
&signer_configs,
);