Skip to content

Commit

Permalink
wip: store main_chain
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Oct 17, 2024
1 parent 1e1a8aa commit de3fee7
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 58 deletions.
35 changes: 25 additions & 10 deletions src/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@ use starknet::get_block_timestamp;

#[derive(Drop, Serde, Default, starknet::Store)]
pub struct BlockStatus {
pub digest: Digest,
pub challenged_cpow: u128,
// to do: instead of storing the prev_block_digest, we could store its potential address in
// memory
pub registration_timestamp: u64,
pub prev_block_digest: Digest,
pub challenged_cpow: u128,
pub pow: u128,
}

Expand All @@ -16,21 +18,34 @@ pub impl BlockStatusImpl of BlockStatusTrait {
*self.registration_timestamp == 0
}

fn new(digest: Digest, pow: u128) -> BlockStatus {
fn new(prev_block_digest: Digest, pow: u128) -> BlockStatus {
BlockStatus {
digest, challenged_cpow: 0, registration_timestamp: get_block_timestamp(), pow,
prev_block_digest,
registration_timestamp: get_block_timestamp(),
challenged_cpow: 0,
pow,
}
}
}

#[starknet::interface]
pub trait IUtuRelay<TContractState> {
fn register_blocks(
ref self: TContractState,
starting_height: u64,
height_proof: Option<felt252>,
blocks: Span<BlockHeader>
) -> bool;
/// Registers new blocks with the relay.
///
/// This function allows anyone to register blocks (they don't have to be contiguous or in
/// order). It verifies that each registered block passes its threshold.
///
/// Note: Registered blocks are not automatically included in the main chain.
fn register_blocks(ref self: TContractState, blocks: Span<BlockHeader>);

/// Sets the main chain for a given interval.
///
/// This function allows setting the "official chain" (the strongest one) over the provided
/// interval. It starts from the end block hash and verifies that this hash and all its
/// parents are registered. The interval is specified as [ begin, end [.
fn set_main_chain(
ref self: TContractState, begin_height: u64, end_height: u64, end_block_hash: Digest
);

fn challenge_block(
ref self: TContractState, block_height: u64, blocks: Array<BlockHeader>
Expand Down
126 changes: 78 additions & 48 deletions src/utu_relay.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,19 @@ pub mod UtuRelay {
},
interfaces::{IUtuRelay, BlockStatus, BlockStatusTrait}
};
use starknet::storage::{StorageMapReadAccess, Map};
use starknet::storage::{
StorageMapReadAccess, StoragePointerReadAccess, StoragePointerWriteAccess, StoragePathEntry,
Map
};
use core::num::traits::zero::Zero;


#[storage]
struct Storage {
blocks: Map<u64, BlockStatus>,
// This is an acyclic graph which contains all blocks registered, including those from forks
blocks: Map<Digest, BlockStatus>,
// This is a mapping of each chain height to a block from the strongest chain registered
chain: Map<u64, Digest>,
}

#[event]
Expand All @@ -24,19 +32,76 @@ pub mod UtuRelay {

#[abi(embed_v0)]
impl UtuRelayImpl of IUtuRelay<ContractState> {
fn register_blocks(
ref self: ContractState,
starting_height: u64,
height_proof: Option<felt252>,
mut blocks: Span<BlockHeader>
) -> bool {
// Implementation for register_blockss
// For now, we'll just return false
let prev_block = self.blocks.read(starting_height - 1);
self.register_blocks_helper(starting_height, blocks, prev_block.digest);
false
fn register_blocks(ref self: ContractState, mut blocks: Span<BlockHeader>) {
loop {
match blocks.pop_front() {
Option::None => { break; },
Option::Some(block) => {
let block_hash = block.hash();
let target_threshold = block.compute_target_threshold();
// verifies pow spent
if block_hash.into() < target_threshold {
panic!("Block hash is higher than its target threshold.");
};
// estimate pow value
let pow = compute_pow_from_target(target_threshold);
self
.blocks
.write(block_hash, BlockStatusTrait::new(*block.prev_block_hash, pow));
}
};
};
}


fn set_main_chain(
ref self: ContractState, begin_height: u64, mut end_height: u64, end_block_hash: Digest
) { // override existing blocks starting at `end_height`
let mut current_cpow: u128 = 0;
let mut block_i = end_height;
loop {
let block_digest = self.chain.read(block_i);
if block_digest == Zero::zero() {
break;
}
let block_entry = self.blocks.entry(block_digest);
let block = block_entry.read();
// we erase the block, this will get reverted if pow is not sufficient
block_entry.write(Default::default());
current_cpow += block.pow;
block_i += 1;
};

// cancel if these existing blocks have a stronger cpow
let new_block = self.blocks.read(end_block_hash);
let mut new_cpow = new_block.pow;
if new_cpow <= current_cpow {
panic!("Main chain has a stronger cpow than your proposed fork last block pow.");
};
self.chain.write(end_height, end_block_hash);
let mut block_hash = new_block.prev_block_digest;

// write blocks
loop {
if begin_height == end_height {
break;
};
let block = self.blocks.read(block_hash);
new_cpow += block.pow;
end_height -= 1;

// check if there is an existing block
let block_hash_entry = self.chain.entry(end_height);
// if there is no existing block, its pow will be 0
if new_cpow <= self.blocks.read(block_hash_entry.read()).pow {
panic!("Main chain has a single block stronger than your proposed fork.");
};
block_hash_entry.write(block_hash);
block_hash = block.prev_block_digest;
};
}


fn challenge_block(
ref self: ContractState, block_height: u64, blocks: Array<BlockHeader>
) -> bool {
Expand All @@ -51,39 +116,4 @@ pub mod UtuRelay {
Option::None
}
}

#[generate_trait]
impl InternalImpl of InternalTrait {
fn register_blocks_helper(
ref self: ContractState,
height: u64,
mut blocks: Span<BlockHeader>,
mut prev_hash: Digest,
) -> u128 {
match blocks.pop_front() {
Option::None => { 0 },
Option::Some(block) => {
let block_hash = block.hash();
let next_cpow = self.register_blocks_helper(height + 1, blocks, block_hash);
let target_threshold = block.compute_target_threshold();
// verifies pow spent
if block_hash.into() < target_threshold {
panic!("Block hash is higher than its target threshold.");
};

let pow = compute_pow_from_target(target_threshold);
let existing_block = self.blocks.read(height);
let cpow = next_cpow + pow;
if !existing_block.is_empty() && existing_block.digest != block_hash {
// todo: computing existing_block cpow and compare to cpow or panic
// todo: optimize to avoid recomputing alt cpow
panic!("You can't overwrite a block with a higher cumulated pow.");
};
self.blocks.write(height, BlockStatusTrait::new(block_hash, pow));
// todo: check hash connection before AND after?
cpow
}
}
}
}
}

0 comments on commit de3fee7

Please sign in to comment.