Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add AssumeValidArg enum and correct verify_script #131

Merged
merged 1 commit into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 45 additions & 22 deletions crates/floresta-chain/src/pruned_utreexo/chain_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -89,11 +89,17 @@ pub struct ChainStateInner<PersistedState: ChainStore> {
/// Assume valid is a Core-specific config that tells the node to not validate signatures
/// in blocks before this one. Note that we only skip signature validation, everything else
/// is still validated.
assume_valid: (BlockHash, u32),
assume_valid: Option<BlockHash>,
}
pub struct ChainState<PersistedState: ChainStore> {
inner: RwLock<ChainStateInner<PersistedState>>,
}
#[derive(Debug, Copy, Clone)]
pub enum AssumeValidArg {
Disabled,
Hardcoded,
UserInput(BlockHash),
}

impl<PersistedState: ChainStore> ChainState<PersistedState> {
fn maybe_reindex(&self, potential_tip: &DiskBlockHeader) {
Expand Down Expand Up @@ -468,7 +474,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
pub fn new(
chainstore: PersistedState,
network: Network,
assume_valid: Option<BlockHash>,
assume_valid: AssumeValidArg,
) -> ChainState<PersistedState> {
let genesis = genesis_block(network.into());
chainstore
Expand All @@ -481,7 +487,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
.update_block_index(0, genesis.block_hash())
.expect("Error updating index");

let assume_valid_hash = Self::get_assume_valid_value(network, assume_valid);
let assume_valid = Self::get_assume_valid_value(network, assume_valid);
ChainState {
inner: RwLock::new(ChainStateInner {
chainstore,
Expand All @@ -501,31 +507,35 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
consensus: Consensus {
parameters: network.into(),
},
assume_valid: (assume_valid_hash, 0),
assume_valid,
}),
}
}
fn get_assume_valid_value(network: Network, arg: Option<BlockHash>) -> BlockHash {
fn get_assume_valid_value(network: Network, arg: AssumeValidArg) -> Option<BlockHash> {
fn get_hash(hash: &str) -> BlockHash {
BlockHash::from_str(hash).expect("hardcoded hash should not fail")
}
if let Some(assume_valid_hash) = arg {
assume_valid_hash
} else {
match network {
match arg {
AssumeValidArg::Disabled => None,
AssumeValidArg::UserInput(hash) => Some(hash),
AssumeValidArg::Hardcoded => match network {
Network::Bitcoin => {
get_hash("00000000000000000009c97098b5295f7e5f183ac811fb5d1534040adb93cabd")
.into()
}
Network::Testnet => {
get_hash("0000000000000004877fa2d36316398528de4f347df2f8a96f76613a298ce060")
.into()
}
Network::Signet => {
get_hash("0000004f401bac79fe6cb3a10ef367b071e0fb51a1c9f4b3e8484e4dd03e1863")
.into()
}
Network::Regtest => {
get_hash("0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206")
.into()
}
}
},
}
}
fn get_disk_block_header(&self, hash: &BlockHash) -> Result<DiskBlockHeader, BlockchainError> {
Expand Down Expand Up @@ -578,7 +588,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
pub fn load_chain_state(
chainstore: KvChainStore,
network: Network,
assume_valid_hash: Option<BlockHash>,
assume_valid: AssumeValidArg,
) -> Result<ChainState<KvChainStore>, BlockchainError> {
let acc = Self::load_acc(&chainstore);

Expand All @@ -597,7 +607,7 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
consensus: Consensus {
parameters: network.into(),
},
assume_valid: (Self::get_assume_valid_value(network, assume_valid_hash), 0),
assume_valid: Self::get_assume_valid_value(network, assume_valid),
};
info!(
"Chainstate loaded at height: {}, checking if we have all blocks",
Expand Down Expand Up @@ -668,9 +678,6 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
// Updates our local view of the network
inner.acc = acc;
inner.best_block.valid_block(block.block_hash());
if block.block_hash() == inner.assume_valid.0 {
inner.assume_valid.1 = height;
}
Ok(())
}
fn update_tip(&self, best_block: BlockHash, height: u32) {
Expand All @@ -680,7 +687,16 @@ impl<PersistedState: ChainStore> ChainState<PersistedState> {
}
fn verify_script(&self, height: u32) -> bool {
let inner = self.inner.read();
inner.assume_valid.1 < height

inner.assume_valid.map_or(true, |hash| {
match inner.chainstore.get_header(&hash).unwrap() {
// If the assume-valid block is in the best chain, only verify scripts if we are higher
Some(DiskBlockHeader::HeadersOnly(_, assume_h))
| Some(DiskBlockHeader::FullyValid(_, assume_h)) => height > assume_h,
// Assume-valid is not in the best chain, so verify all the scripts
_ => true,
}
})
}
pub fn acc(&self) -> Stump {
read_lock!(self).acc.to_owned()
Expand Down Expand Up @@ -1135,9 +1151,6 @@ impl<PersistedState: ChainStore> UpdatableChainstate for ChainState<PersistedSta
))?;

inner.chainstore.update_block_index(height, block_hash)?;
if header.block_hash() == inner.assume_valid.0 {
inner.assume_valid.1 = height;
}
} else {
trace!("Header not in the best chain");
self.maybe_reorg(header)?;
Expand Down Expand Up @@ -1320,6 +1333,7 @@ mod test {
use super::UpdatableChainstate;
use crate::prelude::HashMap;
use crate::pruned_utreexo::consensus::Consensus;
use crate::AssumeValidArg;
use crate::KvChainStore;
use crate::Network;
#[test]
Expand All @@ -1331,7 +1345,11 @@ mod test {

let test_id = rand::random::<u64>();
let chainstore = KvChainStore::new(format!("./data/{test_id}/")).unwrap();
let chain = ChainState::<KvChainStore>::new(chainstore, Network::Bitcoin, None);
let chain = ChainState::<KvChainStore>::new(
chainstore,
Network::Bitcoin,
AssumeValidArg::Hardcoded,
);
while let Ok(header) = BlockHeader::consensus_decode(&mut cursor) {
chain.accept_header(header).unwrap();
}
Expand All @@ -1345,7 +1363,8 @@ mod test {

let test_id = rand::random::<u64>();
let chainstore = KvChainStore::new(format!("./data/{test_id}/")).unwrap();
let chain = ChainState::<KvChainStore>::new(chainstore, Network::Signet, None);
let chain =
ChainState::<KvChainStore>::new(chainstore, Network::Signet, AssumeValidArg::Hardcoded);
while let Ok(header) = BlockHeader::consensus_decode(&mut cursor) {
chain.accept_header(header).unwrap();
}
Expand All @@ -1370,7 +1389,11 @@ mod test {
fn test_reorg() {
let test_id = rand::random::<u64>();
let chainstore = KvChainStore::new(format!("./data/{test_id}/")).unwrap();
let chain = ChainState::<KvChainStore>::new(chainstore, Network::Regtest, None);
let chain = ChainState::<KvChainStore>::new(
chainstore,
Network::Regtest,
AssumeValidArg::Hardcoded,
);
let blocks = include_str!("./testdata/test_reorg.json");
let blocks: Vec<Vec<&str>> = serde_json::from_str(blocks).unwrap();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ pub struct ChainStateBuilder<PersistedState: ChainStore> {
chainstore: Option<PersistedState>,
ibd: bool,
chain_params: Option<ChainParams>,
assume_valid: Option<(BlockHash, u32)>,
assume_valid: Option<BlockHash>,
tip: Option<(BlockHash, u32)>,
first: Option<BlockHeader>,
}
Expand Down Expand Up @@ -73,7 +73,7 @@ impl<T: ChainStore> ChainStateBuilder<T> {
self.chain_params = Some(chain_params);
self
}
pub fn with_assume_valid(mut self, assume_valid: (BlockHash, u32)) -> Self {
pub fn with_assume_valid(mut self, assume_valid: BlockHash) -> Self {
self.assume_valid = Some(assume_valid);
self
}
Expand Down Expand Up @@ -102,7 +102,7 @@ impl<T: ChainStore> ChainStateBuilder<T> {
let block = self.tip.unwrap_or((BlockHash::all_zeros(), 0));
BestChain::from(block)
}
pub fn assume_valid(&self) -> (BlockHash, u32) {
self.assume_valid.unwrap_or((BlockHash::all_zeros(), 0))
pub fn assume_valid(&self) -> Option<BlockHash> {
self.assume_valid
}
}
2 changes: 1 addition & 1 deletion crates/floresta/examples/chainstate-builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ async fn main() {
// to validate the blockchain. If you set the chain height, you should update
// the accumulator to the state of the blockchain at that height too.
let _chain: ChainState<KvChainStore> = ChainStateBuilder::new()
.with_assume_valid((BlockHash::all_zeros(), 0))
.with_assume_valid(BlockHash::all_zeros())
.with_chain_params(ChainParams::from(Network::Bitcoin))
.with_tip(
(genesis_block(bitcoin::Network::Bitcoin).block_hash(), 0),
Expand Down
5 changes: 3 additions & 2 deletions crates/floresta/examples/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use floresta::chain::KvChainStore;
use floresta::chain::Network;
use floresta::wire::mempool::Mempool;
use floresta::wire::node::UtreexoNode;
use floresta_chain::AssumeValidArg;
use floresta_wire::node_interface::NodeMethods;
use floresta_wire::running_node::RunningNode;

Expand All @@ -35,13 +36,13 @@ async fn main() {
// and the headers chain. It will also validate new blocks and headers as we receive them.
// The last parameter is the assume valid block. We assume that all blocks before this
// one have valid signatures. This is a performance optimization, as we don't need to validate all
// signatures in the blockchain, just the ones after the assume valid block. We are givin a None
// signatures in the blockchain, just the ones after the assume valid block. We are giving a Disabled
// value, so we will validate all signatures regardless.
// We place the chain state in an Arc, so we can share it with other components.
let chain = Arc::new(ChainState::<KvChainStore>::new(
chain_store,
Network::Bitcoin,
None,
AssumeValidArg::Disabled,
));

// Create a new node. It will connect to the Bitcoin network and start downloading the blockchain.
Expand Down
8 changes: 6 additions & 2 deletions florestad/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ use cli::Commands;
use cli::FilterType;
use config_file::ConfigFile;
use floresta_chain::pruned_utreexo::BlockchainInterface;
use floresta_chain::AssumeValidArg;
use floresta_chain::BlockchainError;
use floresta_chain::ChainState;
use floresta_chain::KvChainStore;
Expand Down Expand Up @@ -417,13 +418,16 @@ fn load_chain_state(
assume_valid: Option<bitcoin::BlockHash>,
) -> ChainState<KvChainStore> {
let db = KvChainStore::new(data_dir.to_string()).expect("Could not read db");
match ChainState::<KvChainStore>::load_chain_state(db, network.into(), assume_valid) {
let assume_valid_arg =
assume_valid.map_or(AssumeValidArg::Hardcoded, AssumeValidArg::UserInput);

match ChainState::<KvChainStore>::load_chain_state(db, network.into(), assume_valid_arg) {
Ok(chainstate) => chainstate,
Err(err) => match err {
BlockchainError::ChainNotInitialized => {
let db = KvChainStore::new(data_dir.to_string()).expect("Could not read db");

ChainState::<KvChainStore>::new(db, network.into(), assume_valid)
ChainState::<KvChainStore>::new(db, network.into(), assume_valid_arg)
}
_ => unreachable!(),
},
Expand Down
Loading