Skip to content

Commit

Permalink
fix: enforce unique commitments in utxo set (#3173)
Browse files Browse the repository at this point in the history
  • Loading branch information
stringhandler committed Aug 17, 2021
2 parents 90ba420 + 7c30d65 commit 23a7d64
Show file tree
Hide file tree
Showing 29 changed files with 434 additions and 219 deletions.
2 changes: 1 addition & 1 deletion applications/test_faucet/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
task::spawn(async move {
let result = task::spawn_blocking(move || {
let script = script!(Nop);
let (utxo, key, _) = helpers::create_utxo(value, &fc, Some(feature), &script);
let (utxo, key, _) = helpers::create_utxo(value, &fc, feature, &script);
print!(".");
(utxo, key, value)
})
Expand Down
8 changes: 7 additions & 1 deletion base_layer/core/src/chain_storage/blockchain_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use crate::{
},
transactions::{
transaction::{TransactionInput, TransactionKernel},
types::{HashOutput, Signature},
types::{Commitment, HashOutput, Signature},
},
};
use croaring::Bitmap;
Expand Down Expand Up @@ -105,6 +105,12 @@ pub trait BlockchainBackend: Send + Sync {
/// Fetch a specific output. Returns the output and the leaf index in the output MMR
fn fetch_output(&self, output_hash: &HashOutput) -> Result<Option<(PrunedOutput, u32, u64)>, ChainStorageError>;

/// Returns the unspent TransactionOutput output that matches the given commitment if it exists in the current UTXO
/// set, otherwise None is returned.
fn fetch_unspent_output_hash_by_commitment(
&self,
commitment: &Commitment,
) -> Result<Option<HashOutput>, ChainStorageError>;
/// Fetch all outputs in a block
fn fetch_outputs_in_block(&self, header_hash: &HashOutput) -> Result<Vec<PrunedOutput>, ChainStorageError>;

Expand Down
15 changes: 10 additions & 5 deletions base_layer/core/src/chain_storage/blockchain_database.rs
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,14 @@ where B: BlockchainBackend
Ok(db.fetch_output(&hash)?.map(|(out, _index, _)| out))
}

pub fn fetch_unspent_output_by_commitment(
&self,
commitment: &Commitment,
) -> Result<Option<HashOutput>, ChainStorageError> {
let db = self.db_read_access()?;
db.fetch_unspent_output_hash_by_commitment(commitment)
}

/// Return a list of matching utxos, with each being `None` if not found. If found, the transaction
/// output, and a boolean indicating if the UTXO was spent as of the block hash specified or the tip if not
/// specified.
Expand Down Expand Up @@ -1604,10 +1612,7 @@ fn reorganize_chain<T: BlockchainBackend>(
let block_hash_hex = block.accumulated_data().hash.to_hex();
txn.delete_orphan(block.accumulated_data().hash.clone());
let chain_metadata = backend.fetch_chain_metadata()?;
let deleted_bitmap = backend.fetch_deleted_bitmap()?;
if let Err(e) =
block_validator.validate_body_for_valid_orphan(&block, backend, &chain_metadata, &deleted_bitmap)
{
if let Err(e) = block_validator.validate_body_for_valid_orphan(&block, backend, &chain_metadata) {
warn!(
target: LOG_TARGET,
"Orphan block {} ({}) failed validation during chain reorg: {:?}",
Expand Down Expand Up @@ -2655,7 +2660,7 @@ mod test {
let prev_block = block_hashes
.get(&from)
.unwrap_or_else(|| panic!("Could not find block {}", from));
let mut block = create_block(1, prev_block.height() + 1, vec![]);
let (mut block, _) = create_block(1, prev_block.height() + 1, vec![]);
block.header.prev_hash = prev_block.hash().clone();

// Keep times constant in case we need a particular target difficulty
Expand Down
27 changes: 1 addition & 26 deletions base_layer/core/src/chain_storage/db_transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ use crate::{
blocks::{Block, BlockHeader},
chain_storage::{error::ChainStorageError, ChainBlock, ChainHeader, MmrTree},
transactions::{
transaction::{TransactionInput, TransactionKernel, TransactionOutput},
transaction::{TransactionKernel, TransactionOutput},
types::{Commitment, HashOutput},
},
};
Expand Down Expand Up @@ -145,15 +145,6 @@ impl DbTransaction {
self
}

pub fn insert_input(&mut self, input: TransactionInput, header_hash: HashOutput, mmr_leaf_index: u32) -> &mut Self {
self.operations.push(WriteOperation::InsertInput {
header_hash,
input: Box::new(input),
mmr_position: mmr_leaf_index,
});
self
}

pub fn update_pruned_hash_set(
&mut self,
mmr_tree: MmrTree,
Expand Down Expand Up @@ -283,11 +274,6 @@ pub enum WriteOperation {
InsertBlockBody {
block: Arc<ChainBlock>,
},
InsertInput {
header_hash: HashOutput,
input: Box<TransactionInput>,
mmr_position: u32,
},
InsertKernel {
header_hash: HashOutput,
kernel: Box<TransactionKernel>,
Expand Down Expand Up @@ -389,17 +375,6 @@ impl fmt::Display for WriteOperation {
header_height,
mmr_position
),
InsertInput {
header_hash,
input,
mmr_position,
} => write!(
f,
"Insert input {} in block: {} position: {}",
input.output_hash().to_hex(),
header_hash.to_hex(),
mmr_position
),
DeleteOrphanChainTip(hash) => write!(f, "DeleteOrphanChainTip({})", hash.to_hex()),
InsertOrphanChainTip(hash) => write!(f, "InsertOrphanChainTip({})", hash.to_hex()),
DeleteBlock(hash) => write!(f, "DeleteBlock({})", hash.to_hex()),
Expand Down
6 changes: 6 additions & 0 deletions base_layer/core/src/chain_storage/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,12 +107,18 @@ pub enum ChainStorageError {
IoError(#[from] std::io::Error),
#[error("Cannot calculate MMR roots for block that does not form a chain with the current tip. {0}")]
CannotCalculateNonTipMmr(String),
#[error("Key {key} in {table_name} already exists")]
KeyExists { table_name: &'static str, key: String },
}

impl ChainStorageError {
pub fn is_value_not_found(&self) -> bool {
matches!(self, ChainStorageError::ValueNotFound { .. })
}

pub fn is_key_exist_error(&self) -> bool {
matches!(self, ChainStorageError::KeyExists { .. })
}
}

impl From<task::JoinError> for ChainStorageError {
Expand Down
10 changes: 10 additions & 0 deletions base_layer/core/src/chain_storage/lmdb_db/lmdb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ where
val,
e,
);

if let lmdb_zero::Error::Code(code) = &e {
if *code == lmdb_zero::error::KEYEXIST {
return ChainStorageError::KeyExists {
table_name,
key: to_hex(key.as_lmdb_bytes()),
};
}
}

ChainStorageError::InsertError {
table: table_name,
error: e.to_string(),
Expand Down
Loading

0 comments on commit 23a7d64

Please sign in to comment.