Skip to content

Commit

Permalink
Merge pull request #57 from manlikeHB/validate_coinbase
Browse files Browse the repository at this point in the history
 feat: Implement validate_coinbase function #14
  • Loading branch information
maciejka authored Aug 13, 2024
2 parents f1fb38e + 1575d6b commit 12469a1
Showing 1 changed file with 211 additions and 6 deletions.
217 changes: 211 additions & 6 deletions src/validation.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use core::traits::Into;
use core::traits::TryInto;

use super::merkle_tree::merkle_root;
use super::utils::{shl, shr, Hash};
use super::state::{Block, ChainState, Transaction, UtreexoState, UtreexoSet, TxIn, TxOut};
use super::state::{Block, ChainState, Transaction, UtreexoState, UtreexoSet, TxIn, TxOut, OutPoint};

const MAX_TARGET: u256 = 0x00000000FFFF0000000000000000000000000000000000000000000000000000;

Expand All @@ -11,7 +14,7 @@ impl BlockValidatorImpl of BlockValidator {

let (total_fees, merkle_root) = fee_and_merkle_root(@block)?;

validate_coinbase(@block, total_fees)?;
validate_coinbase(@block, total_fees, self.block_height)?;

let prev_timestamps = next_prev_timestamps(@self, @block);
let (current_target, epoch_start_time) = adjust_difficulty(@self, @block);
Expand Down Expand Up @@ -214,8 +217,38 @@ fn fee_and_merkle_root(block: @Block) -> Result<(u64, Hash), ByteArray> {
Result::Ok((total_fee, merkle_root(ref txids)))
}

fn validate_coinbase(block: @Block, total_fees: u64) -> Result<(), ByteArray> {
//TODO implement
fn validate_coinbase(block: @Block, total_fees: u64, block_height: u32) -> Result<(), ByteArray> {
let tx = block.txs[0];

// Validate the coinbase input
// Ensure there is exactly one coinbase input
assert((*tx.inputs).len() == 1, 'Input count should be 1');

// Ensure the input's vout is 0xFFFFFFFF
assert(*tx.inputs[0].previous_output.vout == 0xFFFFFFFF, 'vout should be 0xFFFFFFFF');

// Ensure the input's TXID is zero
assert(*tx.inputs[0].previous_output.txid == 0, 'txid should be 0');

// Validate the outputs' amounts
// Sum up the total amount of all outputs
// and also add the outputs to the UtreexoSet.
let mut total_output_amount = 0;

for txs in *block
.txs {
for output in *txs
.outputs {
total_output_amount = total_output_amount + *output.value;
//TODO add outputs to UtreexoSet

};
};

// Ensure the total output amount is at most the block reward + TX fees
let block_reward = compute_block_reward(block_height);
assert(total_output_amount <= total_fees + block_reward, 'total output > block rwd + fees');

Result::Ok(())
}

Expand All @@ -232,10 +265,9 @@ mod tests {
use super::{
validate_timestamp, validate_proof_of_work, compute_block_reward, compute_total_work,
compute_work_from_target, shr, shl, Block, ChainState, UtreexoState, next_prev_timestamps,
TransactionValidatorImpl
TransactionValidatorImpl, validate_coinbase
};


#[test]
fn test_validate_timestamp() {
let mut chain_state = ChainState {
Expand Down Expand Up @@ -432,4 +464,177 @@ mod tests {
assert(*next_prev_timestamps[8] == 8, 'Failed to compute');
assert(*next_prev_timestamps[9] == 9, 'Failed to compute');
}

#[test]
#[should_panic(expected: ('Input count should be 1',))]
fn test_validate_coinbase_with_multiple_input() {
let block = Block {
header: Header { version: 1_u32, time: 1231006505_u32, bits: 1, nonce: 2083236893_u32 },
txs: array![
Transaction {
version: 1,
is_segwit: false,
inputs: array![
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0_u256, vout: 0xffffffff_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
},
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0_u256, vout: 0xffffffff_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
}
]
.span(),
outputs: array![TxOut { value: 5000000000_u64, pk_script: from_base16(""), }]
.span(),
lock_time: 0
}
]
.span()
};
let total_fees = 5000000000_u64;
let block_height = 1;

validate_coinbase(@block, total_fees, block_height).unwrap();
}

#[test]
#[should_panic(expected: ('vout should be 0xFFFFFFFF',))]
fn test_validate_coinbase_with_wrong_vout() {
let block = Block {
header: Header { version: 1_u32, time: 1231006505_u32, bits: 1, nonce: 2083236893_u32 },
txs: array![
Transaction {
version: 1,
is_segwit: false,
inputs: array![
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0_u256, vout: 0x1_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
}
]
.span(),
outputs: array![TxOut { value: 5000000000_u64, pk_script: from_base16(""), }]
.span(),
lock_time: 0
}
]
.span()
};
let total_fees = 5000000000_u64;
let block_height = 1;

validate_coinbase(@block, total_fees, block_height).unwrap();
}

#[test]
#[should_panic(expected: ('txid should be 0',))]
fn test_validate_coinbase_with_txid_not_zero() {
let block = Block {
header: Header { version: 1_u32, time: 1231006505_u32, bits: 1, nonce: 2083236893_u32 },
txs: array![
Transaction {
version: 1,
is_segwit: false,
inputs: array![
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0x2_u256, vout: 0xFFFFFFFF_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
}
]
.span(),
outputs: array![TxOut { value: 5000000000_u64, pk_script: from_base16(""), }]
.span(),
lock_time: 0
}
]
.span()
};
let total_fees = 5000000000_u64;
let block_height = 1;

validate_coinbase(@block, total_fees.into(), block_height).unwrap();
}
#[test]
#[should_panic(expected: ('total output > block rwd + fees',))]
fn test_validate_coinbase_outputs_amount() {
let block = Block {
header: Header { version: 1_u32, time: 1231006505_u32, bits: 1, nonce: 2083236893_u32 },
txs: array![
Transaction {
version: 1,
is_segwit: false,
inputs: array![
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0_u256, vout: 0xffffffff_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
}
]
.span(),
outputs: array![TxOut { value: 5000000000_u64, pk_script: from_base16(""), }]
.span(),
lock_time: 0
}
]
.span()
};

let total_fees = 0_u64;
let block_height = 856_563;

validate_coinbase(@block, total_fees, block_height).unwrap();
}

#[test]
fn test_validate_coinbase() {
let block = Block {
header: Header { version: 1_u32, time: 1231006505_u32, bits: 1, nonce: 2083236893_u32 },
txs: array![
Transaction {
version: 1,
is_segwit: false,
inputs: array![
TxIn {
script: from_base16(""),
sequence: 4294967295,
previous_output: OutPoint {
txid: 0_u256, vout: 0xffffffff_u32, txo_index: 0, amount: 0_64
},
witness: from_base16("0")
}
]
.span(),
outputs: array![TxOut { value: 5000000000_u64, pk_script: from_base16(""), }]
.span(),
lock_time: 0
}
]
.span()
};

let total_fees = 5000000000_u64;
let block_height = 856_563;

validate_coinbase(@block, total_fees, block_height).unwrap();
}
}

0 comments on commit 12469a1

Please sign in to comment.