Skip to content

Commit

Permalink
fix: height_proof in set_canonical_chain tests
Browse files Browse the repository at this point in the history
  • Loading branch information
Th0rgal committed Oct 25, 2024
1 parent c09f5a7 commit 4767cab
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 28 deletions.
53 changes: 40 additions & 13 deletions src/bitcoin/transactions/coinbase.cairo
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::utils::{hash::Digest, double_sha256::double_sha256_byte_array};

#[derive(Drop)]
pub struct CoinbaseData {
pub tx_id: Digest,
pub height: u64
Expand All @@ -23,9 +24,14 @@ pub struct CoinbaseData {

pub fn get_coinbase_data(raw_tx: @ByteArray) -> CoinbaseData {
let tx_id: Digest = double_sha256_byte_array(raw_tx);

let mut shift = 0;
// would be zero for segwit transaction
let input_count = raw_tx[4];
if input_count != 1 {
if raw_tx[4] == 0 {
// we skip marker and flag
shift += 2;
}
if raw_tx[4 + shift] != 1 {
panic!("A coinbase transaction input count must be 1.");
};
// we then check this single input is transaction hash 0x0
Expand All @@ -34,24 +40,25 @@ pub fn get_coinbase_data(raw_tx: @ByteArray) -> CoinbaseData {
if prev_tx_i == 37 {
break;
}
if raw_tx[prev_tx_i] != 0 {
if raw_tx[prev_tx_i + shift] != 0 {
panic!("The single coinbase input tx hash input must be 0x0.")
};
prev_tx_i += 1;
};

// Read the compactSize value starting at index 41, almost certainly in first branch
let first_byte = raw_tx[41];
let first_byte = raw_tx[41 + shift];
// the first byte of coinbase data is a marker, then the next 3 bytes hold the block height
let coinbase_height_start_index = if first_byte == 0xfd {
44 // 2-byte varint
} else if first_byte == 0xfe {
46 // 4-byte varint
} else if first_byte == 0xff {
50 // 8-byte varint
} else {
42 // Single byte varint (default case)
};
let coinbase_height_start_index = shift
+ if first_byte == 0xfd {
44 // 2-byte varint
} else if first_byte == 0xfe {
46 // 4-byte varint
} else if first_byte == 0xff {
50 // 8-byte varint
} else {
42 // Single byte varint (default case)
};

if raw_tx[coinbase_height_start_index] != 0x3 {
panic!(
Expand Down Expand Up @@ -90,4 +97,24 @@ mod tests {

assert(coinbase_data.height == 227_836, 'Unexpected block height');
}


#[test]
fn test_get_coinbase_data_2() {
// hex raw transaction of 7b20c53b4b4e962d688d4bb49fb43a53eeb117d4dfb9bcf349a8c7686e74d9e6
// which is the coinbase tx from block 865_698, the first one to include block height
let coinbase_tx_raw_data = from_hex(
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5603a2350d194d696e656420627920416e74506f6f6c20e2002a00b4747806fabe6d6d710d04d1ea50a50e6329e2fa1ab865fa16083c12ae90926ee2d687d78e64243210000000000000000000e2701102000000000000ffffffff05220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade787aca64c130000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed70918c35041817b2ce38010673f3420b25a34c0480a9e53a280f7eb1b0ccffdc00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a03000000000000000002b6a2952534b424c4f434b3a900671964dfd53207639c814270d32fbb670620e4ddd8821dd3d5c100067a1e300000000"
);
let coinbase_data = get_coinbase_data(@coinbase_tx_raw_data);
assert(
coinbase_data
.tx_id == hex_to_hash_rev(
"7b20c53b4b4e962d688d4bb49fb43a53eeb117d4dfb9bcf349a8c7686e74d9e6"
),
'Unexpected coinbase tx id'
);

assert(coinbase_data.height == 865_698, 'Unexpected block height');
}
}
105 changes: 100 additions & 5 deletions src/tests/fork_resolutions.cairo
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::super::interfaces::IUtuRelayDispatcherTrait;
use crate::{
interfaces::BlockStatus, utils::{hex::hex_to_hash_rev, hash::Digest},
interfaces::BlockStatus, utils::{hex::{from_hex, hex_to_hash_rev}, hash::Digest},
bitcoin::block::{BlockHeader, BlockHeaderTrait},
tests::utils::{deploy_utu, BlockStatusIntoSpan, DigestIntoSpan},
};
Expand Down Expand Up @@ -70,11 +70,31 @@ fn test_replacing_by_longer_chain() {
];
utu.register_blocks(block_headers.span());

let coinbase_tx_raw_data = from_hex(
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5603a2350d194d696e656420627920416e74506f6f6c20e2002a00b4747806fabe6d6d710d04d1ea50a50e6329e2fa1ab865fa16083c12ae90926ee2d687d78e64243210000000000000000000e2701102000000000000ffffffff05220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade787aca64c130000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed70918c35041817b2ce38010673f3420b25a34c0480a9e53a280f7eb1b0ccffdc00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a03000000000000000002b6a2952534b424c4f434b3a900671964dfd53207639c814270d32fbb670620e4ddd8821dd3d5c100067a1e300000000"
);

let merkle_branch = [
hex_to_hash_rev("1a523b9d1db74158daea077276b1cd3f3eac87cd685d136133d414d04d7f1aed"),
hex_to_hash_rev("f3b0b4fced6987a1c9a5f2f70eb245585d02bf8579c4ec32d08faa88285738b7"),
hex_to_hash_rev("ea38f07950ea6fd120699c1b77578a8e8f922d4b6640345f5ac264d11b4598da"),
hex_to_hash_rev("4385664ebdd2988d5293167646a791d4ec3e165be0012424c70a842270bba968"),
hex_to_hash_rev("fa19b3bda922723a31ea7d1cfe474cd9f52a8d70b28c481c6567f92581b4d0ab"),
hex_to_hash_rev("47f2f41cdede72662413e02b0a646b3c5f5b4f2629a81f83ad903db78302ef19"),
hex_to_hash_rev("03c644a689e264ed605517e22929b7c6e972d2ad2e942bfeaea50cae8f3a598d"),
hex_to_hash_rev("5041fe4b358c487b01cec6b2ba8780d831465bcae3b748f7e2377915a58ee0f4"),
hex_to_hash_rev("67a27f730a4d4ac69937afb8be7464564710fa2ddcbffc1cdc1cccd4543c6d36"),
hex_to_hash_rev("7602d7c94c5f25b91937fc5ac1c4a3f2cffca3426ca1df7fa3fabd690755f46d"),
hex_to_hash_rev("29c3f6ea6e559e9f03625061f89f405ab7341b32e1e3d3507de104c506e92af4"),
hex_to_hash_rev("d9d0c63e9fdd9a4b278cb41c669405073988f75baaa7719fcda0650badc4b2a2"),
hex_to_hash_rev("372d13ce7f696f978dbf135af7f3658df37126abf93c0996f92ff8f709081ef0")
];
let height_proof = (block_865_698, coinbase_tx_raw_data, merkle_branch.span());

// this should set the chain to an orphan block
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::None);
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::Some(height_proof));
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.update_canonical_chain(865_698, 865_700, block_865_700_hash, Option::None);
let updated_block_digest = utu.get_block(865_699);
Expand All @@ -84,6 +104,36 @@ fn test_replacing_by_longer_chain() {
assert(utu.get_block(865_700) == block_865_700_hash, 'wrong last block');
}

#[test]
#[should_panic(
expected: "You must provide a height proof if you don't continue the canonical chain."
)]
fn test_missing_height_proof() {
let utu = deploy_utu();

start_cheat_block_timestamp(utu.contract_address, 1_728_969_360);

// Block 865_698
let block_865_698_hash = hex_to_hash_rev(
"000000000000000000012bc4e973e18e17b9980ba5b6fe545a5f05e0e222828c"
);
let block_865_698: BlockHeader = BlockHeaderTrait::new(
582_238_208_u32,
hex_to_hash_rev("0000000000000000000135e8b5214c6de06ad988280816ce0daa1d92317c4904"),
hex_to_hash_rev("219394ee994ef9dda390b34d6ef8d7fb3e24a05b2c29f02c1d7839aa6c154787"),
1_728_969_360_u32,
0x17030ecd_u32,
3_876_725_546,
);

// Register all blocks
let block_headers: Array<BlockHeader> = array![block_865_698];
utu.register_blocks(block_headers.span());

// this should panic
utu.update_canonical_chain(865_698, 865_698, block_865_698_hash, Option::None);
}


#[test]
#[should_panic(expected: "Canonical chain has a stronger cumulated pow than your proposed fork.")]
Expand Down Expand Up @@ -144,8 +194,28 @@ fn test_replacing_by_shorter_chain() {
];
utu.register_blocks(block_headers.span());

let coinbase_tx_raw_data = from_hex(
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5603a2350d194d696e656420627920416e74506f6f6c20e2002a00b4747806fabe6d6d710d04d1ea50a50e6329e2fa1ab865fa16083c12ae90926ee2d687d78e64243210000000000000000000e2701102000000000000ffffffff05220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade787aca64c130000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed70918c35041817b2ce38010673f3420b25a34c0480a9e53a280f7eb1b0ccffdc00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a03000000000000000002b6a2952534b424c4f434b3a900671964dfd53207639c814270d32fbb670620e4ddd8821dd3d5c100067a1e300000000"
);
let merkle_branch = [
hex_to_hash_rev("1a523b9d1db74158daea077276b1cd3f3eac87cd685d136133d414d04d7f1aed"),
hex_to_hash_rev("f3b0b4fced6987a1c9a5f2f70eb245585d02bf8579c4ec32d08faa88285738b7"),
hex_to_hash_rev("ea38f07950ea6fd120699c1b77578a8e8f922d4b6640345f5ac264d11b4598da"),
hex_to_hash_rev("4385664ebdd2988d5293167646a791d4ec3e165be0012424c70a842270bba968"),
hex_to_hash_rev("fa19b3bda922723a31ea7d1cfe474cd9f52a8d70b28c481c6567f92581b4d0ab"),
hex_to_hash_rev("47f2f41cdede72662413e02b0a646b3c5f5b4f2629a81f83ad903db78302ef19"),
hex_to_hash_rev("03c644a689e264ed605517e22929b7c6e972d2ad2e942bfeaea50cae8f3a598d"),
hex_to_hash_rev("5041fe4b358c487b01cec6b2ba8780d831465bcae3b748f7e2377915a58ee0f4"),
hex_to_hash_rev("67a27f730a4d4ac69937afb8be7464564710fa2ddcbffc1cdc1cccd4543c6d36"),
hex_to_hash_rev("7602d7c94c5f25b91937fc5ac1c4a3f2cffca3426ca1df7fa3fabd690755f46d"),
hex_to_hash_rev("29c3f6ea6e559e9f03625061f89f405ab7341b32e1e3d3507de104c506e92af4"),
hex_to_hash_rev("d9d0c63e9fdd9a4b278cb41c669405073988f75baaa7719fcda0650badc4b2a2"),
hex_to_hash_rev("372d13ce7f696f978dbf135af7f3658df37126abf93c0996f92ff8f709081ef0")
];
let height_proof = (block_865_698, coinbase_tx_raw_data, merkle_branch.span());

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

// then we try to update to an orphan block
utu.update_canonical_chain(865_698, 865_699, block_865_699_hash_1, Option::None);
Expand Down Expand Up @@ -201,8 +271,28 @@ fn test_replacing_by_equal_chain() {
let block_headers: Array<BlockHeader> = array![block_865_698, block_865_699_1, block_865_699_2];
utu.register_blocks(block_headers.span());

let coinbase_tx_raw_data = from_hex(
"01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff5603a2350d194d696e656420627920416e74506f6f6c20e2002a00b4747806fabe6d6d710d04d1ea50a50e6329e2fa1ab865fa16083c12ae90926ee2d687d78e64243210000000000000000000e2701102000000000000ffffffff05220200000000000017a91442402a28dd61f2718a4b27ae72a4791d5bbdade787aca64c130000000017a9145249bdf2c131d43995cff42e8feee293f79297a8870000000000000000266a24aa21a9ed70918c35041817b2ce38010673f3420b25a34c0480a9e53a280f7eb1b0ccffdc00000000000000002f6a2d434f52450164db24a662e20bbdf72d1cc6e973dbb2d12897d54e3ecda72cb7961caa4b541b1e322bcfe0b5a03000000000000000002b6a2952534b424c4f434b3a900671964dfd53207639c814270d32fbb670620e4ddd8821dd3d5c100067a1e300000000"
);
let merkle_branch = [
hex_to_hash_rev("1a523b9d1db74158daea077276b1cd3f3eac87cd685d136133d414d04d7f1aed"),
hex_to_hash_rev("f3b0b4fced6987a1c9a5f2f70eb245585d02bf8579c4ec32d08faa88285738b7"),
hex_to_hash_rev("ea38f07950ea6fd120699c1b77578a8e8f922d4b6640345f5ac264d11b4598da"),
hex_to_hash_rev("4385664ebdd2988d5293167646a791d4ec3e165be0012424c70a842270bba968"),
hex_to_hash_rev("fa19b3bda922723a31ea7d1cfe474cd9f52a8d70b28c481c6567f92581b4d0ab"),
hex_to_hash_rev("47f2f41cdede72662413e02b0a646b3c5f5b4f2629a81f83ad903db78302ef19"),
hex_to_hash_rev("03c644a689e264ed605517e22929b7c6e972d2ad2e942bfeaea50cae8f3a598d"),
hex_to_hash_rev("5041fe4b358c487b01cec6b2ba8780d831465bcae3b748f7e2377915a58ee0f4"),
hex_to_hash_rev("67a27f730a4d4ac69937afb8be7464564710fa2ddcbffc1cdc1cccd4543c6d36"),
hex_to_hash_rev("7602d7c94c5f25b91937fc5ac1c4a3f2cffca3426ca1df7fa3fabd690755f46d"),
hex_to_hash_rev("29c3f6ea6e559e9f03625061f89f405ab7341b32e1e3d3507de104c506e92af4"),
hex_to_hash_rev("d9d0c63e9fdd9a4b278cb41c669405073988f75baaa7719fcda0650badc4b2a2"),
hex_to_hash_rev("372d13ce7f696f978dbf135af7f3658df37126abf93c0996f92ff8f709081ef0")
];
let height_proof = (block_865_698, coinbase_tx_raw_data, merkle_branch.span());

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

// then we try to update to an orphan block (should be refused so that you can't update back and
// forth)
Expand Down Expand Up @@ -275,6 +365,11 @@ 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

// so we skip height verification
let chain_block_1_addr =
3057458122501230473334132957886155656455173919071911735801003915624585018607;
store(utu.contract_address, chain_block_1_addr, block1_digest.into());

utu.update_canonical_chain(1, 3, block3a_digest, Option::None);
utu.update_canonical_chain(1, 4, block4_digest, Option::None);
}
39 changes: 29 additions & 10 deletions src/utu_relay.cairo
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,16 @@ pub mod UtuRelay {
end_block_hash: Digest,
height_proof: Option<(BlockHeader, ByteArray, Span<Digest>)>
) {
let mut requires_height_proof = true;
// 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
.update_canonical_chain_helper(end_block_hash, end_height, begin_height - 1);
.update_canonical_chain_helper(
ref requires_height_proof, end_block_hash, end_height, begin_height - 1
);

if self.chain.read(begin_height - 1).is_zero() {
if requires_height_proof {
match height_proof {
Option::None => {
panic!(
Expand All @@ -79,6 +82,10 @@ pub mod UtuRelay {
header, coinbase_raw_data, merkle_proof
)) => {
if self.chain.read(begin_height) != header.hash() {
println!("begin_height: {}", begin_height);
println!(
"hashes: {}, {}", self.chain.read(begin_height), header.hash()
);
panic!(
"Your provided proof doesn't correspond to the begin block height."
);
Expand Down Expand Up @@ -145,7 +152,11 @@ pub mod UtuRelay {
#[generate_trait]
pub impl InternalImpl of InternalTrait {
fn update_canonical_chain_helper(
ref self: ContractState, new_block_digest: Digest, block_index: u64, stop_index: u64,
ref self: ContractState,
ref requires_height_proof: bool,
new_block_digest: Digest,
block_index: u64,
stop_index: u64,
) -> (u128, u128) {
// fetch the block stored in the chain
let block_digest_entry = self.chain.entry(block_index);
Expand All @@ -158,12 +169,15 @@ pub mod UtuRelay {
// For honest users, simply provide the correct replacement blocks
if block_index == stop_index {
// new_block_digest is previous_block_digest of his son we just processed
if current_block_digest != Zero::zero()
&& current_block_digest != new_block_digest {
panic!(
"Canonical chain block preceding your proposed fork is inconsistent. Please provide a stronger replacement."
);
// if there is no block, we need a height_proof
if current_block_digest != Zero::zero() {
if current_block_digest != new_block_digest {
panic!(
"Canonical chain block preceding your proposed fork is inconsistent. Please provide a stronger replacement."
);
} else {
// if there is a valid connecting block, we don't need a height_proof
requires_height_proof = false;
};
}

return (0, 0);
Expand All @@ -178,11 +192,16 @@ pub mod UtuRelay {

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

// if there was no conflict before
if current_block_digest == new_block_digest {
// this block was already present, no need for a height proof
requires_height_proof = false;
return (0, 0);
// if there was a conflict (may be), we measure cpow & new_cpow
} else {
Expand Down

0 comments on commit 4767cab

Please sign in to comment.