Skip to content

Commit

Permalink
Show if a reorg has happened on /status (#518)
Browse files Browse the repository at this point in the history
  • Loading branch information
raphjaph authored Sep 27, 2022
1 parent 6d1f36f commit 6d2f245
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 25 deletions.
8 changes: 8 additions & 0 deletions src/index.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use {
bitcoincore_rpc::{Auth, Client, RpcApi},
rayon::iter::{IntoParallelRefIterator, ParallelIterator},
redb::WriteStrategy,
std::sync::atomic::{AtomicBool, Ordering},
};

mod rtx;
Expand All @@ -21,6 +22,7 @@ pub(crate) struct Index {
database: Database,
database_path: PathBuf,
height_limit: Option<u64>,
reorged: AtomicBool,
}

#[derive(Debug, PartialEq)]
Expand Down Expand Up @@ -109,6 +111,7 @@ impl Index {
database,
database_path,
height_limit: options.height_limit,
reorged: AtomicBool::new(false),
})
}

Expand Down Expand Up @@ -199,6 +202,10 @@ impl Index {
Ok(())
}

pub(crate) fn is_reorged(&self) -> bool {
self.reorged.load(Ordering::Relaxed)
}

pub(crate) fn index_block(&self, wtx: &mut WriteTransaction, height: u64) -> Result<bool> {
let mut height_to_hash = wtx.open_table(HEIGHT_TO_HASH)?;
let mut outpoint_to_ordinal_ranges = wtx.open_table(OUTPOINT_TO_ORDINAL_RANGES)?;
Expand Down Expand Up @@ -250,6 +257,7 @@ impl Index {
let prev_hash = height_to_hash.get(&prev_height)?.unwrap();

if prev_hash != block.header.prev_blockhash.as_ref() {
self.reorged.store(true, Ordering::Relaxed);
return Err(anyhow!("Reorg detected at or before {prev_height}"));
}
}
Expand Down
38 changes: 29 additions & 9 deletions src/subcommand/server.rs
Original file line number Diff line number Diff line change
Expand Up @@ -403,14 +403,18 @@ impl Server {
}
}

async fn status() -> impl IntoResponse {
(
StatusCode::OK,
StatusCode::OK
.canonical_reason()
.unwrap_or_default()
.to_string(),
)
async fn status(index: extract::Extension<Arc<Index>>) -> impl IntoResponse {
if index.is_reorged() {
(
StatusCode::OK,
"Reorg detected, please rebuild the database.",
)
} else {
(
StatusCode::OK,
StatusCode::OK.canonical_reason().unwrap_or_default(),
)
}
}

async fn search_by_query(
Expand Down Expand Up @@ -584,7 +588,9 @@ mod tests {
}

fn get(&self, url: &str) -> reqwest::blocking::Response {
self.index.index().unwrap();
if let Err(error) = self.index.index() {
log::error!("{error}");
}
reqwest::blocking::get(self.join_url(url)).unwrap()
}

Expand Down Expand Up @@ -1129,4 +1135,18 @@ mod tests {
),
);
}

#[test]
fn detect_reorg() {
let test_server = TestServer::new();

test_server.bitcoin_rpc_server.mine_blocks(1);

test_server.assert_response("/status", StatusCode::OK, "OK");

test_server.bitcoin_rpc_server.invalidate_tip();
test_server.bitcoin_rpc_server.mine_blocks(2);

test_server.assert_response_regex("/status", StatusCode::OK, "Reorg detected.*");
}
}
41 changes: 25 additions & 16 deletions src/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,13 @@ struct BitcoinRpcData {
blocks: BTreeMap<BlockHash, Block>,
transactions: BTreeMap<Txid, Transaction>,
mempool: Vec<Transaction>,
nonce: u32,
}

impl BitcoinRpcData {
fn new() -> Self {
let mut hashes = Vec::new();
let mut blocks = BTreeMap::new();

let genesis_block = bitcoin::blockdata::constants::genesis_block(Network::Bitcoin);
let genesis_block_hash = genesis_block.block_hash();
hashes.push(genesis_block_hash);
Expand All @@ -44,12 +44,22 @@ impl BitcoinRpcData {
blocks,
transactions: BTreeMap::new(),
mempool: Vec::new(),
nonce: 0,
}
}

fn push_block(&mut self, header: BlockHeader) -> Block {
fn push_block(&mut self) -> Block {
let nonce = self.nonce;
self.nonce += 1;
let mut block = Block {
header,
header: BlockHeader {
version: 0,
prev_blockhash: BlockHash::default(),
merkle_root: Default::default(),
time: 0,
bits: 0,
nonce,
},
txdata: vec![Transaction {
version: 0,
lock_time: 0,
Expand Down Expand Up @@ -81,6 +91,13 @@ impl BitcoinRpcData {
block
}

fn pop_block(&mut self) -> BlockHash {
let blockhash = self.hashes.pop().unwrap();
self.blocks.remove(&blockhash);

blockhash
}

fn broadcast_tx(&mut self, tx: Transaction) {
self.mempool.push(tx);
}
Expand Down Expand Up @@ -210,20 +227,8 @@ impl BitcoinRpcServerHandle {
}

pub(crate) fn mine_blocks(&self, num: u64) -> Vec<Block> {
let mut mined_blocks = Vec::new();
let mut bitcoin_rpc_data = self.data.lock().unwrap();
for _ in 0..num {
let block = bitcoin_rpc_data.push_block(BlockHeader {
version: 0,
prev_blockhash: BlockHash::default(),
merkle_root: Default::default(),
time: 0,
bits: 0,
nonce: 0,
});
mined_blocks.push(block);
}
mined_blocks
(0..num).map(|_| bitcoin_rpc_data.push_block()).collect()
}

pub(crate) fn broadcast_dummy_tx(&self) -> Txid {
Expand All @@ -238,6 +243,10 @@ impl BitcoinRpcServerHandle {

txid
}

pub(crate) fn invalidate_tip(&self) -> BlockHash {
self.data.lock().unwrap().pop_block()
}
}

impl Drop for BitcoinRpcServerHandle {
Expand Down

0 comments on commit 6d2f245

Please sign in to comment.