mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-05-29 23:43:02 +08:00
feat: setup blind-signer lib and binary
This is a thread, for use in testing, that blindly signs blocks and submits signer votes.
This commit is contained in:
15
Cargo.lock
generated
15
Cargo.lock
generated
@@ -522,6 +522,20 @@ dependencies = [
|
||||
"wyz",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "blind-signer"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"libsigner",
|
||||
"pico-args",
|
||||
"reqwest",
|
||||
"serde_json",
|
||||
"slog",
|
||||
"stacks-common",
|
||||
"stacks-node",
|
||||
"stackslib",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.9.0"
|
||||
@@ -3496,6 +3510,7 @@ dependencies = [
|
||||
"async-std",
|
||||
"backtrace",
|
||||
"base64 0.12.3",
|
||||
"blind-signer",
|
||||
"chrono",
|
||||
"clarity",
|
||||
"hashbrown 0.14.3",
|
||||
|
||||
@@ -10,11 +10,13 @@ members = [
|
||||
"contrib/tools/relay-server",
|
||||
"libsigner",
|
||||
"stacks-signer",
|
||||
"testnet/stacks-node"]
|
||||
"testnet/stacks-node",
|
||||
"blind-signer",
|
||||
]
|
||||
|
||||
# Dependencies we want to keep the same between workspace members
|
||||
[workspace.dependencies]
|
||||
ed25519-dalek = { version = "2.1.1", features = ["serde", "rand_core"] }
|
||||
[workspace.dependencies]
|
||||
ed25519-dalek = { version = "2.1.1", features = ["serde", "rand_core"] }
|
||||
hashbrown = "0.14.3"
|
||||
rand_core = "0.6"
|
||||
rand = "0.8"
|
||||
|
||||
30
blind-signer/Cargo.toml
Normal file
30
blind-signer/Cargo.toml
Normal file
@@ -0,0 +1,30 @@
|
||||
[package]
|
||||
name = "blind-signer"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
slog = { version = "2.5.2", features = ["max_level_trace"] }
|
||||
pico-args = "0.5.0"
|
||||
reqwest = { version = "0.11", default_features = false, features = [
|
||||
"blocking",
|
||||
"json",
|
||||
"rustls",
|
||||
"rustls-tls",
|
||||
] }
|
||||
serde_json = { version = "1.0", features = [
|
||||
"arbitrary_precision",
|
||||
"raw_value",
|
||||
] }
|
||||
stacks = { package = "stackslib", path = "../stackslib" }
|
||||
stacks-common = { path = "../stacks-common" }
|
||||
libsigner = { path = "../libsigner" }
|
||||
stacks-node = { path = "../testnet/stacks-node" }
|
||||
|
||||
[lib]
|
||||
name = "blind_signer"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[[bin]]
|
||||
name = "blind-signer"
|
||||
path = "src/main.rs"
|
||||
290
blind-signer/src/lib.rs
Normal file
290
blind-signer/src/lib.rs
Normal file
@@ -0,0 +1,290 @@
|
||||
use libsigner::{BlockResponse, SignerMessage, SignerSession, StackerDBSession};
|
||||
use stacks::chainstate::burn::db::sortdb::SortitionDB;
|
||||
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
|
||||
use stacks::chainstate::nakamoto::test_signers::TestSigners;
|
||||
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
|
||||
use stacks::chainstate::stacks::boot::{MINERS_NAME, SIGNERS_VOTING_NAME};
|
||||
use stacks::clarity::vm::types::QualifiedContractIdentifier;
|
||||
use stacks::clarity::vm::Value;
|
||||
use stacks::codec::StacksMessageCodec;
|
||||
use stacks::libstackerdb::{SlotMetadata, StackerDBChunkData};
|
||||
use stacks::net::api::callreadonly::CallReadOnlyRequestBody;
|
||||
use stacks::net::api::getstackers::GetStackersResponse;
|
||||
use stacks::types::chainstate::StacksAddress;
|
||||
use stacks::util::hash::to_hex;
|
||||
use stacks::util_lib::boot::boot_code_id;
|
||||
use stacks_common::types::chainstate::StacksPublicKey;
|
||||
use stacks_common::util::hash::Sha512Trunc256Sum;
|
||||
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
|
||||
use stacks_node::config::Config;
|
||||
use stacks_node::utils::{get_account, make_contract_call, submit_tx, to_addr};
|
||||
use std::{
|
||||
collections::HashSet,
|
||||
thread::{self, JoinHandle},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use(o, slog_log, slog_trace, slog_debug, slog_info, slog_warn, slog_error)]
|
||||
extern crate slog;
|
||||
#[macro_use]
|
||||
extern crate stacks_common;
|
||||
|
||||
/// Spawn a blind signing thread. `signer` is the private key
|
||||
/// of the individual signer who broadcasts the response to the StackerDB
|
||||
pub fn blind_signer(
|
||||
conf: &Config,
|
||||
signers: &TestSigners,
|
||||
signer: &Secp256k1PrivateKey,
|
||||
) -> JoinHandle<()> {
|
||||
let mut signed_blocks = HashSet::new();
|
||||
let conf = conf.clone();
|
||||
let signers = signers.clone();
|
||||
let signer = signer.clone();
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_millis(500));
|
||||
match read_and_sign_block_proposal(&conf, &signers, &signer, &signed_blocks) {
|
||||
Ok(signed_block) => {
|
||||
if signed_blocks.contains(&signed_block) {
|
||||
continue;
|
||||
}
|
||||
info!("Signed block"; "signer_sig_hash" => signed_block.to_hex());
|
||||
signed_blocks.insert(signed_block);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error reading and signing block proposal: {e}");
|
||||
}
|
||||
}
|
||||
|
||||
signer_vote_if_needed(&conf, &signers, &signer);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_and_sign_block_proposal(
|
||||
conf: &Config,
|
||||
signers: &TestSigners,
|
||||
signer: &Secp256k1PrivateKey,
|
||||
signed_blocks: &HashSet<Sha512Trunc256Sum>,
|
||||
) -> Result<Sha512Trunc256Sum, String> {
|
||||
let burnchain = conf.get_burnchain();
|
||||
let sortdb = burnchain.open_sortition_db(true).unwrap();
|
||||
let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
|
||||
let miner_pubkey = StacksPublicKey::from_private(&conf.get_miner_config().mining_key.unwrap());
|
||||
let miner_slot_id = NakamotoChainState::get_miner_slot(&sortdb, &tip, &miner_pubkey)
|
||||
.map_err(|_| "Unable to get miner slot")?
|
||||
.ok_or("No miner slot exists")?;
|
||||
let reward_cycle = burnchain
|
||||
.block_height_to_reward_cycle(tip.block_height)
|
||||
.unwrap();
|
||||
let rpc_sock = conf
|
||||
.node
|
||||
.rpc_bind
|
||||
.clone()
|
||||
.parse()
|
||||
.expect("Failed to parse socket");
|
||||
|
||||
let mut proposed_block: NakamotoBlock = {
|
||||
let miner_contract_id = boot_code_id(MINERS_NAME, false);
|
||||
let mut miners_stackerdb = StackerDBSession::new(rpc_sock, miner_contract_id);
|
||||
miners_stackerdb
|
||||
.get_latest(miner_slot_id)
|
||||
.map_err(|_| "Failed to get latest chunk from the miner slot ID")?
|
||||
.ok_or("No chunk found")?
|
||||
};
|
||||
let proposed_block_hash = format!("0x{}", proposed_block.header.block_hash());
|
||||
let signer_sig_hash = proposed_block.header.signer_signature_hash();
|
||||
if signed_blocks.contains(&signer_sig_hash) {
|
||||
// already signed off on this block, don't sign again.
|
||||
return Ok(signer_sig_hash);
|
||||
}
|
||||
|
||||
info!(
|
||||
"Fetched proposed block from .miners StackerDB";
|
||||
"proposed_block_hash" => &proposed_block_hash,
|
||||
"signer_sig_hash" => &signer_sig_hash.to_hex(),
|
||||
);
|
||||
|
||||
signers
|
||||
.clone()
|
||||
.sign_nakamoto_block(&mut proposed_block, reward_cycle);
|
||||
|
||||
let signer_message = SignerMessage::BlockResponse(BlockResponse::Accepted((
|
||||
signer_sig_hash.clone(),
|
||||
proposed_block.header.signer_signature.clone(),
|
||||
)));
|
||||
|
||||
let signers_contract_id =
|
||||
NakamotoSigners::make_signers_db_contract_id(reward_cycle, libsigner::BLOCK_MSG_ID, false);
|
||||
|
||||
let http_origin = format!("http://{}", &conf.node.rpc_bind);
|
||||
let signers_info = get_stacker_set(&http_origin, reward_cycle);
|
||||
let signer_index = get_signer_index(&signers_info, &Secp256k1PublicKey::from_private(signer))
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let next_version = get_stackerdb_slot_version(&http_origin, &signers_contract_id, signer_index)
|
||||
.map(|x| x + 1)
|
||||
.unwrap_or(0);
|
||||
let mut signers_contract_sess = StackerDBSession::new(rpc_sock, signers_contract_id);
|
||||
let mut chunk_to_put = StackerDBChunkData::new(
|
||||
u32::try_from(signer_index).unwrap(),
|
||||
next_version,
|
||||
signer_message.serialize_to_vec(),
|
||||
);
|
||||
chunk_to_put.sign(signer).unwrap();
|
||||
signers_contract_sess
|
||||
.put_chunk(&chunk_to_put)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(signer_sig_hash)
|
||||
}
|
||||
|
||||
fn signer_vote_if_needed(conf: &Config, signers: &TestSigners, signer: &Secp256k1PrivateKey) {
|
||||
let burnchain = conf.get_burnchain();
|
||||
let sortdb = burnchain.open_sortition_db(true).unwrap();
|
||||
let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
|
||||
let reward_cycle = burnchain
|
||||
.block_height_to_reward_cycle(tip.block_height)
|
||||
.unwrap();
|
||||
let prepare_phase_start = burnchain
|
||||
.pox_constants
|
||||
.prepare_phase_start(burnchain.first_block_height, reward_cycle);
|
||||
|
||||
if tip.block_height >= prepare_phase_start {
|
||||
// If the key is already set, do nothing.
|
||||
if is_key_set_for_cycle(reward_cycle + 1, conf.is_mainnet(), &conf.node.rpc_bind)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are self-signing, then we need to vote on the aggregate public key
|
||||
let http_origin = format!("http://{}", &conf.node.rpc_bind);
|
||||
|
||||
// Get the aggregate key
|
||||
let aggregate_key = signers.clone().generate_aggregate_key(reward_cycle + 1);
|
||||
let aggregate_public_key = Value::buff_from(aggregate_key.compress().data.to_vec())
|
||||
.expect("Failed to serialize aggregate public key");
|
||||
|
||||
let signer_nonce = get_account(&http_origin, &to_addr(signer)).nonce;
|
||||
|
||||
// Vote on the aggregate public key
|
||||
let voting_tx = make_contract_call(
|
||||
&signer,
|
||||
signer_nonce,
|
||||
300,
|
||||
&StacksAddress::burn_address(false),
|
||||
SIGNERS_VOTING_NAME,
|
||||
"vote-for-aggregate-public-key",
|
||||
&[
|
||||
Value::UInt(0),
|
||||
aggregate_public_key.clone(),
|
||||
Value::UInt(0),
|
||||
Value::UInt(reward_cycle as u128 + 1),
|
||||
],
|
||||
);
|
||||
submit_tx(&http_origin, &voting_tx);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_stacker_set(http_origin: &str, cycle: u64) -> GetStackersResponse {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{http_origin}/v2/stacker_set/{cycle}");
|
||||
let res = client
|
||||
.get(&path)
|
||||
.send()
|
||||
.unwrap()
|
||||
.json::<serde_json::Value>()
|
||||
.unwrap();
|
||||
info!("Stacker set response: {res}");
|
||||
let res = serde_json::from_value(res).unwrap();
|
||||
res
|
||||
}
|
||||
|
||||
fn get_signer_index(
|
||||
stacker_set: &GetStackersResponse,
|
||||
signer_key: &Secp256k1PublicKey,
|
||||
) -> Result<usize, String> {
|
||||
let Some(ref signer_set) = stacker_set.stacker_set.signers else {
|
||||
return Err("Empty signer set for reward cycle".into());
|
||||
};
|
||||
let signer_key_bytes = signer_key.to_bytes_compressed();
|
||||
signer_set
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(ix, entry)| {
|
||||
if entry.signing_key.as_slice() == signer_key_bytes.as_slice() {
|
||||
Some(ix)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.ok_or_else(|| {
|
||||
format!(
|
||||
"Signing key not found. {} not found.",
|
||||
to_hex(&signer_key_bytes)
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_stackerdb_slot_version(
|
||||
http_origin: &str,
|
||||
contract: &QualifiedContractIdentifier,
|
||||
slot_id: u64,
|
||||
) -> Option<u32> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!(
|
||||
"{http_origin}/v2/stackerdb/{}/{}",
|
||||
&contract.issuer, &contract.name
|
||||
);
|
||||
let res = client
|
||||
.get(&path)
|
||||
.send()
|
||||
.unwrap()
|
||||
.json::<Vec<SlotMetadata>>()
|
||||
.unwrap();
|
||||
debug!("StackerDB metadata response: {res:?}");
|
||||
res.iter().find_map(|slot| {
|
||||
if u64::from(slot.slot_id) == slot_id {
|
||||
Some(slot.slot_version)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn is_key_set_for_cycle(
|
||||
reward_cycle: u64,
|
||||
is_mainnet: bool,
|
||||
http_origin: &str,
|
||||
) -> Result<bool, String> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let boot_address = StacksAddress::burn_address(is_mainnet);
|
||||
let path = format!("http://{http_origin}/v2/contracts/call-read/{boot_address}/signers-voting/get-approved-aggregate-key");
|
||||
let body = CallReadOnlyRequestBody {
|
||||
sender: boot_address.to_string(),
|
||||
sponsor: None,
|
||||
arguments: vec![Value::UInt(reward_cycle as u128)
|
||||
.serialize_to_hex()
|
||||
.map_err(|_| "Failed to serialize reward cycle")?],
|
||||
};
|
||||
let res = client
|
||||
.post(&path)
|
||||
.json(&body)
|
||||
.send()
|
||||
.map_err(|_| "Failed to send request")?
|
||||
.json::<serde_json::Value>()
|
||||
.map_err(|_| "Failed to extract json Value")?;
|
||||
let result_value = Value::try_deserialize_hex_untyped(
|
||||
&res.get("result")
|
||||
.ok_or("No result in response")?
|
||||
.as_str()
|
||||
.ok_or("Result is not a string")?[2..],
|
||||
)
|
||||
.map_err(|_| "Failed to deserialize Clarity value")?;
|
||||
|
||||
result_value
|
||||
.expect_optional()
|
||||
.map(|v| v.is_some())
|
||||
.map_err(|_| "Response is not optional".to_string())
|
||||
}
|
||||
41
blind-signer/src/main.rs
Normal file
41
blind-signer/src/main.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
#[macro_use]
|
||||
extern crate stacks_common;
|
||||
|
||||
use std::{process, thread::park};
|
||||
|
||||
use pico_args::Arguments;
|
||||
use stacks::{
|
||||
chainstate::nakamoto::test_signers::TestSigners, util::secp256k1::Secp256k1PrivateKey,
|
||||
};
|
||||
use stacks_node::config::{Config, ConfigFile};
|
||||
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use(o, slog_log, slog_trace, slog_debug, slog_info, slog_warn, slog_error)]
|
||||
extern crate slog;
|
||||
|
||||
fn main() {
|
||||
let mut args = Arguments::from_env();
|
||||
let config_path: String = args.value_from_str("--config").unwrap();
|
||||
args.finish();
|
||||
info!("Loading config at path {}", config_path);
|
||||
let config_file = match ConfigFile::from_path(&config_path) {
|
||||
Ok(config_file) => config_file,
|
||||
Err(e) => {
|
||||
warn!("Invalid config file: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let conf = match Config::from_config_file(config_file) {
|
||||
Ok(conf) => conf,
|
||||
Err(e) => {
|
||||
warn!("Invalid config: {}", e);
|
||||
process::exit(1);
|
||||
}
|
||||
};
|
||||
|
||||
let signers = TestSigners::default();
|
||||
let sender_signer_sk = Secp256k1PrivateKey::new();
|
||||
blind_signer::blind_signer(&conf, &signers, &sender_signer_sk);
|
||||
park();
|
||||
}
|
||||
@@ -31,6 +31,7 @@ wsts = { workspace = true }
|
||||
rand = { workspace = true }
|
||||
rand_core = { workspace = true }
|
||||
hashbrown = { workspace = true }
|
||||
reqwest = { version = "0.11", default_features = false, features = ["blocking", "json", "rustls", "rustls-tls"] }
|
||||
|
||||
[target.'cfg(not(target_env = "msvc"))'.dependencies]
|
||||
tikv-jemallocator = {workspace = true}
|
||||
@@ -44,6 +45,7 @@ clarity = { path = "../../clarity", features = ["default", "testing"]}
|
||||
stacks-common = { path = "../../stacks-common", features = ["default", "testing"] }
|
||||
stacks = { package = "stackslib", path = "../../stackslib", features = ["default", "testing"] }
|
||||
stacks-signer = { path = "../../stacks-signer" }
|
||||
blind-signer = { path = "../../blind-signer" }
|
||||
tracing = "0.1.37"
|
||||
tracing-subscriber = { version = "0.3.17", features = ["env-filter"] }
|
||||
wsts = {workspace = true}
|
||||
@@ -61,6 +63,10 @@ path = "src/main.rs"
|
||||
name = "stacks-events"
|
||||
path = "src/stacks_events.rs"
|
||||
|
||||
[lib]
|
||||
name = "stacks_node"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[features]
|
||||
monitoring_prom = ["stacks/monitoring_prom"]
|
||||
slog_json = ["stacks/slog_json", "stacks-common/slog_json", "clarity/slog_json"]
|
||||
|
||||
@@ -53,9 +53,9 @@ use stacks_common::types::chainstate::BurnchainHeaderHash;
|
||||
use stacks_common::util::hash::{hex_bytes, Hash160};
|
||||
use stacks_common::util::secp256k1::Secp256k1PublicKey;
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use super::super::operations::BurnchainOpSigner;
|
||||
use super::super::Config;
|
||||
use super::{BurnchainController, BurnchainTip, Error as BurnchainControllerError};
|
||||
|
||||
/// The number of bitcoin blocks that can have
|
||||
@@ -2556,8 +2556,9 @@ mod tests {
|
||||
use std::fs::File;
|
||||
use std::io::Write;
|
||||
|
||||
use stacks_node::config::DEFAULT_SATS_PER_VB;
|
||||
|
||||
use super::*;
|
||||
use crate::config::DEFAULT_SATS_PER_VB;
|
||||
|
||||
#[test]
|
||||
fn test_get_satoshis_per_byte() {
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
use std::collections::HashMap;
|
||||
use std::process::{Command, Stdio};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use stacks::burnchains::bitcoin::address::BitcoinAddress;
|
||||
use stacks::burnchains::bitcoin::{BitcoinNetworkType, BitcoinTxOutput};
|
||||
use stacks::burnchains::{Burnchain, BurnchainSigner, Error as BurnchainError, Txid};
|
||||
|
||||
@@ -9,6 +9,7 @@ use clarity::vm::costs::ExecutionCost;
|
||||
use clarity::vm::types::{AssetIdentifier, PrincipalData, QualifiedContractIdentifier};
|
||||
use lazy_static::lazy_static;
|
||||
use rand::RngCore;
|
||||
use serde::Deserialize;
|
||||
use stacks::burnchains::bitcoin::BitcoinNetworkType;
|
||||
use stacks::burnchains::{Burnchain, MagicBytes, BLOCKSTACK_MAGIC_MAINNET};
|
||||
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
|
||||
@@ -33,6 +34,7 @@ use stacks::net::{Neighbor, NeighborKey};
|
||||
use stacks::util_lib::boot::boot_code_id;
|
||||
use stacks::util_lib::db::Error as DBError;
|
||||
use stacks_common::consts::SIGNER_SLOTS_PER_USER;
|
||||
use stacks_common::test_debug;
|
||||
use stacks_common::types::chainstate::StacksAddress;
|
||||
use stacks_common::types::net::PeerAddress;
|
||||
use stacks_common::types::Address;
|
||||
|
||||
@@ -39,8 +39,7 @@ use stacks::net::stackerdb::StackerDBEventDispatcher;
|
||||
use stacks_common::codec::StacksMessageCodec;
|
||||
use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, StacksBlockId};
|
||||
use stacks_common::util::hash::bytes_to_hex;
|
||||
|
||||
use super::config::{EventKeyType, EventObserverConfig};
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig};
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct EventObserver {
|
||||
|
||||
@@ -12,8 +12,8 @@ use stacks::chainstate::stacks::db::StacksChainState;
|
||||
use stacks::chainstate::stacks::miner::MinerStatus;
|
||||
use stacks::net::NetworkResult;
|
||||
use stacks_common::types::chainstate::{BlockHeaderHash, BurnchainHeaderHash, ConsensusHash};
|
||||
use stacks_node::config::MinerConfig;
|
||||
|
||||
use crate::config::MinerConfig;
|
||||
use crate::neon::Counters;
|
||||
use crate::neon_node::LeaderKeyRegistrationState;
|
||||
use crate::run_loop::RegisteredKey;
|
||||
|
||||
9
testnet/stacks-node/src/lib.rs
Normal file
9
testnet/stacks-node/src/lib.rs
Normal file
@@ -0,0 +1,9 @@
|
||||
#[allow(unused_imports)]
|
||||
#[macro_use(o, slog_log, slog_trace, slog_debug, slog_info, slog_warn, slog_error)]
|
||||
extern crate slog;
|
||||
#[macro_use]
|
||||
extern crate stacks_common;
|
||||
|
||||
pub mod chain_data;
|
||||
pub mod config;
|
||||
pub mod utils;
|
||||
@@ -12,12 +12,12 @@ extern crate slog;
|
||||
|
||||
pub use stacks_common::util;
|
||||
use stacks_common::util::hash::hex_bytes;
|
||||
use stacks_node::chain_data::MinerStats;
|
||||
use stacks_node::config::{Config, ConfigFile};
|
||||
|
||||
pub mod monitoring;
|
||||
|
||||
pub mod burnchains;
|
||||
pub mod chain_data;
|
||||
pub mod config;
|
||||
pub mod event_dispatcher;
|
||||
pub mod genesis_data;
|
||||
pub mod globals;
|
||||
@@ -47,13 +47,11 @@ use tikv_jemallocator::Jemalloc;
|
||||
pub use self::burnchains::{
|
||||
BitcoinRegtestController, BurnchainController, BurnchainTip, MocknetController,
|
||||
};
|
||||
pub use self::config::{Config, ConfigFile};
|
||||
pub use self::event_dispatcher::EventDispatcher;
|
||||
pub use self::keychain::Keychain;
|
||||
pub use self::node::{ChainTip, Node};
|
||||
pub use self::run_loop::{helium, neon};
|
||||
pub use self::tenure::Tenure;
|
||||
use crate::chain_data::MinerStats;
|
||||
use crate::neon_node::{BlockMinerThread, TipCandidate};
|
||||
use crate::run_loop::boot_nakamoto;
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ use stacks::net::stackerdb::StackerDBs;
|
||||
use stacks_common::types::chainstate::SortitionId;
|
||||
use stacks_common::types::StacksEpochId;
|
||||
|
||||
use super::{Config, EventDispatcher, Keychain};
|
||||
use crate::burnchains::bitcoin_regtest_controller::addr2str;
|
||||
use crate::neon_node::{LeaderKeyRegistrationState, StacksNode as NeonNode};
|
||||
use crate::run_loop::nakamoto::{Globals, RunLoop};
|
||||
use crate::run_loop::RegisteredKey;
|
||||
use crate::Keychain;
|
||||
|
||||
pub mod miner;
|
||||
pub mod peer;
|
||||
|
||||
@@ -46,14 +46,15 @@ use stacks_common::types::chainstate::{StacksAddress, StacksBlockId};
|
||||
use stacks_common::types::{PrivateKey, StacksEpochId};
|
||||
use stacks_common::util::hash::{Hash160, Sha512Trunc256Sum};
|
||||
use stacks_common::util::vrf::VRFProof;
|
||||
use stacks_node::config::Config;
|
||||
use wsts::curve::point::Point;
|
||||
|
||||
use super::relayer::RelayerThread;
|
||||
use super::{Config, Error as NakamotoNodeError, EventDispatcher, Keychain};
|
||||
use super::{Error as NakamotoNodeError, Keychain};
|
||||
use crate::nakamoto_node::VRF_MOCK_MINER_KEY;
|
||||
use crate::run_loop::nakamoto::Globals;
|
||||
use crate::run_loop::RegisteredKey;
|
||||
use crate::{neon_node, ChainTip};
|
||||
use crate::{neon_node, ChainTip, EventDispatcher};
|
||||
|
||||
/// If the miner was interrupted while mining a block, how long should the
|
||||
/// miner thread sleep before trying again?
|
||||
|
||||
@@ -31,12 +31,13 @@ use stacks::net::dns::{DNSClient, DNSResolver};
|
||||
use stacks::net::p2p::PeerNetwork;
|
||||
use stacks::net::RPCHandlerArgs;
|
||||
use stacks_common::util::hash::Sha256Sum;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use crate::burnchains::make_bitcoin_indexer;
|
||||
use crate::nakamoto_node::relayer::RelayerDirective;
|
||||
use crate::neon_node::open_chainstate_with_faults;
|
||||
use crate::run_loop::nakamoto::{Globals, RunLoop};
|
||||
use crate::{Config, EventDispatcher};
|
||||
use crate::EventDispatcher;
|
||||
|
||||
/// Thread that runs the network state machine, handling both p2p and http requests.
|
||||
pub struct PeerThread {
|
||||
|
||||
@@ -50,11 +50,9 @@ use stacks_common::types::StacksEpochId;
|
||||
use stacks_common::util::get_epoch_time_ms;
|
||||
use stacks_common::util::hash::Hash160;
|
||||
use stacks_common::util::vrf::{VRFProof, VRFPublicKey};
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use super::{
|
||||
BlockCommits, Config, Error as NakamotoNodeError, EventDispatcher, Keychain,
|
||||
BLOCK_PROCESSOR_STACK_SIZE,
|
||||
};
|
||||
use super::{BlockCommits, Error as NakamotoNodeError, Keychain, BLOCK_PROCESSOR_STACK_SIZE};
|
||||
use crate::burnchains::BurnchainController;
|
||||
use crate::nakamoto_node::miner::{BlockMinerThread, MinerDirective};
|
||||
use crate::neon_node::{
|
||||
@@ -62,7 +60,7 @@ use crate::neon_node::{
|
||||
};
|
||||
use crate::run_loop::nakamoto::{Globals, RunLoop};
|
||||
use crate::run_loop::RegisteredKey;
|
||||
use crate::BitcoinRegtestController;
|
||||
use crate::{BitcoinRegtestController, EventDispatcher};
|
||||
|
||||
/// Command types for the Nakamoto relayer thread, issued to it by other threads
|
||||
pub enum RelayerDirective {
|
||||
|
||||
@@ -201,13 +201,14 @@ use stacks_common::util::hash::{to_hex, Hash160, Sha256Sum};
|
||||
use stacks_common::util::secp256k1::Secp256k1PrivateKey;
|
||||
use stacks_common::util::vrf::{VRFProof, VRFPublicKey};
|
||||
use stacks_common::util::{get_epoch_time_ms, get_epoch_time_secs};
|
||||
use stacks_node::chain_data::MinerStats;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use super::{BurnchainController, Config, EventDispatcher, Keychain};
|
||||
use super::{BurnchainController, EventDispatcher, Keychain};
|
||||
use crate::burnchains::bitcoin_regtest_controller::{
|
||||
addr2str, BitcoinRegtestController, OngoingBlockCommit,
|
||||
};
|
||||
use crate::burnchains::make_bitcoin_indexer;
|
||||
use crate::chain_data::MinerStats;
|
||||
use crate::globals::{NeonGlobals as Globals, RelayerDirective};
|
||||
use crate::run_loop::neon::RunLoop;
|
||||
use crate::run_loop::RegisteredKey;
|
||||
|
||||
@@ -44,8 +44,9 @@ use stacks_common::util::get_epoch_time_secs;
|
||||
use stacks_common::util::hash::Sha256Sum;
|
||||
use stacks_common::util::secp256k1::Secp256k1PrivateKey;
|
||||
use stacks_common::util::vrf::VRFPublicKey;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use super::{BurnchainController, BurnchainTip, Config, EventDispatcher, Keychain, Tenure};
|
||||
use super::{BurnchainController, BurnchainTip, EventDispatcher, Keychain, Tenure};
|
||||
use crate::burnchains::make_bitcoin_indexer;
|
||||
use crate::genesis_data::USE_TEST_GENESIS_CHAINSTATE;
|
||||
use crate::run_loop;
|
||||
|
||||
@@ -24,11 +24,11 @@ use stacks::chainstate::burn::db::sortdb::SortitionDB;
|
||||
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
|
||||
use stacks::core::StacksEpochExtension;
|
||||
use stacks_common::types::{StacksEpoch, StacksEpochId};
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use crate::neon::Counters;
|
||||
use crate::run_loop::nakamoto::RunLoop as NakaRunLoop;
|
||||
use crate::run_loop::neon::RunLoop as NeonRunLoop;
|
||||
use crate::Config;
|
||||
|
||||
/// This runloop handles booting to Nakamoto:
|
||||
/// During epochs [1.0, 2.5], it runs a neon run_loop.
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
use stacks::chainstate::stacks::db::ClarityTx;
|
||||
use stacks_common::types::chainstate::BurnchainHeaderHash;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use super::RunLoopCallbacks;
|
||||
use crate::burnchains::Error as BurnchainControllerError;
|
||||
use crate::{
|
||||
BitcoinRegtestController, BurnchainController, ChainTip, Config, MocknetController, Node,
|
||||
};
|
||||
use crate::{BitcoinRegtestController, BurnchainController, ChainTip, MocknetController, Node};
|
||||
|
||||
/// RunLoop is coordinating a simulated burnchain and some simulated nodes
|
||||
/// taking turns in producing blocks.
|
||||
|
||||
@@ -33,6 +33,7 @@ use stacks::core::StacksEpochId;
|
||||
use stacks::net::atlas::{AtlasConfig, AtlasDB, Attachment};
|
||||
use stacks_common::types::PublicKey;
|
||||
use stacks_common::util::hash::Hash160;
|
||||
use stacks_node::config::Config;
|
||||
use stx_genesis::GenesisData;
|
||||
|
||||
use crate::burnchains::make_bitcoin_indexer;
|
||||
@@ -46,9 +47,7 @@ use crate::node::{
|
||||
use crate::run_loop::neon;
|
||||
use crate::run_loop::neon::Counters;
|
||||
use crate::syncctl::{PoxSyncWatchdog, PoxSyncWatchdogComms};
|
||||
use crate::{
|
||||
run_loop, BitcoinRegtestController, BurnchainController, Config, EventDispatcher, Keychain,
|
||||
};
|
||||
use crate::{run_loop, BitcoinRegtestController, BurnchainController, EventDispatcher, Keychain};
|
||||
|
||||
pub const STDERR: i32 = 2;
|
||||
pub type Globals = GenericGlobals<nakamoto_node::relayer::RelayerDirective>;
|
||||
|
||||
@@ -27,6 +27,7 @@ use stacks_common::deps_common::ctrlc::SignalId;
|
||||
use stacks_common::types::PublicKey;
|
||||
use stacks_common::util::hash::Hash160;
|
||||
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
|
||||
use stacks_node::config::Config;
|
||||
use stx_genesis::GenesisData;
|
||||
|
||||
use super::RunLoopCallbacks;
|
||||
@@ -39,9 +40,7 @@ use crate::node::{
|
||||
use_test_genesis_chainstate,
|
||||
};
|
||||
use crate::syncctl::{PoxSyncWatchdog, PoxSyncWatchdogComms};
|
||||
use crate::{
|
||||
run_loop, BitcoinRegtestController, BurnchainController, Config, EventDispatcher, Keychain,
|
||||
};
|
||||
use crate::{run_loop, BitcoinRegtestController, BurnchainController, EventDispatcher, Keychain};
|
||||
|
||||
pub const STDERR: i32 = 2;
|
||||
|
||||
|
||||
@@ -5,9 +5,9 @@ use std::sync::Arc;
|
||||
use stacks::burnchains::{Burnchain, Error as burnchain_error};
|
||||
use stacks::chainstate::stacks::db::StacksChainState;
|
||||
use stacks_common::util::{get_epoch_time_secs, sleep_ms};
|
||||
use stacks_node::config::Config;
|
||||
|
||||
use crate::burnchains::BurnchainTip;
|
||||
use crate::Config;
|
||||
|
||||
// amount of time to wait for an inv or download sync to complete.
|
||||
// These _really should_ complete before the PoX sync watchdog permits processing the next reward
|
||||
|
||||
@@ -16,10 +16,11 @@ use stacks::core::mempool::MemPoolDB;
|
||||
use stacks_common::types::chainstate::VRFSeed;
|
||||
use stacks_common::util::hash::Hash160;
|
||||
use stacks_common::util::vrf::VRFProof;
|
||||
use stacks_node::config::Config;
|
||||
|
||||
/// Only used by the Helium (Mocknet) node
|
||||
use super::node::ChainTip;
|
||||
use super::{BurnchainTip, Config};
|
||||
use super::BurnchainTip;
|
||||
|
||||
pub struct TenureArtifacts {
|
||||
pub anchored_block: StacksBlock,
|
||||
|
||||
@@ -9,9 +9,9 @@ use stacks::chainstate::burn::operations::BlockstackOperationType::{
|
||||
use stacks::chainstate::stacks::StacksPrivateKey;
|
||||
use stacks::core::StacksEpochId;
|
||||
use stacks_common::util::hash::hex_bytes;
|
||||
use stacks_node::config::InitialBalance;
|
||||
|
||||
use super::PUBLISH_CONTRACT;
|
||||
use crate::config::InitialBalance;
|
||||
use crate::helium::RunLoop;
|
||||
use crate::tests::to_addr;
|
||||
use crate::Config;
|
||||
|
||||
@@ -24,8 +24,9 @@ use stacks_common::types::chainstate::{
|
||||
};
|
||||
use stacks_common::util::hash::hex_bytes;
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::tests::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::tests::neon_integrations::*;
|
||||
use crate::tests::{
|
||||
|
||||
@@ -33,9 +33,10 @@ use stacks_common::types::PrivateKey;
|
||||
use stacks_common::util::hash::{Hash160, Sha256Sum};
|
||||
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::{Config, EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use crate::burnchains::bitcoin_regtest_controller::UTXO;
|
||||
use crate::config::{Config, EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::neon::RunLoopCounter;
|
||||
use crate::operations::BurnchainOpSigner;
|
||||
use crate::stacks_common::address::AddressHashMode;
|
||||
|
||||
@@ -16,9 +16,9 @@ use stacks_common::types::PrivateKey;
|
||||
use stacks_common::util::hash::Hash160;
|
||||
use stacks_common::util::secp256k1::Secp256k1PublicKey;
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use super::neon_integrations::get_account;
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::neon_node::StacksNode;
|
||||
use crate::stacks_common::types::Address;
|
||||
use crate::stacks_common::util::hash::bytes_to_hex;
|
||||
|
||||
@@ -21,8 +21,9 @@ use stacks::burnchains::{Burnchain, PoxConstants};
|
||||
use stacks::core;
|
||||
use stacks::core::STACKS_EPOCH_MAX;
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::submit_tx;
|
||||
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::tests::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::tests::neon_integrations::*;
|
||||
use crate::tests::*;
|
||||
|
||||
@@ -35,12 +35,13 @@ use stacks_common::types::Address;
|
||||
use stacks_common::util::hash::{bytes_to_hex, hex_bytes, Hash160};
|
||||
use stacks_common::util::secp256k1::Secp256k1PublicKey;
|
||||
use stacks_common::util::sleep_ms;
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::tests::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::tests::neon_integrations::{
|
||||
get_account, get_chain_info, get_pox_info, neon_integration_test_conf, next_block_and_wait,
|
||||
submit_tx, test_observer, wait_for_runloop,
|
||||
get_chain_info, get_pox_info, neon_integration_test_conf, next_block_and_wait, test_observer,
|
||||
wait_for_runloop,
|
||||
};
|
||||
use crate::tests::{make_contract_call, to_addr};
|
||||
use crate::{neon, BitcoinRegtestController, BurnchainController};
|
||||
|
||||
@@ -34,12 +34,12 @@ use stacks::net::api::getistraitimplemented::GetIsTraitImplementedResponse;
|
||||
use stacks_common::codec::StacksMessageCodec;
|
||||
use stacks_common::types::chainstate::{StacksAddress, StacksBlockId, VRFSeed};
|
||||
use stacks_common::util::hash::{hex_bytes, to_hex, Sha256Sum};
|
||||
use stacks_node::config::InitialBalance;
|
||||
|
||||
use super::{
|
||||
make_contract_call, make_contract_publish, make_stacks_transfer, to_addr, ADDR_4, SK_1, SK_2,
|
||||
SK_3,
|
||||
};
|
||||
use crate::config::InitialBalance;
|
||||
use crate::helium::RunLoop;
|
||||
use crate::tests::make_sponsored_stacks_transfer_on_testnet;
|
||||
|
||||
|
||||
@@ -13,23 +13,22 @@
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::atomic::{AtomicU64, Ordering};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::{Duration, Instant};
|
||||
use std::{env, thread};
|
||||
|
||||
use blind_signer::blind_signer;
|
||||
use clarity::vm::ast::ASTRules;
|
||||
use clarity::vm::costs::ExecutionCost;
|
||||
use clarity::vm::types::{PrincipalData, QualifiedContractIdentifier};
|
||||
use lazy_static::lazy_static;
|
||||
use libsigner::{BlockResponse, SignerMessage, SignerSession, StackerDBSession};
|
||||
use libsigner::{SignerSession, StackerDBSession};
|
||||
use stacks::burnchains::MagicBytes;
|
||||
use stacks::chainstate::burn::db::sortdb::SortitionDB;
|
||||
use stacks::chainstate::coordinator::comm::CoordinatorChannels;
|
||||
use stacks::chainstate::nakamoto::miner::NakamotoBlockBuilder;
|
||||
use stacks::chainstate::nakamoto::signer_set::NakamotoSigners;
|
||||
use stacks::chainstate::nakamoto::test_signers::TestSigners;
|
||||
use stacks::chainstate::nakamoto::{NakamotoBlock, NakamotoChainState};
|
||||
use stacks::chainstate::stacks::address::PoxAddress;
|
||||
@@ -43,7 +42,7 @@ use stacks::core::{
|
||||
PEER_VERSION_EPOCH_2_1, PEER_VERSION_EPOCH_2_2, PEER_VERSION_EPOCH_2_3, PEER_VERSION_EPOCH_2_4,
|
||||
PEER_VERSION_EPOCH_2_5, PEER_VERSION_EPOCH_3_0,
|
||||
};
|
||||
use stacks::libstackerdb::{SlotMetadata, StackerDBChunkData};
|
||||
use stacks::libstackerdb::SlotMetadata;
|
||||
use stacks::net::api::callreadonly::CallReadOnlyRequestBody;
|
||||
use stacks::net::api::getstackers::GetStackersResponse;
|
||||
use stacks::net::api::postblock_proposal::{
|
||||
@@ -59,16 +58,17 @@ use stacks_common::consts::{CHAIN_ID_TESTNET, STACKS_EPOCH_MAX};
|
||||
use stacks_common::types::chainstate::{
|
||||
BlockHeaderHash, StacksAddress, StacksPrivateKey, StacksPublicKey,
|
||||
};
|
||||
use stacks_common::util::hash::{to_hex, Sha512Trunc256Sum};
|
||||
use stacks_common::util::hash::to_hex;
|
||||
use stacks_common::util::secp256k1::{MessageSignature, Secp256k1PrivateKey, Secp256k1PublicKey};
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use super::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::neon::{Counters, RunLoopCounter};
|
||||
use crate::neon::Counters;
|
||||
use crate::run_loop::boot_nakamoto;
|
||||
use crate::tests::neon_integrations::{
|
||||
get_account, get_chain_info_result, get_pox_info, next_block_and_wait,
|
||||
run_until_burnchain_height, submit_tx, test_observer, wait_for_runloop,
|
||||
get_chain_info_result, get_pox_info, next_block_and_wait, run_until_burnchain_height,
|
||||
test_observer, wait_for_runloop,
|
||||
};
|
||||
use crate::tests::{make_stacks_transfer, to_addr};
|
||||
use crate::{tests, BitcoinRegtestController, BurnchainController, Config, ConfigFile, Keychain};
|
||||
@@ -201,120 +201,6 @@ pub fn add_initial_balances(
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Spawn a blind signing thread. `signer` is the private key
|
||||
/// of the individual signer who broadcasts the response to the StackerDB
|
||||
pub fn blind_signer(
|
||||
conf: &Config,
|
||||
signers: &TestSigners,
|
||||
signer: &Secp256k1PrivateKey,
|
||||
proposals_count: RunLoopCounter,
|
||||
) -> JoinHandle<()> {
|
||||
let mut signed_blocks = HashSet::new();
|
||||
let conf = conf.clone();
|
||||
let signers = signers.clone();
|
||||
let signer = signer.clone();
|
||||
let mut last_count = proposals_count.load(Ordering::SeqCst);
|
||||
thread::spawn(move || loop {
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
let cur_count = proposals_count.load(Ordering::SeqCst);
|
||||
if cur_count <= last_count {
|
||||
continue;
|
||||
}
|
||||
last_count = cur_count;
|
||||
match read_and_sign_block_proposal(&conf, &signers, &signer, &signed_blocks) {
|
||||
Ok(signed_block) => {
|
||||
if signed_blocks.contains(&signed_block) {
|
||||
continue;
|
||||
}
|
||||
info!("Signed block"; "signer_sig_hash" => signed_block.to_hex());
|
||||
signed_blocks.insert(signed_block);
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Error reading and signing block proposal: {e}");
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_and_sign_block_proposal(
|
||||
conf: &Config,
|
||||
signers: &TestSigners,
|
||||
signer: &Secp256k1PrivateKey,
|
||||
signed_blocks: &HashSet<Sha512Trunc256Sum>,
|
||||
) -> Result<Sha512Trunc256Sum, String> {
|
||||
let burnchain = conf.get_burnchain();
|
||||
let sortdb = burnchain.open_sortition_db(true).unwrap();
|
||||
let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn()).unwrap();
|
||||
let miner_pubkey = StacksPublicKey::from_private(&conf.get_miner_config().mining_key.unwrap());
|
||||
let miner_slot_id = NakamotoChainState::get_miner_slot(&sortdb, &tip, &miner_pubkey)
|
||||
.map_err(|_| "Unable to get miner slot")?
|
||||
.ok_or("No miner slot exists")?;
|
||||
let reward_cycle = burnchain
|
||||
.block_height_to_reward_cycle(tip.block_height)
|
||||
.unwrap();
|
||||
let rpc_sock = conf
|
||||
.node
|
||||
.rpc_bind
|
||||
.clone()
|
||||
.parse()
|
||||
.expect("Failed to parse socket");
|
||||
|
||||
let mut proposed_block: NakamotoBlock = {
|
||||
let miner_contract_id = boot_code_id(MINERS_NAME, false);
|
||||
let mut miners_stackerdb = StackerDBSession::new(rpc_sock, miner_contract_id);
|
||||
miners_stackerdb
|
||||
.get_latest(miner_slot_id)
|
||||
.map_err(|_| "Failed to get latest chunk from the miner slot ID")?
|
||||
.ok_or("No chunk found")?
|
||||
};
|
||||
let proposed_block_hash = format!("0x{}", proposed_block.header.block_hash());
|
||||
let signer_sig_hash = proposed_block.header.signer_signature_hash();
|
||||
if signed_blocks.contains(&signer_sig_hash) {
|
||||
// already signed off on this block, don't sign again.
|
||||
return Ok(signer_sig_hash);
|
||||
}
|
||||
|
||||
info!(
|
||||
"Fetched proposed block from .miners StackerDB";
|
||||
"proposed_block_hash" => &proposed_block_hash,
|
||||
"signer_sig_hash" => &signer_sig_hash.to_hex(),
|
||||
);
|
||||
|
||||
signers
|
||||
.clone()
|
||||
.sign_nakamoto_block(&mut proposed_block, reward_cycle);
|
||||
|
||||
let signer_message = SignerMessage::BlockResponse(BlockResponse::Accepted((
|
||||
signer_sig_hash.clone(),
|
||||
proposed_block.header.signer_signature.clone(),
|
||||
)));
|
||||
|
||||
let signers_contract_id =
|
||||
NakamotoSigners::make_signers_db_contract_id(reward_cycle, libsigner::BLOCK_MSG_ID, false);
|
||||
|
||||
let http_origin = format!("http://{}", &conf.node.rpc_bind);
|
||||
let signers_info = get_stacker_set(&http_origin, reward_cycle);
|
||||
let signer_index = get_signer_index(&signers_info, &Secp256k1PublicKey::from_private(signer))
|
||||
.unwrap()
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let next_version = get_stackerdb_slot_version(&http_origin, &signers_contract_id, signer_index)
|
||||
.map(|x| x + 1)
|
||||
.unwrap_or(0);
|
||||
let mut signers_contract_sess = StackerDBSession::new(rpc_sock, signers_contract_id);
|
||||
let mut chunk_to_put = StackerDBChunkData::new(
|
||||
u32::try_from(signer_index).unwrap(),
|
||||
next_version,
|
||||
signer_message.serialize_to_vec(),
|
||||
);
|
||||
chunk_to_put.sign(signer).unwrap();
|
||||
signers_contract_sess
|
||||
.put_chunk(&chunk_to_put)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(signer_sig_hash)
|
||||
}
|
||||
|
||||
/// Return a working nakamoto-neon config and the miner's bitcoin address to fund
|
||||
pub fn naka_neon_integration_conf(seed: Option<&[u8]>) -> (Config, StacksAddress) {
|
||||
let mut conf = super::new_test_conf();
|
||||
@@ -687,70 +573,6 @@ fn is_key_set_for_cycle(
|
||||
.map_err(|_| "Response is not optional".to_string())
|
||||
}
|
||||
|
||||
fn signer_vote_if_needed(
|
||||
btc_regtest_controller: &BitcoinRegtestController,
|
||||
naka_conf: &Config,
|
||||
signer_sks: &[StacksPrivateKey], // TODO: Is there some way to get this from the TestSigners?
|
||||
signers: &TestSigners,
|
||||
) {
|
||||
// When we reach the next prepare phase, submit new voting transactions
|
||||
let block_height = btc_regtest_controller.get_headers_height();
|
||||
let reward_cycle = btc_regtest_controller
|
||||
.get_burnchain()
|
||||
.block_height_to_reward_cycle(block_height)
|
||||
.unwrap();
|
||||
let prepare_phase_start = btc_regtest_controller
|
||||
.get_burnchain()
|
||||
.pox_constants
|
||||
.prepare_phase_start(
|
||||
btc_regtest_controller.get_burnchain().first_block_height,
|
||||
reward_cycle,
|
||||
);
|
||||
|
||||
if block_height >= prepare_phase_start {
|
||||
// If the key is already set, do nothing.
|
||||
if is_key_set_for_cycle(
|
||||
reward_cycle + 1,
|
||||
naka_conf.is_mainnet(),
|
||||
&naka_conf.node.rpc_bind,
|
||||
)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If we are self-signing, then we need to vote on the aggregate public key
|
||||
let http_origin = format!("http://{}", &naka_conf.node.rpc_bind);
|
||||
|
||||
// Get the aggregate key
|
||||
let aggregate_key = signers.clone().generate_aggregate_key(reward_cycle + 1);
|
||||
let aggregate_public_key =
|
||||
clarity::vm::Value::buff_from(aggregate_key.compress().data.to_vec())
|
||||
.expect("Failed to serialize aggregate public key");
|
||||
|
||||
for (i, signer_sk) in signer_sks.iter().enumerate() {
|
||||
let signer_nonce = get_account(&http_origin, &to_addr(signer_sk)).nonce;
|
||||
|
||||
// Vote on the aggregate public key
|
||||
let voting_tx = tests::make_contract_call(
|
||||
&signer_sk,
|
||||
signer_nonce,
|
||||
300,
|
||||
&StacksAddress::burn_address(false),
|
||||
SIGNERS_VOTING_NAME,
|
||||
"vote-for-aggregate-public-key",
|
||||
&[
|
||||
clarity::vm::Value::UInt(i as u128),
|
||||
aggregate_public_key.clone(),
|
||||
clarity::vm::Value::UInt(0),
|
||||
clarity::vm::Value::UInt(reward_cycle as u128 + 1),
|
||||
],
|
||||
);
|
||||
submit_tx(&http_origin, &voting_tx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// * `stacker_sks` - must be a private key for sending a large `stack-stx` transaction in order
|
||||
/// for pox-4 to activate
|
||||
@@ -908,7 +730,6 @@ fn simple_neon_integration() {
|
||||
blocks_processed,
|
||||
naka_submitted_vrfs: vrfs_submitted,
|
||||
naka_submitted_commits: commits_submitted,
|
||||
naka_proposed_blocks: proposals_submitted,
|
||||
..
|
||||
} = run_loop.counters();
|
||||
|
||||
@@ -959,7 +780,7 @@ fn simple_neon_integration() {
|
||||
}
|
||||
|
||||
info!("Nakamoto miner started...");
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk, proposals_submitted);
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk);
|
||||
|
||||
// first block wakes up the run loop, wait until a key registration has been submitted.
|
||||
next_block_and(&mut btc_regtest_controller, 60, || {
|
||||
@@ -984,13 +805,6 @@ fn simple_neon_integration() {
|
||||
&commits_submitted,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
signer_vote_if_needed(
|
||||
&btc_regtest_controller,
|
||||
&naka_conf,
|
||||
&[sender_signer_sk],
|
||||
&signers,
|
||||
);
|
||||
}
|
||||
|
||||
// Submit a TX
|
||||
@@ -1026,13 +840,6 @@ fn simple_neon_integration() {
|
||||
&commits_submitted,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
signer_vote_if_needed(
|
||||
&btc_regtest_controller,
|
||||
&naka_conf,
|
||||
&[sender_signer_sk],
|
||||
&signers,
|
||||
);
|
||||
}
|
||||
|
||||
// load the chain tip, and assert that it is a nakamoto block and at least 30 blocks have advanced in epoch 3
|
||||
@@ -1149,7 +956,6 @@ fn mine_multiple_per_tenure_integration() {
|
||||
blocks_processed,
|
||||
naka_submitted_vrfs: vrfs_submitted,
|
||||
naka_submitted_commits: commits_submitted,
|
||||
naka_proposed_blocks: proposals_submitted,
|
||||
..
|
||||
} = run_loop.counters();
|
||||
|
||||
@@ -1188,7 +994,7 @@ fn mine_multiple_per_tenure_integration() {
|
||||
.stacks_block_height;
|
||||
|
||||
info!("Nakamoto miner started...");
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk, proposals_submitted);
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk);
|
||||
|
||||
// first block wakes up the run loop, wait until a key registration has been submitted.
|
||||
next_block_and(&mut btc_regtest_controller, 60, || {
|
||||
@@ -1343,7 +1149,6 @@ fn correct_burn_outs() {
|
||||
blocks_processed,
|
||||
naka_submitted_vrfs: vrfs_submitted,
|
||||
naka_submitted_commits: commits_submitted,
|
||||
naka_proposed_blocks: proposals_submitted,
|
||||
..
|
||||
} = run_loop.counters();
|
||||
|
||||
@@ -1454,33 +1259,7 @@ fn correct_burn_outs() {
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
let block_height = btc_regtest_controller.get_headers_height();
|
||||
let reward_cycle = btc_regtest_controller
|
||||
.get_burnchain()
|
||||
.block_height_to_reward_cycle(block_height)
|
||||
.unwrap();
|
||||
let prepare_phase_start = btc_regtest_controller
|
||||
.get_burnchain()
|
||||
.pox_constants
|
||||
.prepare_phase_start(
|
||||
btc_regtest_controller.get_burnchain().first_block_height,
|
||||
reward_cycle,
|
||||
);
|
||||
|
||||
// Run until the prepare phase
|
||||
run_until_burnchain_height(
|
||||
&mut btc_regtest_controller,
|
||||
&blocks_processed,
|
||||
prepare_phase_start,
|
||||
&naka_conf,
|
||||
);
|
||||
|
||||
signer_vote_if_needed(
|
||||
&btc_regtest_controller,
|
||||
&naka_conf,
|
||||
&[sender_signer_sk],
|
||||
&signers,
|
||||
);
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk);
|
||||
|
||||
run_until_burnchain_height(
|
||||
&mut btc_regtest_controller,
|
||||
@@ -1490,7 +1269,6 @@ fn correct_burn_outs() {
|
||||
);
|
||||
|
||||
info!("Bootstrapped to Epoch-3.0 boundary, Epoch2x miner should stop");
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk, proposals_submitted);
|
||||
|
||||
// we should already be able to query the stacker set via RPC
|
||||
let burnchain = naka_conf.get_burnchain();
|
||||
@@ -1551,13 +1329,6 @@ fn correct_burn_outs() {
|
||||
tip_sn.block_height > prior_tip,
|
||||
"The new burnchain tip must have been processed"
|
||||
);
|
||||
|
||||
signer_vote_if_needed(
|
||||
&btc_regtest_controller,
|
||||
&naka_conf,
|
||||
&[sender_signer_sk],
|
||||
&signers,
|
||||
);
|
||||
}
|
||||
|
||||
coord_channel
|
||||
@@ -1637,7 +1408,6 @@ fn block_proposal_api_endpoint() {
|
||||
blocks_processed,
|
||||
naka_submitted_vrfs: vrfs_submitted,
|
||||
naka_submitted_commits: commits_submitted,
|
||||
naka_proposed_blocks: proposals_submitted,
|
||||
..
|
||||
} = run_loop.counters();
|
||||
|
||||
@@ -1655,7 +1425,7 @@ fn block_proposal_api_endpoint() {
|
||||
);
|
||||
|
||||
info!("Bootstrapped to Epoch-3.0 boundary, starting nakamoto miner");
|
||||
blind_signer(&conf, &signers, &sender_signer_sk, proposals_submitted);
|
||||
blind_signer(&conf, &signers, &sender_signer_sk);
|
||||
|
||||
let burnchain = conf.get_burnchain();
|
||||
let sortdb = burnchain.open_sortition_db(true).unwrap();
|
||||
@@ -1988,7 +1758,6 @@ fn miner_writes_proposed_block_to_stackerdb() {
|
||||
blocks_processed,
|
||||
naka_submitted_vrfs: vrfs_submitted,
|
||||
naka_submitted_commits: commits_submitted,
|
||||
naka_proposed_blocks: proposals_submitted,
|
||||
..
|
||||
} = run_loop.counters();
|
||||
|
||||
@@ -2006,7 +1775,7 @@ fn miner_writes_proposed_block_to_stackerdb() {
|
||||
);
|
||||
|
||||
info!("Nakamoto miner started...");
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk, proposals_submitted);
|
||||
blind_signer(&naka_conf, &signers, &sender_signer_sk);
|
||||
// first block wakes up the run loop, wait until a key registration has been submitted.
|
||||
next_block_and(&mut btc_regtest_controller, 60, || {
|
||||
let vrf_count = vrfs_submitted.load(Ordering::SeqCst);
|
||||
|
||||
@@ -61,6 +61,8 @@ use stacks_common::types::chainstate::{
|
||||
use stacks_common::util::hash::{bytes_to_hex, hex_bytes, to_hex, Hash160};
|
||||
use stacks_common::util::secp256k1::{Secp256k1PrivateKey, Secp256k1PublicKey};
|
||||
use stacks_common::util::{get_epoch_time_ms, get_epoch_time_secs, sleep_ms};
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, FeeEstimatorName, InitialBalance};
|
||||
use stacks_node::utils::{get_account, submit_tx};
|
||||
|
||||
use super::bitcoin_regtest::BitcoinCoreController;
|
||||
use super::{
|
||||
@@ -69,7 +71,6 @@ use super::{
|
||||
SK_2, SK_3,
|
||||
};
|
||||
use crate::burnchains::bitcoin_regtest_controller::{self, BitcoinRPCRequest, UTXO};
|
||||
use crate::config::{EventKeyType, EventObserverConfig, FeeEstimatorName, InitialBalance};
|
||||
use crate::neon_node::RelayerThread;
|
||||
use crate::operations::BurnchainOpSigner;
|
||||
use crate::stacks_common::types::PrivateKey;
|
||||
@@ -697,32 +698,6 @@ pub fn wait_for_microblocks(microblocks_processed: &Arc<AtomicU64>, timeout: u64
|
||||
return true;
|
||||
}
|
||||
|
||||
/// returns Txid string
|
||||
pub fn submit_tx(http_origin: &str, tx: &Vec<u8>) -> String {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/transactions", http_origin);
|
||||
let res = client
|
||||
.post(&path)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.body(tx.clone())
|
||||
.send()
|
||||
.unwrap();
|
||||
if res.status().is_success() {
|
||||
let res: String = res.json().unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
StacksTransaction::consensus_deserialize(&mut &tx[..])
|
||||
.unwrap()
|
||||
.txid()
|
||||
.to_string()
|
||||
);
|
||||
return res;
|
||||
} else {
|
||||
eprintln!("Submit tx error: {}", res.text().unwrap());
|
||||
panic!("");
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_unconfirmed_tx(http_origin: &str, txid: &Txid) -> Option<String> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/transactions/unconfirmed/{}", http_origin, txid);
|
||||
@@ -1196,30 +1171,6 @@ pub fn get_balance<F: std::fmt::Display>(http_origin: &str, account: &F) -> u128
|
||||
get_account(http_origin, account).balance
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Account {
|
||||
pub balance: u128,
|
||||
pub locked: u128,
|
||||
pub nonce: u64,
|
||||
}
|
||||
|
||||
pub fn get_account<F: std::fmt::Display>(http_origin: &str, account: &F) -> Account {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/accounts/{}?proof=0", http_origin, account);
|
||||
let res = client
|
||||
.get(&path)
|
||||
.send()
|
||||
.unwrap()
|
||||
.json::<AccountEntryResponse>()
|
||||
.unwrap();
|
||||
info!("Account response: {:#?}", res);
|
||||
Account {
|
||||
balance: u128::from_str_radix(&res.balance[2..], 16).unwrap(),
|
||||
locked: u128::from_str_radix(&res.locked[2..], 16).unwrap(),
|
||||
nonce: res.nonce,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_pox_info(http_origin: &str) -> Option<RPCPoxInfoData> {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/pox", http_origin);
|
||||
|
||||
@@ -27,6 +27,9 @@ use stacks_common::types::chainstate::{ConsensusHash, StacksBlockId, TrieHash};
|
||||
use stacks_common::types::StacksEpochId;
|
||||
use stacks_common::util::hash::{MerkleTree, Sha512Trunc256Sum};
|
||||
use stacks_common::util::secp256k1::MessageSignature;
|
||||
use stacks_node::config::{
|
||||
Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance,
|
||||
};
|
||||
use stacks_signer::client::{StackerDB, StacksClient};
|
||||
use stacks_signer::config::{build_signer_config_tomls, GlobalConfig as SignerConfig, Network};
|
||||
use stacks_signer::runloop::RunLoopCommand;
|
||||
@@ -40,7 +43,6 @@ use wsts::curve::scalar::Scalar;
|
||||
use wsts::state_machine::OperationResult;
|
||||
use wsts::taproot::SchnorrProof;
|
||||
|
||||
use crate::config::{Config as NeonConfig, EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::event_dispatcher::MinedNakamotoBlockEvent;
|
||||
use crate::neon::Counters;
|
||||
use crate::run_loop::boot_nakamoto;
|
||||
@@ -91,7 +93,7 @@ impl SignerTest {
|
||||
.map(|_| StacksPrivateKey::new())
|
||||
.collect::<Vec<StacksPrivateKey>>();
|
||||
|
||||
let (mut naka_conf, _miner_account) = naka_neon_integration_conf(None);
|
||||
let (naka_conf, _miner_account) = naka_neon_integration_conf(None);
|
||||
|
||||
// Setup the signer and coordinator configurations
|
||||
let signer_configs = build_signer_config_tomls(
|
||||
|
||||
@@ -21,13 +21,14 @@ use stacks::chainstate::stacks::StacksPrivateKey;
|
||||
use stacks::libstackerdb::{StackerDBChunkAckData, StackerDBChunkData};
|
||||
use stacks_common::types::chainstate::StacksAddress;
|
||||
use stacks_common::util::hash::Sha512Trunc256Sum;
|
||||
use stacks_node::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use stacks_node::utils::submit_tx;
|
||||
use {reqwest, serde_json};
|
||||
|
||||
use super::bitcoin_regtest::BitcoinCoreController;
|
||||
use crate::burnchains::BurnchainController;
|
||||
use crate::config::{EventKeyType, EventObserverConfig, InitialBalance};
|
||||
use crate::tests::neon_integrations::{
|
||||
neon_integration_test_conf, next_block_and_wait, submit_tx, test_observer, wait_for_runloop,
|
||||
neon_integration_test_conf, next_block_and_wait, test_observer, wait_for_runloop,
|
||||
};
|
||||
use crate::tests::{make_contract_publish, to_addr};
|
||||
use crate::{neon, BitcoinRegtestController};
|
||||
|
||||
195
testnet/stacks-node/src/utils.rs
Normal file
195
testnet/stacks-node/src/utils.rs
Normal file
@@ -0,0 +1,195 @@
|
||||
use clarity::vm::{ClarityName, ContractName, Value};
|
||||
use stacks::address::{AddressHashMode, C32_ADDRESS_VERSION_TESTNET_SINGLESIG};
|
||||
use stacks::chainstate::stacks::{
|
||||
StacksTransaction, StacksTransactionSigner, TransactionAnchorMode, TransactionAuth,
|
||||
TransactionContractCall, TransactionPayload, TransactionPostConditionMode,
|
||||
TransactionSpendingCondition, TransactionVersion,
|
||||
};
|
||||
use stacks::codec::StacksMessageCodec;
|
||||
use stacks::core::CHAIN_ID_TESTNET;
|
||||
use stacks::net::api::getaccount::AccountEntryResponse;
|
||||
use stacks::types::chainstate::{StacksAddress, StacksPrivateKey, StacksPublicKey};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Account {
|
||||
pub balance: u128,
|
||||
pub locked: u128,
|
||||
pub nonce: u64,
|
||||
}
|
||||
|
||||
pub fn get_account<F: std::fmt::Display>(http_origin: &str, account: &F) -> Account {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/accounts/{}?proof=0", http_origin, account);
|
||||
let res = client
|
||||
.get(&path)
|
||||
.send()
|
||||
.unwrap()
|
||||
.json::<AccountEntryResponse>()
|
||||
.unwrap();
|
||||
info!("Account response: {:#?}", res);
|
||||
Account {
|
||||
balance: u128::from_str_radix(&res.balance[2..], 16).unwrap(),
|
||||
locked: u128::from_str_radix(&res.locked[2..], 16).unwrap(),
|
||||
nonce: res.nonce,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_addr(sk: &StacksPrivateKey) -> StacksAddress {
|
||||
StacksAddress::from_public_keys(
|
||||
C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
|
||||
&AddressHashMode::SerializeP2PKH,
|
||||
1,
|
||||
&vec![StacksPublicKey::from_private(sk)],
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn serialize_sign_standard_single_sig_tx(
|
||||
payload: TransactionPayload,
|
||||
sender: &StacksPrivateKey,
|
||||
nonce: u64,
|
||||
tx_fee: u64,
|
||||
) -> Vec<u8> {
|
||||
serialize_sign_standard_single_sig_tx_anchor_mode(
|
||||
payload,
|
||||
sender,
|
||||
nonce,
|
||||
tx_fee,
|
||||
TransactionAnchorMode::OnChainOnly,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serialize_sign_standard_single_sig_tx_anchor_mode(
|
||||
payload: TransactionPayload,
|
||||
sender: &StacksPrivateKey,
|
||||
nonce: u64,
|
||||
tx_fee: u64,
|
||||
anchor_mode: TransactionAnchorMode,
|
||||
) -> Vec<u8> {
|
||||
serialize_sign_standard_single_sig_tx_anchor_mode_version(
|
||||
payload,
|
||||
sender,
|
||||
nonce,
|
||||
tx_fee,
|
||||
anchor_mode,
|
||||
TransactionVersion::Testnet,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serialize_sign_standard_single_sig_tx_anchor_mode_version(
|
||||
payload: TransactionPayload,
|
||||
sender: &StacksPrivateKey,
|
||||
nonce: u64,
|
||||
tx_fee: u64,
|
||||
anchor_mode: TransactionAnchorMode,
|
||||
version: TransactionVersion,
|
||||
) -> Vec<u8> {
|
||||
serialize_sign_tx_anchor_mode_version(
|
||||
payload,
|
||||
sender,
|
||||
None,
|
||||
nonce,
|
||||
None,
|
||||
tx_fee,
|
||||
anchor_mode,
|
||||
version,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn serialize_sign_tx_anchor_mode_version(
|
||||
payload: TransactionPayload,
|
||||
sender: &StacksPrivateKey,
|
||||
payer: Option<&StacksPrivateKey>,
|
||||
sender_nonce: u64,
|
||||
payer_nonce: Option<u64>,
|
||||
tx_fee: u64,
|
||||
anchor_mode: TransactionAnchorMode,
|
||||
version: TransactionVersion,
|
||||
) -> Vec<u8> {
|
||||
let mut sender_spending_condition =
|
||||
TransactionSpendingCondition::new_singlesig_p2pkh(StacksPublicKey::from_private(sender))
|
||||
.expect("Failed to create p2pkh spending condition from public key.");
|
||||
sender_spending_condition.set_nonce(sender_nonce);
|
||||
|
||||
let auth = match (payer, payer_nonce) {
|
||||
(Some(payer), Some(payer_nonce)) => {
|
||||
let mut payer_spending_condition = TransactionSpendingCondition::new_singlesig_p2pkh(
|
||||
StacksPublicKey::from_private(payer),
|
||||
)
|
||||
.expect("Failed to create p2pkh spending condition from public key.");
|
||||
payer_spending_condition.set_nonce(payer_nonce);
|
||||
payer_spending_condition.set_tx_fee(tx_fee);
|
||||
TransactionAuth::Sponsored(sender_spending_condition, payer_spending_condition)
|
||||
}
|
||||
_ => {
|
||||
sender_spending_condition.set_tx_fee(tx_fee);
|
||||
TransactionAuth::Standard(sender_spending_condition)
|
||||
}
|
||||
};
|
||||
let mut unsigned_tx = StacksTransaction::new(version, auth, payload);
|
||||
unsigned_tx.anchor_mode = anchor_mode;
|
||||
unsigned_tx.post_condition_mode = TransactionPostConditionMode::Allow;
|
||||
unsigned_tx.chain_id = CHAIN_ID_TESTNET;
|
||||
|
||||
let mut tx_signer = StacksTransactionSigner::new(&unsigned_tx);
|
||||
tx_signer.sign_origin(sender).unwrap();
|
||||
if let (Some(payer), Some(_)) = (payer, payer_nonce) {
|
||||
tx_signer.sign_sponsor(payer).unwrap();
|
||||
}
|
||||
|
||||
let mut buf = vec![];
|
||||
tx_signer
|
||||
.get_tx()
|
||||
.unwrap()
|
||||
.consensus_serialize(&mut buf)
|
||||
.unwrap();
|
||||
buf
|
||||
}
|
||||
|
||||
pub fn make_contract_call(
|
||||
sender: &StacksPrivateKey,
|
||||
nonce: u64,
|
||||
tx_fee: u64,
|
||||
contract_addr: &StacksAddress,
|
||||
contract_name: &str,
|
||||
function_name: &str,
|
||||
function_args: &[Value],
|
||||
) -> Vec<u8> {
|
||||
let contract_name = ContractName::from(contract_name);
|
||||
let function_name = ClarityName::from(function_name);
|
||||
|
||||
let payload = TransactionContractCall {
|
||||
address: contract_addr.clone(),
|
||||
contract_name,
|
||||
function_name,
|
||||
function_args: function_args.iter().map(|x| x.clone()).collect(),
|
||||
};
|
||||
|
||||
serialize_sign_standard_single_sig_tx(payload.into(), sender, nonce, tx_fee)
|
||||
}
|
||||
|
||||
/// returns Txid string
|
||||
pub fn submit_tx(http_origin: &str, tx: &Vec<u8>) -> String {
|
||||
let client = reqwest::blocking::Client::new();
|
||||
let path = format!("{}/v2/transactions", http_origin);
|
||||
let res = client
|
||||
.post(&path)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.body(tx.clone())
|
||||
.send()
|
||||
.unwrap();
|
||||
if res.status().is_success() {
|
||||
let res: String = res.json().unwrap();
|
||||
assert_eq!(
|
||||
res,
|
||||
StacksTransaction::consensus_deserialize(&mut &tx[..])
|
||||
.unwrap()
|
||||
.txid()
|
||||
.to_string()
|
||||
);
|
||||
return res;
|
||||
} else {
|
||||
eprintln!("Submit tx error: {}", res.text().unwrap());
|
||||
panic!("");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user