feat: mocknet puppet mode

This commit is contained in:
bestmike007
2022-07-27 13:26:09 +00:00
parent 08c163bab4
commit 96e0e6c235
7 changed files with 160 additions and 76 deletions

View File

@@ -1,5 +1,6 @@
Dockerfile*
target
!target/release/stacks-node
integration_tests/blockstack-consensus-data/
integration_tests/test-out/
api/data

2
.gitignore vendored
View File

@@ -40,6 +40,8 @@ secrets*
# vim
*.swp
# clion
.idea/
#Docker
Docker/blockstore.ini

5
Dockerfile.mocknet Normal file
View File

@@ -0,0 +1,5 @@
FROM debian:bullseye-slim
COPY target/release/stacks-node /bin/stacks-node
CMD ["stacks-node", "start"]

View File

@@ -146,10 +146,14 @@ pub struct ConversationHttp {
outbound_url: Option<UrlString>,
peer_addr: SocketAddr,
keep_alive: bool,
total_request_count: u64, // number of messages taken from the inbox
total_reply_count: u64, // number of messages responsed to
last_request_timestamp: u64, // absolute timestamp of the last time we received at least 1 byte in a request
last_response_timestamp: u64, // absolute timestamp of the last time we sent at least 1 byte in a response
total_request_count: u64,
// number of messages taken from the inbox
total_reply_count: u64,
// number of messages responsed to
last_request_timestamp: u64,
// absolute timestamp of the last time we received at least 1 byte in a request
last_response_timestamp: u64,
// absolute timestamp of the last time we sent at least 1 byte in a response
connection_time: u64, // when this converation was instantiated
canonical_stacks_tip_height: Option<u64>, // chain tip height of the peer's Stacks blockchain
@@ -210,7 +214,10 @@ impl RPCPeerInfoData {
genesis_chainstate_hash: &Sha256Sum,
) -> RPCPeerInfoData {
let server_version = version_string(
"stacks-node",
match option_env!("STACKS_NODE_PUPPET_MODE").unwrap_or("false") {
"true" => "stacks-node-puppetnet",
_ => "stacks-node",
},
option_env!("STACKS_NODE_VERSION")
.or(option_env!("CARGO_PKG_VERSION"))
.unwrap_or("0.0.0.0"),
@@ -368,8 +375,8 @@ impl RPCPoxInfoData {
let next_prepare_phase_in = i64::try_from(next_prepare_phase_start)
.map_err(|_| net_error::ChainstateError("Burn block height overflowed i64".into()))?
- i64::try_from(burnchain_tip.block_height).map_err(|_| {
net_error::ChainstateError("Burn block height overflowed i64".into())
})?;
net_error::ChainstateError("Burn block height overflowed i64".into())
})?;
let cur_cycle_stacked_ustx =
chainstate.get_total_ustx_stacked(&sortdb, tip, reward_cycle_id as u128)?;
@@ -451,7 +458,7 @@ impl RPCNeighborsInfo {
chain_view.burn_block_height,
false,
)
.map_err(net_error::DBError)?;
.map_err(net_error::DBError)?;
let sample: Vec<RPCNeighbor> = neighbor_sample
.into_iter()
@@ -1382,17 +1389,17 @@ impl ConversationHttp {
),
Ok(Some(Err(e))) => match e {
Unchecked(CheckErrors::CostBalanceExceeded(actual_cost, _))
if actual_cost.write_count > 0 =>
{
HttpResponseType::CallReadOnlyFunction(
response_metadata,
CallReadOnlyResponse {
okay: false,
result: None,
cause: Some("NotReadOnly".to_string()),
},
)
}
if actual_cost.write_count > 0 =>
{
HttpResponseType::CallReadOnlyFunction(
response_metadata,
CallReadOnlyResponse {
okay: false,
result: None,
cause: Some("NotReadOnly".to_string()),
},
)
}
_ => HttpResponseType::CallReadOnlyFunction(
response_metadata,
CallReadOnlyResponse {
@@ -1665,7 +1672,7 @@ impl ConversationHttp {
// present in the unconfirmed state?
if let Some(ref unconfirmed) = chainstate.unconfirmed_state.as_ref() {
if let Some((transaction, mblock_hash, seq)) =
unconfirmed.get_unconfirmed_transaction(txid)
unconfirmed.get_unconfirmed_transaction(txid)
{
let response = HttpResponseType::UnconfirmedTransaction(
response_metadata,
@@ -1828,13 +1835,13 @@ impl ConversationHttp {
HttpResponseMetadata::from_http_request_type(req, Some(canonical_stacks_tip_height));
let tip = SortitionDB::get_canonical_burn_chain_tip(sortdb.conn())?;
let stacks_epoch = SortitionDB::get_stacks_epoch(sortdb.conn(), tip.block_height)?
.ok_or_else(|| {
warn!(
.ok_or_else(|| {
warn!(
"Failed to get fee rate estimate because could not load Stacks epoch for canonical burn height = {}",
tip.block_height
);
net_error::ChainstateError("Could not load Stacks epoch for canonical burn height".into())
})?;
net_error::ChainstateError("Could not load Stacks epoch for canonical burn height".into())
})?;
if let Some((cost_estimator, fee_estimator, metric)) = handler_args.get_estimators_ref() {
let estimated_cost = match cost_estimator.estimate_cost(tx, &stacks_epoch.epoch_id) {
Ok(x) => x,
@@ -2628,14 +2635,14 @@ impl ConversationHttp {
network.burnchain_tip.canonical_stacks_tip_height,
)? {
if let Some((consensus_hash, block_hash)) =
ConversationHttp::handle_load_stacks_chain_tip_hashes(
&mut self.connection.protocol,
&mut reply,
&req,
tip,
chainstate,
network.burnchain_tip.canonical_stacks_tip_height,
)?
ConversationHttp::handle_load_stacks_chain_tip_hashes(
&mut self.connection.protocol,
&mut reply,
&req,
tip,
chainstate,
network.burnchain_tip.canonical_stacks_tip_height,
)?
{
let accepted = ConversationHttp::handle_post_microblock(
&mut self.connection.protocol,
@@ -3495,21 +3502,21 @@ mod test {
make_request: F,
check_result: C,
) -> ()
where
F: FnOnce(
&mut TestPeer,
&mut ConversationHttp,
&mut TestPeer,
&mut ConversationHttp,
) -> HttpRequestType,
C: FnOnce(
&HttpRequestType,
&HttpResponseType,
&mut TestPeer,
&mut TestPeer,
&ConversationHttp,
&ConversationHttp,
) -> bool,
where
F: FnOnce(
&mut TestPeer,
&mut ConversationHttp,
&mut TestPeer,
&mut ConversationHttp,
) -> HttpRequestType,
C: FnOnce(
&HttpRequestType,
&HttpResponseType,
&mut TestPeer,
&mut TestPeer,
&ConversationHttp,
&ConversationHttp,
) -> bool,
{
let mut peer_1_config = TestPeerConfig::new(test_name, peer_1_p2p, peer_1_http);
let mut peer_2_config = TestPeerConfig::new(test_name, peer_2_p2p, peer_2_http);
@@ -3518,13 +3525,13 @@ mod test {
let privk1 = StacksPrivateKey::from_hex(
"9f1f85a512a96a244e4c0d762788500687feb97481639572e3bffbd6860e6ab001",
)
.unwrap();
.unwrap();
// STVN97YYA10MY5F6KQJHKNYJNM24C4A1AT39WRW
let privk2 = StacksPrivateKey::from_hex(
"94c319327cc5cd04da7147d32d836eb2e4c44f4db39aa5ede7314a761183d0c701",
)
.unwrap();
.unwrap();
let microblock_privkey = StacksPrivateKey::new();
let microblock_pubkeyhash =
Hash160::from_node_public_key(&StacksPublicKey::from_private(&microblock_privkey));
@@ -3535,14 +3542,14 @@ mod test {
1,
&vec![StacksPublicKey::from_private(&privk1)],
)
.unwrap();
.unwrap();
let addr2 = StacksAddress::from_public_keys(
C32_ADDRESS_VERSION_TESTNET_SINGLESIG,
&AddressHashMode::SerializeP2PKH,
1,
&vec![StacksPublicKey::from_private(&privk2)],
)
.unwrap();
.unwrap();
peer_1_config.initial_balances = vec![
(addr1.to_account_principal(), 1000000000),
@@ -3623,7 +3630,7 @@ mod test {
&format!("hello-world-unconfirmed"),
&unconfirmed_contract.to_string(),
)
.unwrap(),
.unwrap(),
);
tx_unconfirmed_contract.chain_id = 0x80000000;
@@ -3661,15 +3668,15 @@ mod test {
&tip.sortition_id,
&block.block_hash(),
)
.unwrap()
.unwrap(); // succeeds because we don't fork
.unwrap()
.unwrap(); // succeeds because we don't fork
StacksChainState::get_anchored_block_header_info(
chainstate.db(),
&snapshot.consensus_hash,
&snapshot.winning_stacks_block_hash,
)
.unwrap()
.unwrap()
.unwrap()
.unwrap()
}
};
@@ -3679,7 +3686,7 @@ mod test {
tip.total_burn,
microblock_pubkeyhash,
)
.unwrap();
.unwrap();
let (anchored_block, anchored_block_size, anchored_block_cost) =
StacksBlockBuilder::make_anchored_block_from_txs(
block_builder,
@@ -3687,7 +3694,7 @@ mod test {
&sortdb.index_conn(),
vec![tx_coinbase_signed.clone(), tx_contract_signed.clone()],
)
.unwrap();
.unwrap();
anchor_size = anchored_block_size;
anchor_cost = anchored_block_cost;
@@ -3717,7 +3724,7 @@ mod test {
&sort_iconn,
BlockBuilderSettings::max_value(),
)
.unwrap();
.unwrap();
let microblock = microblock_builder
.mine_next_microblock_from_txs(
vec![
@@ -3783,7 +3790,7 @@ mod test {
1,
&vec![StacksPublicKey::from_private(&StacksPrivateKey::new())],
)
.unwrap();
.unwrap();
let mut tx = StacksTransaction {
version: TransactionVersion::Testnet,
chain_id: 0x80000000,
@@ -3824,7 +3831,7 @@ mod test {
sponsor_nonce,
None,
)
.unwrap();
.unwrap();
}
mempool_tx.commit().unwrap();
peer_2.mempool.replace(mempool);
@@ -4000,7 +4007,7 @@ mod test {
&mut peer_1,
&mut peer_2,
&convo_1,
&convo_2
&convo_2,
));
}
@@ -4107,7 +4114,7 @@ mod test {
&stacks_block_id,
&peer_client.config.burnchain,
)
.unwrap();
.unwrap();
*pox_server_info.borrow_mut() = Some(pox_info);
convo_client.new_getpoxinfo(TipRequest::UseLatestAnchoredTip)
},
@@ -4164,7 +4171,7 @@ mod test {
&stacks_block_id,
&peer_client.config.burnchain,
)
.unwrap();
.unwrap();
*pox_server_info.borrow_mut() = Some(pox_info);
convo_client.new_getpoxinfo(TipRequest::UseLatestUnconfirmedTip)
},
@@ -4469,7 +4476,7 @@ mod test {
let privk = StacksPrivateKey::from_hex(
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
)
.unwrap();
.unwrap();
let parent_block = make_codec_test_block(25);
let parent_consensus_hash = ConsensusHash([0x02; 20]);
@@ -4587,7 +4594,7 @@ mod test {
let privk = StacksPrivateKey::from_hex(
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
)
.unwrap();
.unwrap();
let parent_block = make_codec_test_block(25);
let parent_consensus_hash = ConsensusHash([0x02; 20]);
@@ -4701,7 +4708,7 @@ mod test {
let privk = StacksPrivateKey::from_hex(
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
)
.unwrap();
.unwrap();
let consensus_hash = ConsensusHash([0x02; 20]);
let anchored_block_hash = BlockHeaderHash([0x03; 32]);
@@ -4770,7 +4777,7 @@ mod test {
let privk = StacksPrivateKey::from_hex(
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
)
.unwrap();
.unwrap();
let sortdb = peer_server.sortdb.take().unwrap();
Relayer::setup_unconfirmed_state(peer_server.chainstate(), &sortdb).unwrap();
@@ -4812,13 +4819,13 @@ mod test {
unconfirmed_resp.status,
UnconfirmedTransactionStatus::Microblock {
block_hash: (*last_mblock.borrow()).clone(),
seq: 0
seq: 0,
}
);
let tx = StacksTransaction::consensus_deserialize(
&mut &hex_bytes(&unconfirmed_resp.tx).unwrap()[..],
)
.unwrap();
.unwrap();
assert_eq!(tx.txid(), *last_txid.borrow());
true
}
@@ -4976,7 +4983,7 @@ mod test {
let privk = StacksPrivateKey::from_hex(
"eb05c83546fdd2c79f10f5ad5434a90dd28f7e3acb7c092157aa1bc3656b012c01",
)
.unwrap();
.unwrap();
let consensus_hash = ConsensusHash([0x02; 20]);
let anchored_block_hash = BlockHeaderHash([0x03; 32]);
@@ -5603,7 +5610,7 @@ mod test {
TupleData::from_data(vec![("units".into(), Value::Int(123))])
.unwrap()
))
.unwrap()
.unwrap()
);
true
}
@@ -5672,7 +5679,7 @@ mod test {
TupleData::from_data(vec![("units".into(), Value::Int(1))])
.unwrap()
))
.unwrap()
.unwrap()
);
true
}
@@ -5731,7 +5738,7 @@ mod test {
TupleData::from_data(vec![("units".into(), Value::Int(1))])
.unwrap()
))
.unwrap()
.unwrap()
);
true
}

View File

@@ -1,6 +1,7 @@
[node]
name = "helium-node"
rpc_bind = "127.0.0.1:20443"
use_test_genesis_chainstate = true
## Settings for local testnet, relying on a local bitcoind server
## running with the following bitcoin.conf:

View File

@@ -1,5 +1,11 @@
use std::collections::VecDeque;
use std::io;
use std::io::Write;
use std::sync::{Mutex, Arc};
use std::time::Instant;
use async_std::stream::StreamExt;
use async_std::task::block_on;
use http_types::{Method, Response, StatusCode};
use stacks::burnchains::bitcoin::BitcoinBlock;
use stacks::burnchains::{
@@ -28,6 +34,8 @@ pub struct MocknetController {
db: Option<SortitionDB>,
chain_tip: Option<BurnchainTip>,
queued_operations: VecDeque<BlockstackOperationType>,
signaled: Arc<Mutex<bool>>,
control_server: Option<std::thread::JoinHandle<Result<(), io::Error>>>,
}
impl MocknetController {
@@ -45,6 +53,8 @@ impl MocknetController {
db: None,
queued_operations: VecDeque::new(),
chain_tip: None,
signaled: Arc::new(Mutex::new(false)),
control_server: None,
}
}
@@ -137,6 +147,45 @@ impl BurnchainController for MocknetController {
};
self.chain_tip = Some(genesis_state.clone());
let block_height = genesis_state.block_snapshot.block_height;
if option_env!("STACKS_NODE_PUPPET_MODE").unwrap_or("false") == "true" {
info!("ENV STACKS_NODE_PUPPET_MODE is set to true, starting burnchain signal server..");
let signaled = Arc::clone(&self.signaled);
self.control_server = Some(std::thread::spawn(move || block_on(async {
let listener = async_std::net::TcpListener::bind(
option_env!("STACKS_NODE_PUPPET_BIND").unwrap_or("0.0.0.0:20445")).await?;
let addr = format!("http://{}", listener.local_addr()?);
info!("burnchain signal server listening on {}", addr);
// For each incoming TCP connection, spawn a task and call `accept`.
let mut incoming = listener.incoming();
while let Some(stream) = incoming.next().await {
let stream = stream?;
async_h1::accept(stream.clone(), |req| async {
let req = req;
match (
req.method(),
req.url().path(),
) {
(Method::Get, "/ping") => Ok(Response::new(StatusCode::Ok)),
(Method::Post, "/kick") => {
let mut signaled = signaled.lock().unwrap();
*signaled = true;
io::stdout().flush().unwrap();
Ok(Response::new(StatusCode::Ok))
}
_ => {
let mut rs = Response::new(StatusCode::BadRequest);
rs.set_body(format!("[{}] {}", req.method(), req.url().path()));
Ok(rs)
}
}
}).await.unwrap_or(())
}
Ok(())
})));
}
Ok((genesis_state, block_height))
}
@@ -155,6 +204,14 @@ impl BurnchainController for MocknetController {
_ignored_target_height_opt: Option<u64>,
) -> Result<(BurnchainTip, u64), BurnchainControllerError> {
let chain_tip = self.get_chain_tip();
if chain_tip.block_snapshot.block_height > 3 && self.control_server.is_some() {
info!("Waiting a signal to proceed at burn block height {}", chain_tip.block_snapshot.block_height);
loop {
if *(self.signaled.lock().unwrap()) { break; }
std::thread::sleep(std::time::Duration::from_millis(1000));
}
info!("Signal received, mining new burn block...");
}
// Simulating mining
let next_block_header = Self::build_next_block_header(&chain_tip.block_snapshot);
@@ -166,7 +223,7 @@ impl BurnchainController for MocknetController {
Sha256Sum::from_data(
format!("{}::{}", next_block_header.block_height, vtxindex).as_bytes(),
)
.0,
.0,
);
let op = match payload {
BlockstackOperationType::LeaderKeyRegister(payload) => {
@@ -295,6 +352,10 @@ impl BurnchainController for MocknetController {
self.chain_tip = Some(new_state.clone());
let block_height = new_state.block_snapshot.block_height;
{
let mut signaled = self.signaled.lock().unwrap();
*signaled = false;
}
Ok((new_state, block_height))
}

View File

@@ -159,6 +159,10 @@ fn main() {
let num_round: u64 = 0; // Infinite number of rounds
if conf.burnchain.mode == "helium" || conf.burnchain.mode == "mocknet" {
let mut conf = conf;
if option_env!("STACKS_NODE_PUPPET_MODE").unwrap_or("false") == "true" {
conf.burnchain.commit_anchor_block_within = 0;
}
let mut run_loop = helium::RunLoop::new(conf);
if let Err(e) = run_loop.start(num_round) {
warn!("Helium runloop exited: {}", e);
@@ -178,7 +182,10 @@ fn main() {
fn version() -> String {
stacks::version_string(
"stacks-node",
match option_env!("STACKS_NODE_PUPPET_MODE").unwrap_or("false") {
"true" => "stacks-node-puppetnet",
_ => "stacks-node",
},
option_env!("STACKS_NODE_VERSION")
.or(option_env!("CARGO_PKG_VERSION"))
.unwrap_or("0.0.0.0"),