Skip to content

Commit

Permalink
Merge branch 'main' into feat/compute_block_reward
Browse files Browse the repository at this point in the history
  • Loading branch information
MSghais committed Aug 7, 2024
2 parents dbf199b + b074c39 commit 4cc3a5d
Show file tree
Hide file tree
Showing 7 changed files with 266 additions and 37 deletions.
18 changes: 18 additions & 0 deletions .all-contributorsrc
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,24 @@
"contributions": [
"code"
]
},
{
"login": "harsh-ps-2003",
"name": "Harsh Pratap Singh",
"avatar_url": "https://avatars.githubusercontent.com/u/119954739?v=4",
"profile": "https://github.com/harsh-ps-2003",
"contributions": [
"code"
]
},
{
"login": "Xavek",
"name": "Xavek",
"avatar_url": "https://avatars.githubusercontent.com/u/61218841?v=4",
"profile": "https://github.com/Xavek",
"contributions": [
"code"
]
}
],
"contributorsPerLine": 7,
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,11 @@ Raito is a reference to Light Yagami (夜神月, Yagami Raito) from the manga/an

![Raito and Raito](./docs/img/memes/raito_shinigami_fusion.jpg)

# Contact

* [Raito Telegram](https://t.me/RaitoStarknet)
* [Raito OnlyDust](https://app.onlydust.com/p/raito---bitcoin-zk-client)

## Usage

This will compile all the components:
Expand Down Expand Up @@ -131,6 +136,10 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<td align="center" valign="top" width="14.28%"><a href="https://github.com/lomasson"><img src="https://avatars.githubusercontent.com/u/97454276?v=4?s=100" width="100px;" alt="lomasson"/><br /><sub><b>lomasson</b></sub></a><br /><a href="https://github.com/keep-starknet-strange/raito/commits?author=lomasson" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://nodeguardians.io/character/m-kus"><img src="https://avatars.githubusercontent.com/u/44951260?v=4?s=100" width="100px;" alt="Michael Zaikin"/><br /><sub><b>Michael Zaikin</b></sub></a><br /><a href="https://github.com/keep-starknet-strange/raito/commits?author=m-kus" title="Code">💻</a></td>
</tr>
<tr>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/harsh-ps-2003"><img src="https://avatars.githubusercontent.com/u/119954739?v=4?s=100" width="100px;" alt="Harsh Pratap Singh"/><br /><sub><b>Harsh Pratap Singh</b></sub></a><br /><a href="https://github.com/keep-starknet-strange/raito/commits?author=harsh-ps-2003" title="Code">💻</a></td>
<td align="center" valign="top" width="14.28%"><a href="https://github.com/Xavek"><img src="https://avatars.githubusercontent.com/u/61218841?v=4?s=100" width="100px;" alt="Xavek"/><br /><sub><b>Xavek</b></sub></a><br /><a href="https://github.com/keep-starknet-strange/raito/commits?author=Xavek" title="Code">💻</a></td>
</tr>
</tbody>
</table>

Expand Down
3 changes: 2 additions & 1 deletion src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod utils;
pub mod validation;
mod state;
mod validation;
mod main;
2 changes: 0 additions & 2 deletions src/state.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -75,8 +75,6 @@ pub struct Header {
pub version: u32,
/// The hash of the previous block in the blockchain.
pub prev_block_hash: u256,
/// The Merkle root hash of the transactions in the block.
pub merkle_root_hash: u256,
/// The timestamp of the block.
pub time: u32,
/// The difficulty target for mining the block.
Expand Down
36 changes: 36 additions & 0 deletions src/utils.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
use core::traits::Into;
use core::traits::TryInto;

// Bitwise shift left for u256
pub fn shl(value: u256, shift: u32) -> u256 {
value * fast_pow(2.into(), shift.into())
}

// Bitwise shift right for u256
pub fn shr(value: u256, shift: u32) -> u256 {
value / fast_pow(2.into(), shift.into())
}

// Fast exponentiation using the square-and-multiply algorithm
// Reference:
// https://github.com/keep-starknet-strange/alexandria/blob/bcdca70afdf59c9976148e95cebad5cf63d75a7f/packages/math/src/fast_power.cairo#L12
pub fn fast_pow(base: u256, exp: u32) -> u256 {
if exp == 0 {
return 1_u256;
}

let mut res: u256 = 1_u256;
let mut base: u256 = base;
let mut exp: u32 = exp;

loop {
if exp % 2 == 1 {
res = res * base;
}
exp = exp / 2;
if exp == 0 {
break res;
}
base = base * base;
}
}
172 changes: 138 additions & 34 deletions src/validation.cairo
Original file line number Diff line number Diff line change
@@ -1,26 +1,57 @@
use super::state::{Block, ChainState, UtreexoState};
use super::utils::{shl, shr};
use super::state::{Block, ChainState, Transaction, UtreexoState};

const MAX_TARGET: u256 = 0x00000000FFFF0000000000000000000000000000000000000000000000000000;

#[generate_trait]
impl BlockValidatorImpl of BlockValidator {
fn validate_and_apply(self: ChainState, block: Block) -> Result<ChainState, ByteArray> {
validate_prev_block_hash(@self, @block)?;
validate_proof_of_work(@self, @block)?;
validate_proof_of_work(@0_u256, @block)?;
validate_target(@self, @block)?;
validate_timestamp(@self, @block)?;

validate_merkle_root(@self, @block)?;
// validate_and_apply_transactions
let (total_fees, merkle_root) = fee_and_merkle_root(@self, @block)?;

validate_coinbase(@block, total_fees)?;

let best_block_hash = block_hash(@block, merkle_root)?;
let prev_timestamps = next_prev_timestamps(@self, @block);
let total_work = compute_total_work(@self, @block);
let (current_target, epoch_start_time) = adjust_difficulty(@self, @block);
let block_height = self.block_height + 1;

Result::Ok(
ChainState { total_work, current_target, epoch_start_time, prev_timestamps, ..self, }
ChainState {
block_height,
total_work,
best_block_hash,
current_target,
epoch_start_time,
prev_timestamps,
..self,
}
)
}
}

#[generate_trait]
impl TransactionValidatorImpl of TransactionValidator {
fn txid(self: @Transaction) -> u256 {
// TODO: implement
0
}
fn fee(self: @Transaction) -> u256 {
// TODO: implement
0
}
}

fn block_hash(block: @Block, merkle_root: u256) -> Result<u256, ByteArray> {
// TODO: implement
Result::Ok(0)
}

fn validate_prev_block_hash(self: @ChainState, block: @Block) -> Result<(), ByteArray> {
if self.best_block_hash == block.header.prev_block_hash {
Result::Ok(())
Expand All @@ -29,9 +60,14 @@ fn validate_prev_block_hash(self: @ChainState, block: @Block) -> Result<(), Byte
}
}

fn validate_proof_of_work(self: @ChainState, block: @Block) -> Result<(), ByteArray> {
// TODO: implement
Result::Ok(())
fn validate_proof_of_work(target: @u256, block: @Block) -> Result<(), ByteArray> {
if block.header.prev_block_hash <= target {
Result::Ok(())
} else {
Result::Err(
"Insufficient proof of work. Expected block hash {block.header.prev_block_hash} to be less than or equal to {target}."
)
}
}

fn validate_target(self: @ChainState, block: @Block) -> Result<(), ByteArray> {
Expand Down Expand Up @@ -83,11 +119,78 @@ fn compute_block_reward(self: @ChainState, block: @Block) -> Result<felt252, Byt
}
let num: u32 = (50_u32 * 10_u32) ** @8_u32;
Result::Ok((num / denominator).try_into().unwrap())

}

pub fn target_to_bits(target: u256) -> Result<u32, felt252> {
if target == 0 {
return Result::Err('Target is zero');
}

if target > MAX_TARGET {
return Result::Err('Exceeds max value');
}

// Find the most significant byte
let mut size: u32 = 32;
let mut compact = target;

// Count leading zero bytes by finding the first non-zero byte
while size > 1 && shr(compact, (size - 1) * 8) == 0 {
size -= 1;
};

// Extract mantissa (most significant 3 bytes)
let mut mantissa: u32 = shr(compact, (size - 3) * 8).try_into().unwrap();

// Normalize
if mantissa > 0x7fffff {
mantissa = (mantissa + 0x80) / 0x100;
size += 1;
}

// Ensure the mantissa is only 3 bytes
mantissa = mantissa & 0xffffff;

// Check size doesn't exceed maximum
if size > 34 {
return Result::Err('Overflow');
}

// Convert size to u256
let size_u256: u256 = size.into();

// Combine size and mantissa
let result: u32 = (shl(size_u256, 24) + mantissa.into()).try_into().unwrap();

Result::Ok(result)
}

fn fee_and_merkle_root(self: @ChainState, block: @Block) -> Result<(u256, u256), ByteArray> {
let mut txids = ArrayTrait::new();
let mut total_fee = 0;

for tx in *block.txs {
txids.append(tx.txid());
total_fee += tx.fee();
};

Result::Ok((total_fee, merkle_root(txids)))
}

fn merkle_root(txids: Array<u256>) -> u256 {
// TODO: implement
0
}

fn validate_coinbase(block: @Block, total_fees: u256) -> Result<(), ByteArray> {
//TODO implement
Result::Ok(())
}

#[cfg(test)]
mod tests {
use super::{validate_target, validate_timestamp, compute_block_reward};
use super::{validate_target, validate_timestamp, validate_proof_of_work};
use super::{Block, ChainState, UtreexoState};
use super::super::state::{Header, Transaction, TxIn, TxOut};

Expand All @@ -103,9 +206,7 @@ mod tests {
utreexo_state: UtreexoState { roots: array![].span() },
};
let mut block = Block {
header: Header {
version: 1, prev_block_hash: 1, merkle_root_hash: 1, time: 1, bits: 1, nonce: 1,
},
header: Header { version: 1, prev_block_hash: 1, time: 1, bits: 1, nonce: 1, },
txs: ArrayTrait::new().span(),
};

Expand Down Expand Up @@ -135,9 +236,7 @@ mod tests {
utreexo_state: UtreexoState { roots: array![].span() },
};
let mut block = Block {
header: Header {
version: 1, prev_block_hash: 1, merkle_root_hash: 1, time: 12, bits: 1, nonce: 1,
},
header: Header { version: 1, prev_block_hash: 1, time: 12, bits: 1, nonce: 1, },
txs: ArrayTrait::new().span(),
};

Expand All @@ -156,28 +255,33 @@ mod tests {
assert!(result.is_err(), "Median time is greater than block's timestamp");
}

// TODO finish test
#[test]
fn test_compute_block_reward() {
// TODO add correct state to compute block reward
let mut chain_state = ChainState {
block_height: 1,
total_work: 1,
best_block_hash: 1,
current_target: 1,
epoch_start_time: 1,
prev_timestamps: array![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].span(),
utreexo_state: UtreexoState { roots: array![].span() },
};
fn test_validate_proof_of_work() {
let mut block = Block {
header: Header {
version: 1, prev_block_hash: 1, merkle_root_hash: 1, time: 12, bits: 1, nonce: 1,
},
header: Header { version: 1, prev_block_hash: 1, time: 12, bits: 1, nonce: 1, },
txs: ArrayTrait::new().span(),
};
// let result = validate_timestamp(@chain_state, @block);
// TODO add correct state to compute block reward
let result_block = compute_block_reward(@chain_state, @block);
assert!(result_block.is_err(), "number_halvings equal 0");

// target is less than prev block hash
let result = validate_proof_of_work(@0_u256, @block);
assert!(result.is_err(), "Expect target less than prev block hash");

// target is greater than prev block hash
let result = validate_proof_of_work(@2_u256, @block);
assert!(result.is_ok(), "Expect target gt prev block hash");

// target is equal to prev block hash
let result = validate_proof_of_work(@1_u256, @block);
assert!(result.is_ok(), "Expect target equal to prev block hash");

// block prev block hash is greater than target
block.header.prev_block_hash = 2;
let result = validate_proof_of_work(@1_u256, @block);
assert!(result.is_err(), "Expect prev block hash gt target");

// block prev block hash is less than target
block.header.prev_block_hash = 9;
let result = validate_proof_of_work(@10_u256, @block);
assert!(result.is_ok(), "Expect prev block hash lt target");
}
}
63 changes: 63 additions & 0 deletions tests/tests.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
use raito::validation::target_to_bits;

#[test]
fn test_target_to_bits_large_target() {
let target: u256 = 0x1bc330000000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x181bc330, "Incorrect bits for large target");
}

#[test]
fn test_target_to_bits_small_target() {
let target: u256 = 0x92340000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x05009234, "Incorrect bits for small target");
}

#[test]
fn test_target_to_bits_medium_target() {
let target: u256 = 0x12345600;
let result = target_to_bits(target).unwrap();
assert!(result == 0x04123456, "Incorrect bits for medium target");
}

#[test]
fn test_target_to_bits_max_target() {
let max_target: u256 = 0x00000000ffff0000000000000000000000000000000000000000000000000000;
let result = target_to_bits(max_target).unwrap();
assert!(result == 0x1d00ffff, "Incorrect bits for max target");
}

#[test]
fn test_target_to_bits_high_precision_target() {
let target: u256 = 0x000000000d314200000000000000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1c0d3142, "Incorrect bits for high precision target");
}

#[test]
fn test_target_to_bits_low_precision_target() {
let target: u256 = 0x00000000000000000007a4290000000000000000000000000000000000000000;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1707a429, "Incorrect bits for low precision target");
}

#[test]
fn test_target_to_bits_full_mantissa() {
let target: u256 = 0xd86a528bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8bc8b;
let result = target_to_bits(target).unwrap();
assert!(result == 0x1d00d86a, "Incorrect bits for full mantissa target");
}

#[test]
fn test_target_to_bits_zero_target() {
let result = target_to_bits(0.into());
assert!(result.is_err(), "Should error on zero target");
}

#[test]
fn test_target_to_bits_overflow_target() {
let target: u256 = 0x01000000000000000000000000000000000000000000000000000000000000000;
let result = target_to_bits(target);
assert!(result.is_err(), "Should error on overflow target");
}

0 comments on commit 4cc3a5d

Please sign in to comment.