Skip to content

Commit

Permalink
track with main | use bits_to_target fn
Browse files Browse the repository at this point in the history
  • Loading branch information
oluwa-peski committed Aug 8, 2024
2 parents 15cfcdf + 9f1e27c commit bd26c80
Show file tree
Hide file tree
Showing 8 changed files with 414 additions and 26 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
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @b-j-roberts @m-kus @maciejka
* @m-kus @maciejka
44 changes: 39 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,36 @@ Although this is a highly experimental project without immediate plans for deplo

## Roadmap

* [ ] verify block header (block hash, previous block hash, Merkle root, proof-of-work, median time, and difficulty adjustment)
* [ ] verify transactions
* [ ] integrate with Shinigami and verify scripts
* [ ] verify previous chain proofs
* [ ] add utreexo accumulator to the chain state
### Milestone 1 - Block Verification

* header verification
* [ ] block hash
* [ ] previous block hash
* [ ] proof-of-work
* [ ] median time
* [ ] difficulty adjustment
* transaction verification
* [ ] tx hash
* [ ] tx merkle root
* [ ] verify transaction fee
* utreexo
* [ ] fetch utreexo from some kind of bridge node, tbd
* [ ] use utreexo to verify tx inputs
* verify scripts
* integration with Shinigami, tbd
* block verification
* [ ] verify coinbase tx
* integration testing
* [ ] test on individual historical blocks

### Milestone 2 - Real Data

* [ ] feed it with real data
* [ ] test that you can produce and verify proofs of individual blocks

### Milestone 3 - Recursive Verification

* verify chain proofs with cairo verifier, tbd

## Name reference

Expand All @@ -65,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 @@ -106,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
4 changes: 3 additions & 1 deletion src/lib.cairo
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
pub 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;
}
}
195 changes: 178 additions & 17 deletions src/validation.cairo
Original file line number Diff line number Diff line change
@@ -1,27 +1,57 @@
use super::state::{Block, ChainState, UtreexoState};
use super::state::{Block, ChainState, Transaction, UtreexoState};
use super::utils::{shl, shr};

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 (current_target, epoch_start_time) = adjust_difficulty(@self, @block);
let bits_to_target: u256 = block.header.bits.into(); //will replace with bits to target implementation
let total_work = compute_total_work(self.total_work, bits_to_target);
let total_work = compute_total_work(self.total_work, bits_to_target(block.header.bits).unwrap());
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 @@ -30,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 @@ -78,9 +113,109 @@ fn validate_merkle_root(self: @ChainState, block: @Block) -> Result<(), ByteArra
Result::Ok(())
}

// Helper functions
pub fn bits_to_target(bits: u32) -> Result<u256, felt252> {
// Extract exponent and mantissa
let exponent: u32 = (bits / 0x1000000);
let mantissa: u32 = bits & 0x00FFFFFF;

// Check if mantissa is valid (should be less than 0x1000000)
if mantissa > 0x7FFFFF && exponent != 0 {
return Result::Err('Invalid mantissa');
}

// Calculate the full target value
let mut target: u256 = mantissa.into();

if exponent == 0 {
// Special case: exponent 0 means we use the mantissa as-is
return Result::Ok(target);
} else if exponent <= 3 {
// For exponents 1, 2, and 3, divide by 256^(3 - exponent) i.e right shift
let shift = 8 * (3 - exponent);
target = shr(target, shift);
} else {
let shift = 8 * (exponent - 3);
target = shl(target, shift);
}

// Ensure the target doesn't exceed the maximum allowed value
if target > MAX_TARGET {
return Result::Err('Target exceeds maximum');
}

Result::Ok(target)
}

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_work_from_target, compute_total_work};
use super::{validate_target, validate_timestamp, validate_proof_of_work, compute_work_from_target, compute_total_work};
use super::{Block, ChainState, UtreexoState};
use super::super::state::{Header, Transaction, TxIn, TxOut};

Expand All @@ -96,9 +231,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 @@ -128,9 +261,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 Down Expand Up @@ -184,4 +315,34 @@ mod tests {
let work = compute_work_from_target(target);
assert(expected_work == work, 'Failed to compute target');
}

#[test]
fn test_validate_proof_of_work() {
let mut block = Block {
header: Header { version: 1, prev_block_hash: 1, time: 12, bits: 1, nonce: 1, },
txs: ArrayTrait::new().span(),
};

// 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");
}
}
Loading

0 comments on commit bd26c80

Please sign in to comment.