mirror of
https://github.com/alexgo-io/stacks-puppet-node.git
synced 2026-04-28 19:55:20 +08:00
fix: API sync; add determinstic tie-break for stacks chain tips; clean up compiler warnings
This commit is contained in:
@@ -1148,7 +1148,6 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
pub fn set_stacks_block_accepted(
|
||||
&mut self,
|
||||
consensus_hash: &ConsensusHash,
|
||||
parent_stacks_block_hash: &BlockHeaderHash,
|
||||
stacks_block_hash: &BlockHeaderHash,
|
||||
stacks_block_height: u64,
|
||||
) -> Result<(), db_error> {
|
||||
@@ -1163,7 +1162,6 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
self.set_stacks_block_accepted_at_tip(
|
||||
&chain_tip,
|
||||
consensus_hash,
|
||||
parent_stacks_block_hash,
|
||||
stacks_block_hash,
|
||||
stacks_block_height,
|
||||
)?;
|
||||
@@ -1386,7 +1384,6 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
&mut self,
|
||||
burn_tip: &BlockSnapshot,
|
||||
consensus_hash: &ConsensusHash,
|
||||
parent_stacks_block_hash: &BlockHeaderHash,
|
||||
stacks_block_hash: &BlockHeaderHash,
|
||||
stacks_block_height: u64,
|
||||
) -> Result<(), db_error> {
|
||||
@@ -4312,6 +4309,55 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
Ok(root_hash)
|
||||
}
|
||||
|
||||
/// Resolve ties between blocks at the same height.
|
||||
/// Returns the index into `tied`
|
||||
fn break_canonical_stacks_tip_tie(
|
||||
tip: &BlockSnapshot,
|
||||
best_height: u64,
|
||||
new_block_arrivals: &[(ConsensusHash, BlockHeaderHash, u64)],
|
||||
) -> Option<usize> {
|
||||
// if there's a tie, then randomly and deterministically pick one
|
||||
let mut tied = vec![];
|
||||
for (i, (consensus_hash, block_bhh, height)) in new_block_arrivals.iter().enumerate() {
|
||||
if best_height == *height {
|
||||
tied.push((StacksBlockId::new(consensus_hash, block_bhh), i));
|
||||
}
|
||||
}
|
||||
|
||||
if tied.len() == 0 {
|
||||
return None;
|
||||
}
|
||||
if tied.len() == 1 {
|
||||
return Some(tied[0].1);
|
||||
}
|
||||
|
||||
// break ties by hashing the index block hash with the snapshot's sortition hash, and
|
||||
// picking the lexicographically smallest one
|
||||
let mut hash_tied = vec![];
|
||||
let mut mapping = HashMap::new();
|
||||
for (block_id, arrival_idx) in tied.into_iter() {
|
||||
let mut buff = [0u8; 64];
|
||||
buff[0..32].copy_from_slice(&block_id.0);
|
||||
buff[32..64].copy_from_slice(&tip.sortition_hash.0);
|
||||
|
||||
let hashed = Sha512Trunc256Sum::from_data(&buff);
|
||||
hash_tied.push(hashed.clone());
|
||||
mapping.insert(hashed, arrival_idx);
|
||||
}
|
||||
|
||||
hash_tied.sort();
|
||||
let winner = hash_tied
|
||||
.first()
|
||||
.as_ref()
|
||||
.expect("FATAL: zero-length list of tied block IDs")
|
||||
.clone();
|
||||
let winner_index = *mapping
|
||||
.get(&winner)
|
||||
.expect("FATAL: winning block ID not mapped");
|
||||
|
||||
Some(winner_index)
|
||||
}
|
||||
|
||||
/// Find the new Stacks block arrivals as of the given tip `parent_tip`, and returns
|
||||
/// the highest Stacks chain tip and maximum arrival index.
|
||||
/// Used for both discovering the new arrivals and processing them with new snapshots.
|
||||
@@ -4389,12 +4435,12 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
let mut best_tip_height = parent_tip.canonical_stacks_tip_height;
|
||||
let mut ret = vec![];
|
||||
|
||||
for (consensus_hash, block_bhh, height) in new_block_arrivals.into_iter() {
|
||||
ret.push((block_bhh.clone(), height));
|
||||
for (consensus_hash, block_bhh, height) in new_block_arrivals.iter() {
|
||||
ret.push((block_bhh.clone(), *height));
|
||||
|
||||
// genesis blocks are incomparable -- it doesn't matter which one was "first."
|
||||
// everyone else must be higher than the highest known tip to supersede it.
|
||||
if height > best_tip_height || (height == 0 && best_tip_height == 0) {
|
||||
if *height > best_tip_height || (*height == 0 && best_tip_height == 0) {
|
||||
debug!(
|
||||
"At tip {}: {}/{} (height {}) is superceded by {}/{} (height {})",
|
||||
&parent_tip.burn_header_hash,
|
||||
@@ -4403,15 +4449,26 @@ impl<'a> SortitionHandleTx<'a> {
|
||||
best_tip_height,
|
||||
consensus_hash,
|
||||
block_bhh,
|
||||
height
|
||||
*height
|
||||
);
|
||||
|
||||
best_tip_block_bhh = block_bhh;
|
||||
best_tip_consensus_hash = consensus_hash;
|
||||
best_tip_height = height;
|
||||
best_tip_block_bhh = block_bhh.clone();
|
||||
best_tip_consensus_hash = consensus_hash.clone();
|
||||
best_tip_height = *height;
|
||||
}
|
||||
}
|
||||
|
||||
// if there's a tie, then randomly and deterministically pick one
|
||||
let winning_index_opt = SortitionHandleTx::break_canonical_stacks_tip_tie(
|
||||
parent_tip,
|
||||
best_tip_height,
|
||||
&new_block_arrivals,
|
||||
);
|
||||
if let Some(winning_index) = winning_index_opt {
|
||||
best_tip_consensus_hash = new_block_arrivals[winning_index].0;
|
||||
best_tip_block_bhh = new_block_arrivals[winning_index].1;
|
||||
}
|
||||
|
||||
debug!(
|
||||
"Max arrival for child of {} is {} (hash {} height {})",
|
||||
&best_tip_consensus_hash, &max_arrival_index, &best_tip_block_bhh, best_tip_height
|
||||
@@ -6856,11 +6913,6 @@ pub mod tests {
|
||||
// set some blocks as processed
|
||||
for i in 0..5 {
|
||||
let consensus_hash = ConsensusHash([(i + 1) as u8; 20]);
|
||||
let parent_stacks_block_hash = if i == 0 {
|
||||
FIRST_STACKS_BLOCK_HASH.clone()
|
||||
} else {
|
||||
BlockHeaderHash([(i - 1) as u8; 32])
|
||||
};
|
||||
|
||||
let stacks_block_hash = BlockHeaderHash([i as u8; 32]);
|
||||
let height = i;
|
||||
@@ -6872,13 +6924,8 @@ pub mod tests {
|
||||
"test: set_stacks_block_accepted {}/{} height {}",
|
||||
&consensus_hash, &stacks_block_hash, height
|
||||
);
|
||||
tx.set_stacks_block_accepted(
|
||||
&consensus_hash,
|
||||
&parent_stacks_block_hash,
|
||||
&stacks_block_hash,
|
||||
height,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(&consensus_hash, &stacks_block_hash, height)
|
||||
.unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
@@ -6951,20 +6998,14 @@ pub mod tests {
|
||||
// accept blocks 5 and 7 in one fork, and 6, 8, 9 in another.
|
||||
// Stacks fork 1,2,3,4,5,7 will be the longest fork.
|
||||
// Stacks fork 1,2,3,4 will overtake it when blocks 6,8,9 are processed.
|
||||
let mut parent_stacks_block_hash = BlockHeaderHash([0x04; 32]);
|
||||
for (i, height) in [7].iter().zip([6].iter()) {
|
||||
let consensus_hash = ConsensusHash([((i + 1) | 0x80) as u8; 20]);
|
||||
let stacks_block_hash = BlockHeaderHash([(i | 0x80) as u8; 32]);
|
||||
|
||||
{
|
||||
let mut tx = db.tx_begin_at_tip();
|
||||
tx.set_stacks_block_accepted(
|
||||
&consensus_hash,
|
||||
&parent_stacks_block_hash,
|
||||
&stacks_block_hash,
|
||||
*height,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(&consensus_hash, &stacks_block_hash, *height)
|
||||
.unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
@@ -6973,8 +7014,6 @@ pub mod tests {
|
||||
SortitionDB::get_canonical_stacks_chain_tip_hash(db.conn()).unwrap();
|
||||
assert_eq!(block_consensus_hash, consensus_hash);
|
||||
assert_eq!(block_bhh, stacks_block_hash);
|
||||
|
||||
parent_stacks_block_hash = stacks_block_hash;
|
||||
}
|
||||
|
||||
// chain tip is _still_ memoized to the last materialized chain tip (i.e. stacks block 7)
|
||||
@@ -6997,20 +7036,14 @@ pub mod tests {
|
||||
// block 7. The two stacks forks will be:
|
||||
// * 1,2,3,4,5,7
|
||||
// * 1,2,3,4,6,8
|
||||
parent_stacks_block_hash = BlockHeaderHash([4u8; 32]);
|
||||
for (i, height) in [6, 8].iter().zip([5, 6].iter()) {
|
||||
let consensus_hash = ConsensusHash([((i + 1) | 0x80) as u8; 20]);
|
||||
let stacks_block_hash = BlockHeaderHash([(i | 0x80) as u8; 32]);
|
||||
|
||||
{
|
||||
let mut tx = db.tx_begin_at_tip();
|
||||
tx.set_stacks_block_accepted(
|
||||
&consensus_hash,
|
||||
&parent_stacks_block_hash,
|
||||
&stacks_block_hash,
|
||||
*height,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(&consensus_hash, &stacks_block_hash, *height)
|
||||
.unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
@@ -7022,8 +7055,6 @@ pub mod tests {
|
||||
last_snapshot.canonical_stacks_tip_consensus_hash
|
||||
);
|
||||
assert_eq!(block_bhh, last_snapshot.canonical_stacks_tip_hash);
|
||||
|
||||
parent_stacks_block_hash = stacks_block_hash;
|
||||
}
|
||||
|
||||
// when the block for burn block 9 arrives, the canonical stacks fork will be
|
||||
@@ -7034,13 +7065,8 @@ pub mod tests {
|
||||
|
||||
{
|
||||
let mut tx = db.tx_begin_at_tip();
|
||||
tx.set_stacks_block_accepted(
|
||||
&consensus_hash,
|
||||
&parent_stacks_block_hash,
|
||||
&stacks_block_hash,
|
||||
*height,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(&consensus_hash, &stacks_block_hash, *height)
|
||||
.unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
@@ -7102,7 +7128,6 @@ pub mod tests {
|
||||
let mut tx = db.tx_begin_at_tip();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x4c; 20]),
|
||||
&BlockHeaderHash([0x04; 32]),
|
||||
&BlockHeaderHash([0x4b; 32]),
|
||||
5,
|
||||
)
|
||||
@@ -7179,14 +7204,12 @@ pub mod tests {
|
||||
let mut tx = db.tx_handle_begin(&SortitionId([0x2a; 32])).unwrap();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x2a; 20]),
|
||||
&BlockHeaderHash([0x04; 32]),
|
||||
&BlockHeaderHash([0x29; 32]),
|
||||
5,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x2b; 20]),
|
||||
&BlockHeaderHash([0x29; 32]),
|
||||
&BlockHeaderHash([0x2a; 32]),
|
||||
6,
|
||||
)
|
||||
@@ -7246,28 +7269,24 @@ pub mod tests {
|
||||
let mut tx = db.tx_begin_at_tip();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x46; 20]),
|
||||
&BlockHeaderHash([0x04; 32]),
|
||||
&BlockHeaderHash([0x45; 32]),
|
||||
5,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x47; 20]),
|
||||
&BlockHeaderHash([0x45; 32]),
|
||||
&BlockHeaderHash([0x46; 32]),
|
||||
6,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x48; 20]),
|
||||
&BlockHeaderHash([0x46; 32]),
|
||||
&BlockHeaderHash([0x47; 32]),
|
||||
7,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(
|
||||
&ConsensusHash([0x49; 20]),
|
||||
&BlockHeaderHash([0x47; 32]),
|
||||
&BlockHeaderHash([0x48; 32]),
|
||||
8,
|
||||
)
|
||||
@@ -7375,12 +7394,6 @@ pub mod tests {
|
||||
// set some blocks as processed
|
||||
for i in (0..5).rev() {
|
||||
let consensus_hash = ConsensusHash([(i + 1) as u8; 20]);
|
||||
let parent_stacks_block_hash = if i == 0 {
|
||||
FIRST_STACKS_BLOCK_HASH.clone()
|
||||
} else {
|
||||
BlockHeaderHash([(i - 1) as u8; 32])
|
||||
};
|
||||
|
||||
let stacks_block_hash = BlockHeaderHash([i as u8; 32]);
|
||||
let height = i;
|
||||
|
||||
@@ -7391,13 +7404,8 @@ pub mod tests {
|
||||
"test: set_stacks_block_accepted {}/{} height {}",
|
||||
&consensus_hash, &stacks_block_hash, height
|
||||
);
|
||||
tx.set_stacks_block_accepted(
|
||||
&consensus_hash,
|
||||
&parent_stacks_block_hash,
|
||||
&stacks_block_hash,
|
||||
height,
|
||||
)
|
||||
.unwrap();
|
||||
tx.set_stacks_block_accepted(&consensus_hash, &stacks_block_hash, height)
|
||||
.unwrap();
|
||||
tx.commit().unwrap();
|
||||
}
|
||||
|
||||
@@ -7424,6 +7432,56 @@ pub mod tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_stacks_tip_tiebreaker() {
|
||||
let snapshot = BlockSnapshot {
|
||||
accumulated_coinbase_ustx: 0,
|
||||
pox_valid: true,
|
||||
block_height: 123,
|
||||
burn_header_timestamp: get_epoch_time_secs(),
|
||||
burn_header_hash: BurnchainHeaderHash([0x01; 32]),
|
||||
sortition_id: SortitionId([0x02; 32]),
|
||||
parent_sortition_id: SortitionId([0x01; 32]),
|
||||
parent_burn_header_hash: BurnchainHeaderHash([0xff; 32]),
|
||||
consensus_hash: ConsensusHash([0x03; 20]),
|
||||
ops_hash: OpsHash([0x04; 32]),
|
||||
total_burn: 0,
|
||||
sortition: true,
|
||||
sortition_hash: SortitionHash([0x05; 32]),
|
||||
winning_block_txid: Txid([0x06; 32]),
|
||||
winning_stacks_block_hash: BlockHeaderHash([0x07; 32]),
|
||||
index_root: TrieHash([0x08; 32]),
|
||||
num_sortitions: 3,
|
||||
stacks_block_accepted: false,
|
||||
stacks_block_height: 0,
|
||||
arrival_index: 0,
|
||||
canonical_stacks_tip_height: 0,
|
||||
canonical_stacks_tip_hash: BlockHeaderHash([0x09; 32]),
|
||||
canonical_stacks_tip_consensus_hash: ConsensusHash([0x0a; 20]),
|
||||
};
|
||||
|
||||
let best_height = 123;
|
||||
let new_block_arrivals = vec![
|
||||
(ConsensusHash([0x01; 20]), BlockHeaderHash([0x02; 32]), 123),
|
||||
(ConsensusHash([0x03; 20]), BlockHeaderHash([0x04; 32]), 123),
|
||||
(ConsensusHash([0x07; 20]), BlockHeaderHash([0x08; 32]), 122),
|
||||
(ConsensusHash([0x05; 20]), BlockHeaderHash([0x06; 32]), 123),
|
||||
];
|
||||
|
||||
assert_eq!(
|
||||
SortitionHandleTx::break_canonical_stacks_tip_tie(&snapshot, 121, &new_block_arrivals),
|
||||
None
|
||||
);
|
||||
assert_eq!(
|
||||
SortitionHandleTx::break_canonical_stacks_tip_tie(&snapshot, 122, &new_block_arrivals),
|
||||
Some(2)
|
||||
);
|
||||
assert_eq!(
|
||||
SortitionHandleTx::break_canonical_stacks_tip_tie(&snapshot, 123, &new_block_arrivals),
|
||||
Some(0)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_epoch_switch() {
|
||||
let mut rng = rand::thread_rng();
|
||||
|
||||
@@ -2613,7 +2613,6 @@ impl StacksChainState {
|
||||
Some(ref mut sort_tx) => {
|
||||
sort_tx.set_stacks_block_accepted(
|
||||
consensus_hash,
|
||||
&block.parent_anchored_block_hash,
|
||||
&block.anchored_block_hash,
|
||||
block.height,
|
||||
)?;
|
||||
@@ -6132,7 +6131,6 @@ impl StacksChainState {
|
||||
/// Get the parent block of `staging_block`.
|
||||
pub fn get_stacks_block_parent(
|
||||
&self,
|
||||
sortdb: &SortitionDB,
|
||||
staging_block: &StagingBlock,
|
||||
) -> Result<Option<StagingBlock>, Error> {
|
||||
let sql = "SELECT * FROM staging_blocks WHERE processed = 1 AND orphaned = 0 AND consensus_hash = ?1 AND anchored_block_hash = ?2";
|
||||
|
||||
@@ -1394,7 +1394,7 @@ simulating a miner.
|
||||
let mut stacks_block = stacks_chain_tip.to_owned();
|
||||
loop {
|
||||
let stacks_parent_block = chain_state
|
||||
.get_stacks_block_parent(&sort_db, &stacks_block)
|
||||
.get_stacks_block_parent(&stacks_block)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
if stacks_parent_block.height < mine_tip_height {
|
||||
|
||||
@@ -1,16 +1,11 @@
|
||||
use chrono::{DateTime, Local, SecondsFormat, Utc};
|
||||
use chrono::{SecondsFormat, Utc};
|
||||
use lazy_static::lazy_static;
|
||||
use regex::Regex;
|
||||
use serde_json::{json, Value};
|
||||
use stacks_common::codec::StacksMessageCodec;
|
||||
use stacks_common::util::hash::hex_bytes;
|
||||
use std::fs::File;
|
||||
use std::io::prelude::*;
|
||||
use std::io::BufReader;
|
||||
use std::net::TcpListener;
|
||||
use std::net::TcpStream;
|
||||
use std::time::SystemTime;
|
||||
use std::{env, io};
|
||||
|
||||
const DEFAULT_ADDR: &str = "127.0.0.1:3700";
|
||||
|
||||
@@ -41,7 +36,7 @@ fn serve_for_events(addr: &String) {
|
||||
let listener = TcpListener::bind(addr).unwrap();
|
||||
eprintln!("Listening on {}", addr);
|
||||
for stream in listener.incoming() {
|
||||
let mut stream = stream.unwrap();
|
||||
let stream = stream.unwrap();
|
||||
handle_connection(stream);
|
||||
}
|
||||
}
|
||||
@@ -57,7 +52,7 @@ fn handle_connection(mut stream: TcpStream) {
|
||||
|
||||
let mut path = None;
|
||||
let mut content_length = None;
|
||||
let mut payload = None;
|
||||
let payload;
|
||||
|
||||
loop {
|
||||
buf.clear();
|
||||
|
||||
Reference in New Issue
Block a user