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:
Brice Dobry
2024-02-29 00:06:09 -05:00
parent b815506502
commit b04386d4f5
37 changed files with 663 additions and 347 deletions

15
Cargo.lock generated
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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::*;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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!("");
}
}