Skip to content

Commit

Permalink
wip: add height_proof
Browse files Browse the repository at this point in the history
  • Loading branch information
irisdv committed Oct 24, 2024
1 parent af4cc1b commit 0632118
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 21 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,28 @@ Key features:

Register a list of Bitcoin block headers. Blocks don't need to be contiguous or in order.

### set_main_chain(begin_height: u64, end_height: u64, end_block_hash: Digest)
### update_canonical_chain(begin_height: u64, end_height: u64, end_block_hash: Digest, height_proof: Option<(ByteArray, Span<Digest>)>)

Set the official main chain for a given interval [begin_height, end_height). Verifies that the end block hash and all its parents are registered.
Set the official canonical chain for a given interval [begin_height, end_height). Verifies that the end block hash and all its parents are registered. The `height_proof` parameter is optional and is used to verify the block height when the chain at `begin-1` is not set.

### challenge_block(block_height: u64, blocks: Array<BlockHeader>) → bool

Challenge and potentially update a registered block, with an incentive mechanism for successful updates.
To be implemented.

### get_status(block_height: u64) → Option<BlockStatus>
### get_status(block_hash: Digest) → BlockStatus

Retrieve information about a block at the given height, including its status and other relevant data.
Retrieve information about a block using its hash, including its status and other relevant data.

### get_block(height: u64) → Digest

Retrieve the block hash for a given block height.

## Usage Example

Here's a simplified example of how to securely verify a Bitcoin transaction:

1. Ensure the block containing the transaction is part of the main chain using `set_main_chain`.
1. Ensure the block containing the transaction is part of the main chain using `update_canonical_chain`.
2. Verify the block's status using `get_status` to check if it's unchallenged and has been registered for a sufficient time.
3. Adjust verification requirements based on the specific security needs of your application.

Expand Down
15 changes: 13 additions & 2 deletions src/interfaces.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,19 @@ pub trait IUtuRelay<TContractState> {
/// 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
///
/// The `height_proof` parameter is an optional tuple containing:
/// 1. The raw coinbase transaction of the first block in the interval.
/// 2. A Span of Digest values representing the brother hashes needed to verify the merkle root.
///
/// This height_proof is required to verify the block height when chain[begin-1] is not set.
/// If chain[begin-1] is already set, height_proof can be omitted.
fn update_canonical_chain(
ref self: TContractState,
begin_height: u64,
end_height: u64,
end_block_hash: Digest,
height_proof: Option<(ByteArray, Span<Digest>)>
);

fn challenge_block(
Expand Down
17 changes: 8 additions & 9 deletions src/tests/fork_resolutions.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ fn test_replacing_by_longer_chain() {
utu.register_blocks(block_headers.span());

// this should set the chain to an orphan block
utu.set_main_chain(865_698, 865_699, block_865_699_hash_1);
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::None);
let orphan_digest = utu.get_block(865_699);
assert(orphan_digest == block_865_699_hash_1, 'wrong orphan digest');

// then this should correct it because the canonical chain is stronger
utu.set_main_chain(865_698, 865_700, block_865_700_hash);
utu.update_canonical_chain(865_698, 865_700, block_865_700_hash, Option::None);
let updated_block_digest = utu.get_block(865_699);
assert(updated_block_digest == block_865_699_hash_2, 'wrong replaced digest');

Expand Down Expand Up @@ -145,10 +145,10 @@ fn test_replacing_by_shorter_chain() {
utu.register_blocks(block_headers.span());

// we set the main chain to the stronger canonical chain
utu.set_main_chain(865_698, 865_700, block_865_700_hash);
utu.update_canonical_chain(865_698, 865_700, block_865_700_hash, Option::None);

// then we try to update to an orphan block
utu.set_main_chain(865_698, 865_699, block_865_699_hash_1);
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::None);
let orphan_digest = utu.get_block(865_699);
assert(orphan_digest == block_865_699_hash_1, 'wrong orphan digest');
}
Expand Down Expand Up @@ -202,11 +202,11 @@ fn test_replacing_by_equal_chain() {
utu.register_blocks(block_headers.span());

// we set the main chain to the canonical chain
utu.set_main_chain(865_698, 865_700, block_865_699_hash_2);
utu.update_canonical_chain(865_698, 865_700, block_865_699_hash_2, Option::None);

// then we try to update to an orphan block (should be refused so that you can't update back and
// forth)
utu.set_main_chain(865_698, 865_699, block_865_699_hash_1);
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::None);
let orphan_digest = utu.get_block(865_699);
assert(orphan_digest == block_865_699_hash_1, 'wrong orphan digest');
}
Expand Down Expand Up @@ -275,7 +275,6 @@ fn test_replacing_by_longer_but_weaker_chain() {
// b) [ 0x1, 0x2b, 0x3b, 0x4], 1000, 500, 500, 500
// where a[1:] cpow equals 1999 > 1500 for b[1:] even though b is longer

utu.set_main_chain(1, 3, block3a_digest);
utu.set_main_chain(1, 4, block4_digest);
utu.update_canonical_chain(1, 3, block3a_digest, Option::None);
utu.update_canonical_chain(1, 4, block4_digest, Option::None);
}

16 changes: 11 additions & 5 deletions src/utu_relay.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -54,14 +54,18 @@ pub mod UtuRelay {
}


fn set_main_chain(
ref self: ContractState, begin_height: u64, mut end_height: u64, end_block_hash: Digest
fn update_canonical_chain(
ref self: ContractState,
begin_height: u64,
mut end_height: u64,
end_block_hash: Digest,
height_proof: Option<(ByteArray, Span<Digest>)>
) {
// This helper will write the ancestry of end_block_hash over [begin_height, end_height]
// with chain[end_height] holding end_block_hash. If it overwrote some blocks, it
// returns the cumulated pow of the overwritten blocks (current) and the fork (new).
let (mut current_cpow, new_cpow) = self
.set_main_chain_helper(end_block_hash, end_height, begin_height - 1);
.update_canonical_chain_helper(end_block_hash, end_height, begin_height - 1);

let mut next_block_i = end_height + 1;
let mut next_chain_entry = self.chain.entry(next_block_i);
Expand Down Expand Up @@ -114,7 +118,7 @@ pub mod UtuRelay {

#[generate_trait]
pub impl InternalImpl of InternalTrait {
fn set_main_chain_helper(
fn update_canonical_chain_helper(
ref self: ContractState, new_block_digest: Digest, block_index: u64, stop_index: u64,
) -> (u128, u128) {
// fetch the block stored in the chain
Expand Down Expand Up @@ -146,7 +150,9 @@ pub mod UtuRelay {
block_digest_entry.write(new_block_digest);

let (cpow, new_cpow) = self
.set_main_chain_helper(new_block.prev_block_digest, block_index - 1, stop_index);
.update_canonical_chain_helper(
new_block.prev_block_digest, block_index - 1, stop_index
);

// if there was no conflict before
if current_block_digest == new_block_digest {
Expand Down

0 comments on commit 0632118

Please sign in to comment.