Skip to content

Commit

Permalink
Coinbase validation (#76)
Browse files Browse the repository at this point in the history
* rust fmt skip subsidy table

* Implement serialize payload + simplify deser

* Implement modify payload

* Implement coinbase transaction verification

* Store mergeset non-DAA

* todo

* Address review comments
  • Loading branch information
michaelsutton authored Nov 8, 2022
1 parent 9e1c2fd commit afe4a9f
Show file tree
Hide file tree
Showing 12 changed files with 351 additions and 531 deletions.
4 changes: 2 additions & 2 deletions consensus/core/src/tx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pub struct ScriptPublicKey {

impl ScriptPublicKey {
pub fn new(version: u16, script: ScriptVec) -> Self {
Self { script, version }
Self { version, script }
}

pub fn from_vec(version: u16, script: Vec<u8>) -> Self {
Expand Down Expand Up @@ -81,7 +81,7 @@ impl Display for TransactionOutpoint {
}

/// Represents a Kaspa transaction input
#[derive(Debug, Serialize, Deserialize, Clone)] // TODO: Implement a custom serializer for input that drops utxo_entry
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct TransactionInput {
pub previous_outpoint: TransactionOutpoint,
pub signature_script: Vec<u8>,
Expand Down
4 changes: 3 additions & 1 deletion consensus/src/consensus/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ impl Consensus {
reachability_store.clone(),
ghostdag_store.clone(),
headers_store.clone(),
daa_store,
daa_store.clone(),
statuses_store.clone(),
pruning_store.clone(),
depth_store,
Expand Down Expand Up @@ -270,6 +270,7 @@ impl Consensus {
statuses_store.clone(),
ghostdag_store.clone(),
headers_store.clone(),
daa_store,
block_transactions_store,
pruning_store.clone(),
past_pruning_points_store,
Expand All @@ -281,6 +282,7 @@ impl Consensus {
reachability_service.clone(),
dag_traversal_manager.clone(),
difficulty_manager.clone(),
coinbase_manager.clone(),
transaction_validator,
pruning_manager.clone(),
));
Expand Down
2 changes: 1 addition & 1 deletion consensus/src/consensus/test_consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl TestConsensus {
let (daa_score, _) = self
.consensus
.difficulty_manager
.calc_daa_score_and_added_blocks(&mut window.iter().map(|item| item.0.hash), &ghostdag_data);
.calc_daa_score_and_non_daa_mergeset_blocks(&mut window.iter().map(|item| item.0.hash), &ghostdag_data);
header.bits = self.consensus.difficulty_manager.calculate_difficulty_bits(&window);
header.daa_score = daa_score;
header.timestamp = self.consensus.past_median_time_manager.calc_past_median_time(ghostdag_data.clone()).0 + 1;
Expand Down
20 changes: 10 additions & 10 deletions consensus/src/model/stores/daa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,26 @@ use super::{
errors::StoreError,
DB,
};
use consensus_core::{blockhash::BlockHashes, BlockHasher};
use consensus_core::{BlockHashSet, BlockHasher};
use hashes::Hash;
use rocksdb::WriteBatch;

pub trait DaaStoreReader {
fn get_daa_added_blocks(&self, hash: Hash) -> Result<BlockHashes, StoreError>;
fn get_mergeset_non_daa(&self, hash: Hash) -> Result<Arc<BlockHashSet>, StoreError>;
}

pub trait DaaStore: DaaStoreReader {
// This is append only
fn insert(&self, hash: Hash, added_blocks: BlockHashes) -> Result<(), StoreError>;
fn insert(&self, hash: Hash, mergeset_non_daa: Arc<BlockHashSet>) -> Result<(), StoreError>;
}

const STORE_PREFIX: &[u8] = b"daa-added-blocks";
const STORE_PREFIX: &[u8] = b"mergeset_non_daa";

/// A DB + cache implementation of `DaaStore` trait, with concurrency support.
#[derive(Clone)]
pub struct DbDaaStore {
db: Arc<DB>,
access: CachedDbAccess<Hash, BlockHashes, BlockHasher>,
access: CachedDbAccess<Hash, Arc<BlockHashSet>, BlockHasher>,
}

impl DbDaaStore {
Expand All @@ -36,27 +36,27 @@ impl DbDaaStore {
Self::new(Arc::clone(&self.db), cache_size)
}

pub fn insert_batch(&self, batch: &mut WriteBatch, hash: Hash, added_blocks: BlockHashes) -> Result<(), StoreError> {
pub fn insert_batch(&self, batch: &mut WriteBatch, hash: Hash, mergeset_non_daa: Arc<BlockHashSet>) -> Result<(), StoreError> {
if self.access.has(hash)? {
return Err(StoreError::KeyAlreadyExists(hash.to_string()));
}
self.access.write(BatchDbWriter::new(batch), hash, added_blocks)?;
self.access.write(BatchDbWriter::new(batch), hash, mergeset_non_daa)?;
Ok(())
}
}

impl DaaStoreReader for DbDaaStore {
fn get_daa_added_blocks(&self, hash: Hash) -> Result<BlockHashes, StoreError> {
fn get_mergeset_non_daa(&self, hash: Hash) -> Result<Arc<BlockHashSet>, StoreError> {
self.access.read(hash)
}
}

impl DaaStore for DbDaaStore {
fn insert(&self, hash: Hash, added_blocks: BlockHashes) -> Result<(), StoreError> {
fn insert(&self, hash: Hash, mergeset_non_daa: Arc<BlockHashSet>) -> Result<(), StoreError> {
if self.access.has(hash)? {
return Err(StoreError::KeyAlreadyExists(hash.to_string()));
}
self.access.write(DirectDbWriter::new(&self.db), hash, added_blocks)?;
self.access.write(DirectDbWriter::new(&self.db), hash, mergeset_non_daa)?;
Ok(())
}
}
2 changes: 1 addition & 1 deletion consensus/src/model/stores/database/key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ impl DbKey {
}

pub fn prefix_only(prefix: &[u8]) -> Self {
Self::new(prefix, &[])
Self::new(prefix, [])
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl BlockBodyProcessor {
}

fn check_coinbase_blue_score_and_subsidy(self: &Arc<Self>, block: &Block) -> BlockProcessResult<()> {
match self.coinbase_manager.validate_coinbase_payload_in_isolation_and_extract_coinbase_data(&block.transactions[0]) {
match self.coinbase_manager.deserialize_coinbase_payload(&block.transactions[0].payload) {
Ok(data) => {
if data.blue_score != block.header.blue_score {
return Err(RuleError::BadCoinbasePayloadBlueScore(data.blue_score, block.header.blue_score));
Expand Down
8 changes: 4 additions & 4 deletions consensus/src/pipeline/header_processor/pre_pow_validation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,17 +58,17 @@ impl HeaderProcessor {
header: &Header,
) -> BlockProcessResult<()> {
let ghostdag_data = ctx.ghostdag_data.clone().unwrap();
let window = self.dag_traversal_manager.block_window(ghostdag_data, self.difficulty_window_size);
let window = self.dag_traversal_manager.block_window(ghostdag_data.clone(), self.difficulty_window_size);

let (daa_score, daa_added_blocks) = self
let (daa_score, mergeset_non_daa) = self
.difficulty_manager
.calc_daa_score_and_added_blocks(&mut window.iter().map(|item| item.0.hash), &ctx.ghostdag_data.clone().unwrap());
.calc_daa_score_and_non_daa_mergeset_blocks(&mut window.iter().map(|item| item.0.hash), &ghostdag_data);

if daa_score != header.daa_score {
return Err(RuleError::UnexpectedHeaderDaaScore(daa_score, header.daa_score));
}

ctx.daa_added_blocks = Some(daa_added_blocks);
ctx.mergeset_non_daa = Some(mergeset_non_daa);

let expected_bits = self.difficulty_manager.calculate_difficulty_bits(&window);
if header.bits != expected_bits {
Expand Down
9 changes: 5 additions & 4 deletions consensus/src/pipeline/header_processor/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ use crate::{
use consensus_core::{
blockhash::{BlockHashes, ORIGIN},
header::Header,
BlockHashSet,
};
use crossbeam_channel::{Receiver, Sender};
use hashes::Hash;
Expand All @@ -57,7 +58,7 @@ pub struct HeaderProcessingContext<'a> {
pub ghostdag_data: Option<Arc<GhostdagData>>,
pub block_window_for_difficulty: Option<BlockWindowHeap>,
pub block_window_for_past_median_time: Option<BlockWindowHeap>,
pub daa_added_blocks: Option<Vec<Hash>>,
pub mergeset_non_daa: Option<BlockHashSet>,
pub merge_depth_root: Option<Hash>,
pub finality_point: Option<Hash>,
pub block_level: Option<u8>,
Expand All @@ -75,7 +76,7 @@ impl<'a> HeaderProcessingContext<'a> {
ghostdag_data: None,
non_pruned_parents: None,
block_window_for_difficulty: None,
daa_added_blocks: None,
mergeset_non_daa: None,
block_window_for_past_median_time: None,
merge_depth_root: None,
finality_point: None,
Expand Down Expand Up @@ -319,7 +320,7 @@ impl HeaderProcessor {
self.ghostdag_store.insert_batch(&mut batch, ctx.hash, &ghostdag_data).unwrap();
self.block_window_cache_for_difficulty.insert(ctx.hash, Arc::new(ctx.block_window_for_difficulty.unwrap()));
self.block_window_cache_for_past_median_time.insert(ctx.hash, Arc::new(ctx.block_window_for_past_median_time.unwrap()));
self.daa_store.insert_batch(&mut batch, ctx.hash, Arc::new(ctx.daa_added_blocks.unwrap())).unwrap();
self.daa_store.insert_batch(&mut batch, ctx.hash, Arc::new(ctx.mergeset_non_daa.unwrap())).unwrap();
self.headers_store.insert_batch(&mut batch, ctx.hash, ctx.header.clone(), ctx.block_level.unwrap()).unwrap();
self.depth_store.insert_batch(&mut batch, ctx.hash, ctx.merge_depth_root.unwrap(), ctx.finality_point.unwrap()).unwrap();

Expand Down Expand Up @@ -395,7 +396,7 @@ impl HeaderProcessor {
ctx.ghostdag_data = Some(self.ghostdag_manager.genesis_ghostdag_data());
ctx.block_window_for_difficulty = Some(Default::default());
ctx.block_window_for_past_median_time = Some(Default::default());
ctx.daa_added_blocks = Some(Default::default());
ctx.mergeset_non_daa = Some(Default::default());
ctx.merge_depth_root = Some(ORIGIN);
ctx.finality_point = Some(ORIGIN);
ctx.block_level = Some(self.max_block_level);
Expand Down
16 changes: 12 additions & 4 deletions consensus/src/pipeline/virtual_processor/processor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
acceptance_data::{AcceptanceData, DbAcceptanceDataStore},
block_transactions::DbBlockTransactionsStore,
block_window_cache::BlockWindowCacheStore,
daa::DbDaaStore,
errors::StoreError,
ghostdag::{DbGhostdagStore, GhostdagStoreReader},
headers::{DbHeadersStore, HeaderStoreReader},
Expand All @@ -28,8 +29,8 @@ use crate::{
params::Params,
pipeline::{deps_manager::BlockTask, virtual_processor::utxo_validation::UtxoProcessingContext},
processes::{
difficulty::DifficultyManager, pruning::PruningManager, transaction_validator::TransactionValidator,
traversal_manager::DagTraversalManager,
coinbase::CoinbaseManager, difficulty::DifficultyManager, pruning::PruningManager,
transaction_validator::TransactionValidator, traversal_manager::DagTraversalManager,
},
};
use consensus_core::utxo::{utxo_diff::UtxoDiff, utxo_view::UtxoViewComposition};
Expand Down Expand Up @@ -65,6 +66,7 @@ pub struct VirtualStateProcessor {
pub(super) statuses_store: Arc<RwLock<DbStatusesStore>>,
pub(super) ghostdag_store: Arc<DbGhostdagStore>,
pub(super) headers_store: Arc<DbHeadersStore>,
pub(super) daa_store: Arc<DbDaaStore>,
pub(super) block_transactions_store: Arc<DbBlockTransactionsStore>,
pub(super) pruning_store: Arc<RwLock<DbPruningStore>>,
pub(super) past_pruning_points_store: Arc<DbPastPruningPointsStore>,
Expand All @@ -82,6 +84,7 @@ pub struct VirtualStateProcessor {
pub(super) reachability_service: MTReachabilityService<DbReachabilityStore>,
pub(super) dag_traversal_manager: DagTraversalManager<DbGhostdagStore, BlockWindowCacheStore>,
pub(super) difficulty_manager: DifficultyManager<DbHeadersStore>,
pub(super) coinbase_manager: CoinbaseManager,
pub(super) transaction_validator: TransactionValidator,
pub(super) pruning_manager: PruningManager<DbGhostdagStore, DbReachabilityStore, DbHeadersStore, DbPastPruningPointsStore>,
}
Expand All @@ -97,6 +100,7 @@ impl VirtualStateProcessor {
statuses_store: Arc<RwLock<DbStatusesStore>>,
ghostdag_store: Arc<DbGhostdagStore>,
headers_store: Arc<DbHeadersStore>,
daa_store: Arc<DbDaaStore>,
block_transactions_store: Arc<DbBlockTransactionsStore>,
pruning_store: Arc<RwLock<DbPruningStore>>,
past_pruning_points_store: Arc<DbPastPruningPointsStore>,
Expand All @@ -110,6 +114,7 @@ impl VirtualStateProcessor {
reachability_service: MTReachabilityService<DbReachabilityStore>,
dag_traversal_manager: DagTraversalManager<DbGhostdagStore, BlockWindowCacheStore>,
difficulty_manager: DifficultyManager<DbHeadersStore>,
coinbase_manager: CoinbaseManager,
transaction_validator: TransactionValidator,
pruning_manager: PruningManager<DbGhostdagStore, DbReachabilityStore, DbHeadersStore, DbPastPruningPointsStore>,
) -> Self {
Expand All @@ -127,6 +132,7 @@ impl VirtualStateProcessor {
statuses_store,
headers_store,
ghostdag_store,
daa_store,
block_transactions_store,
pruning_store,
past_pruning_points_store,
Expand All @@ -146,6 +152,7 @@ impl VirtualStateProcessor {
reachability_service,
dag_traversal_manager,
difficulty_manager,
coinbase_manager,
transaction_validator,
pruning_manager,
}
Expand Down Expand Up @@ -184,7 +191,6 @@ impl VirtualStateProcessor {

// TODO: check finality violation
// TODO: handle disqualified chain loop
// TODO: coinbase validation
// TODO: acceptance data format
// TODO: refactor this methods into multiple methods

Expand Down Expand Up @@ -262,13 +268,15 @@ impl VirtualStateProcessor {
let window = self.dag_traversal_manager.block_window(virtual_ghostdag_data.clone(), self.difficulty_window_size);
let (virtual_daa_score, _) = self
.difficulty_manager
.calc_daa_score_and_added_blocks(&mut window.iter().map(|item| item.0.hash), &virtual_ghostdag_data);
.calc_daa_score_and_non_daa_mergeset_blocks(&mut window.iter().map(|item| item.0.hash), &virtual_ghostdag_data);
self.calculate_utxo_state(&mut ctx, &selected_parent_utxo_view, virtual_daa_score);

// Update the accumulated diff
accumulated_diff.with_diff_in_place(&ctx.mergeset_diff).unwrap();

// Build the new virtual state
// TODO: store virtual mergeset fees and virtual mergeset non-DAA blocks in virtual state,
// so that virtual coinbase can be built (for build block template)
let new_virtual_state =
VirtualState::new(virtual_parents, virtual_ghostdag_data, virtual_daa_score, ctx.multiset_hash, ctx.mergeset_diff);

Expand Down
Loading

0 comments on commit afe4a9f

Please sign in to comment.