diff --git a/README.md b/README.md index b74314c..216ebe1 100644 --- a/README.md +++ b/README.md @@ -4,11 +4,11 @@ Utu Relay is a Starknet smart contract that enables secure verification of Bitco ## Overview -Utu Relay allows anyone to register Bitcoin block headers on Starknet and set the main chain. Smart contracts can access these blocks, along with trust metrics like cumulative proof-of-work and unchallenged time, to verify that any Bitcoin transaction was accepted by the network. +Utu Relay allows anyone to register Bitcoin block headers on Starknet and set the canonical chain. Smart contracts can access these blocks, along with trust metrics like cumulative proof-of-work and unchallenged time, to verify that any Bitcoin transaction was accepted by the network. Key features: - Verify any part of Bitcoin's history -- Maintain an official main chain +- Maintain canonical chain - Strong security guarantees - Game theory incentives for maintaining accuracy @@ -47,7 +47,7 @@ Retrieve the block hash for a given block height. 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 `update_canonical_chain`. +1. Ensure the block containing the transaction is part of the canonical 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. diff --git a/src/interfaces.cairo b/src/interfaces.cairo index 873536f..fd42712 100644 --- a/src/interfaces.cairo +++ b/src/interfaces.cairo @@ -29,10 +29,10 @@ pub trait IUtuRelay { /// 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. + /// Note: Registered blocks are not automatically included in the canonical chain. fn register_blocks(ref self: TContractState, blocks: Span); - /// Sets the main chain for a given interval. + /// Sets the canonical 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 @@ -49,7 +49,7 @@ pub trait IUtuRelay { begin_height: u64, end_height: u64, end_block_hash: Digest, - height_proof: Option<(ByteArray, Span)> + height_proof: Option<(BlockHeader, ByteArray, Span)> ); fn challenge_block( diff --git a/src/tests/fork_resolutions.cairo b/src/tests/fork_resolutions.cairo index afd8fd7..1d4a82b 100644 --- a/src/tests/fork_resolutions.cairo +++ b/src/tests/fork_resolutions.cairo @@ -86,7 +86,7 @@ fn test_replacing_by_longer_chain() { #[test] -#[should_panic(expected: "Main chain has a stronger cumulated pow than your proposed fork.")] +#[should_panic(expected: "Canonical chain has a stronger cumulated pow than your proposed fork.")] fn test_replacing_by_shorter_chain() { let utu = deploy_utu(); @@ -144,7 +144,7 @@ fn test_replacing_by_shorter_chain() { ]; utu.register_blocks(block_headers.span()); - // we set the main chain to the stronger canonical chain + // we set the canonical chain to the stronger canonical chain utu.update_canonical_chain(865_698, 865_700, block_865_700_hash, Option::None); // then we try to update to an orphan block @@ -155,7 +155,7 @@ fn test_replacing_by_shorter_chain() { #[test] -#[should_panic(expected: "Main chain has a stronger cumulated pow than your proposed fork.")] +#[should_panic(expected: "Canonical chain has a stronger cumulated pow than your proposed fork.")] fn test_replacing_by_equal_chain() { let utu = deploy_utu(); @@ -201,7 +201,7 @@ fn test_replacing_by_equal_chain() { let block_headers: Array = array![block_865_698, block_865_699_1, block_865_699_2]; utu.register_blocks(block_headers.span()); - // we set the main chain to the canonical chain + // we set the canonical chain to the canonical chain 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 @@ -212,7 +212,7 @@ fn test_replacing_by_equal_chain() { } #[test] -#[should_panic(expected: "Main chain has a stronger cumulated pow than your proposed fork.")] +#[should_panic(expected: "Canonical chain has a stronger cumulated pow than your proposed fork.")] fn test_replacing_by_longer_but_weaker_chain() { let utu = deploy_utu(); // a random timestamp diff --git a/src/utu_relay.cairo b/src/utu_relay.cairo index 79b0e6c..e382ab6 100644 --- a/src/utu_relay.cairo +++ b/src/utu_relay.cairo @@ -3,8 +3,9 @@ pub mod UtuRelay { use starknet::storage::{StorageMapWriteAccess}; use crate::{ utils::hash::Digest, - bitcoin::block::{ - BlockHeader, BlockHashTrait, PowVerificationTrait, compute_pow_from_target + bitcoin::{ + block::{BlockHeader, BlockHashTrait, PowVerificationTrait, compute_pow_from_target}, + block_height::get_block_height }, interfaces::{IUtuRelay, BlockStatus, BlockStatusTrait} }; @@ -59,7 +60,7 @@ pub mod UtuRelay { begin_height: u64, mut end_height: u64, end_block_hash: Digest, - height_proof: Option<(ByteArray, Span)> + height_proof: Option<(BlockHeader, ByteArray, Span)> ) { // 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 @@ -67,6 +68,31 @@ pub mod UtuRelay { let (mut current_cpow, new_cpow) = self .update_canonical_chain_helper(end_block_hash, end_height, begin_height - 1); + if self.chain.read(begin_height - 1).is_zero() { + match height_proof { + Option::None => { + panic!( + "You must provide a height proof if you don't continue the canonical chain." + ) + }, + Option::Some(( + header, coinbase_raw_data, merkle_proof + )) => { + if self.chain.read(begin_height) != header.hash() { + panic!( + "Your provided proof doesn't correspond to the begin block height." + ); + }; + let extracted_height = get_block_height( + @header, @coinbase_raw_data, merkle_proof + ); + if extracted_height != begin_height { + panic!("Your provided proof doesn't prove the correct height."); + }; + } + } + }; + let mut next_block_i = end_height + 1; let mut next_chain_entry = self.chain.entry(next_block_i); let mut next_block_digest = next_chain_entry.read(); @@ -94,7 +120,7 @@ pub mod UtuRelay { // and automatically revert everything if the fork cpow is weaker if current_cpow >= new_cpow { - panic!("Main chain has a stronger cumulated pow than your proposed fork."); + panic!("Canonical chain has a stronger cumulated pow than your proposed fork."); } } @@ -135,9 +161,10 @@ pub mod UtuRelay { if current_block_digest != Zero::zero() && current_block_digest != new_block_digest { panic!( - "Main chain block preceding your proposed fork is inconsistent. Please provide a stronger replacement." + "Canonical chain block preceding your proposed fork is inconsistent. Please provide a stronger replacement." ); - }; + // if there is no block, we need a height_proof + } return (0, 0); }