From ab995e554cb1933feb508242e3949a337b69591e Mon Sep 17 00:00:00 2001 From: 0xK2 <65908739+thomas192@users.noreply.github.com> Date: Tue, 26 Mar 2024 22:57:03 +0100 Subject: [PATCH] feat: da verifier (#129) * Added doc * test_verifier WIP * test_verifier done * Update * Added doc * test_rollup_inclusion_proofs WIP * test_rollup_inclusion_proofs done * TODOs comments * Scarb fmt --- src/blobstreamx.cairo | 1 - src/tree/binary/merkle_tree.cairo | 2 +- src/verifier/da_verifier.cairo | 166 +++- .../tests/test_rollup_inclusion_proofs.cairo | 717 ++++++++++++++++-- src/verifier/tests/test_verifier.cairo | 430 ++++++++++- src/verifier/types.cairo | 3 + 6 files changed, 1215 insertions(+), 104 deletions(-) diff --git a/src/blobstreamx.cairo b/src/blobstreamx.cairo index 00f7756..3d82690 100644 --- a/src/blobstreamx.cairo +++ b/src/blobstreamx.cairo @@ -152,7 +152,6 @@ mod blobstreamx { data_root_bytes.append_u256(data_root.data_root); let (is_proof_valid, _) = merkle_tree::verify(root, @proof, @data_root_bytes); - is_proof_valid } } diff --git a/src/tree/binary/merkle_tree.cairo b/src/tree/binary/merkle_tree.cairo index c452135..2113820 100644 --- a/src/tree/binary/merkle_tree.cairo +++ b/src/tree/binary/merkle_tree.cairo @@ -3,7 +3,7 @@ use blobstream_sn::tree::binary::hasher::{leaf_digest, node_digest}; use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; use blobstream_sn::tree::utils::{path_length_from_key, get_split_point}; -#[derive(Copy, Drop, PartialEq)] +#[derive(Copy, Drop, PartialEq, Debug)] enum ErrorCodes { NoError, InvalidNumberOfSideNodes, diff --git a/src/verifier/da_verifier.cairo b/src/verifier/da_verifier.cairo index b739c1b..763488b 100644 --- a/src/verifier/da_verifier.cairo +++ b/src/verifier/da_verifier.cairo @@ -1,3 +1,5 @@ +/// The DAVerifier verifies that some shares, which were posted on Celestia, were committed to +/// by the BlobstreamX smart contract. mod DAVerifier { use alexandria_bytes::{Bytes, BytesTrait}; use blobstream_sn::interfaces::{IDAOracleDispatcher, IDAOracleDispatcherTrait}; @@ -8,9 +10,15 @@ mod DAVerifier { }; use blobstream_sn::tree::namespace::{Namespace, NamespaceValueTrait}; use blobstream_sn::verifier::types::{SharesProof, AttestationProof}; + use core::traits::TryInto; // TODO: Error naming & other naming: to enum? + // I am not sure what is better practice, so far we have been using mostly + // modules and consts for errors in piltover and here + // I can make the change but I suppose we should stick to only one + // TODO: data_root_tuple -> data_root & data_root_tuple_root -> data_root? + // not sure what you mean here mod Error { const NoError: felt252 = 'NoError'; // The shares to the rows proof is invalid. @@ -29,16 +37,24 @@ mod DAVerifier { const UnequalDataLengthAndNumberOfSharesProofs: felt252 = 'UnequalDLandNSP'; // The number of leaves in the binary merkle proof is not divisible by 4. const InvalidNumberOfLeavesInProof: felt252 = 'InvalidNumberOfLeavesInProof'; - // The provided range is invalid. - const InvalidRange: felt252 = 'InvalidRange'; - // The provided range is out of bounds. - const OutOfBoundsRange: felt252 = 'OutOfBoundsRange'; } + /// Verifies that the shares, which were posted to Celestia, were committed to by the Blobstream smart contract. + /// + /// # Arguments + /// + /// * `bridge` - The Blobstream smart contract instance. + /// * `shares_proof` - The proof of the shares to the data root tuple root. + /// * `root` - The data root of the block that contains the shares. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, Error::NoError otherwise. fn verify_shares_to_data_root_tuple_root( bridge: IDAOracleDispatcher, shares_proof: SharesProof, root: u256 ) -> (bool, felt252) { - // checking that the data root was committed to by the Blobstream smart contract. + // check that the data root was committed to by the Blobstream smart contract. let (success, error) = verify_multi_row_roots_to_data_root_tuple_root( bridge, shares_proof.row_roots.span(), @@ -49,7 +65,6 @@ mod DAVerifier { if !success { return (false, error); } - return verify_shares_to_data_root_tuple_root_proof( shares_proof.data.span(), shares_proof.share_proofs.span(), @@ -60,6 +75,21 @@ mod DAVerifier { ); } + /// Verifies the shares to data root tuple root proof. + /// + /// # Arguments + /// + /// * `data` - The data that needs to be proven. + /// * `share_proofs` - The share to the row roots proof. + /// * `namespace` - The namespace of the shares. + /// * `row_roots` - The row roots where the shares belong. + /// * `row_proofs` - The proofs of the rowRoots to the data root. + /// * `root` - The data root of the block that contains the shares. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, Error::NoError otherwise. fn verify_shares_to_data_root_tuple_root_proof( data: Span, share_proofs: Span, @@ -68,6 +98,7 @@ mod DAVerifier { row_proofs: Span, root: u256 ) -> (bool, felt252) { + // check that the rows roots commit to the data root let (success, error) = verify_multi_row_roots_to_data_root_tuple_root_proof( row_roots, row_proofs, root ); @@ -79,17 +110,23 @@ mod DAVerifier { return (false, Error::UnequalShareProofsAndRowRootsNumber); } - //TODO: to u32? - let mut number_of_shares_in_proofs: u256 = 0; + // TODO: to u32? + // currently max square size is 128 => extended square size 256 + // max number of shares = 256 * 256 = 65,536 => fits in a u32 + // to the extent of my understanding of the celestia protocol key values + // should then also fit inside a u32 + // then, why are NamespaceMerkleMultiproof key fields u256 ? + let mut number_of_shares_in_proofs: u32 = 0; let mut i: u32 = 0; while i < share_proofs .len() { - number_of_shares_in_proofs += *share_proofs.at(i).end_key - - *share_proofs.at(i).begin_key; + // should begin_key and end_key fields really be u256 ? + let diff = *share_proofs.at(i).end_key - *share_proofs.at(i).begin_key; + number_of_shares_in_proofs += diff.try_into().unwrap(); i += 1; }; - if data.len().into() != number_of_shares_in_proofs { + if data.len() != number_of_shares_in_proofs { return (false, Error::UnequalDataLengthAndNumberOfSharesProofs); } @@ -100,6 +137,7 @@ mod DAVerifier { .len() { let shares_used: u256 = *share_proofs.at(i).end_key - *share_proofs.at(i).begin_key; // TODO: Do i need to check for errors here & Span + // may be solved if NamespaceMerkleMultiproof used u32 fields let s: Span = data.slice(cursor, cursor + shares_used.try_into().unwrap()); if !NamespaceMerkleTree::verify_multi( *row_roots.at(i), share_proofs.at(i), namespace, s @@ -118,9 +156,20 @@ mod DAVerifier { return (true, Error::NoError); } - // Verifies that a row/column root, from a Celestia block, - // was committed to by the Blobstream smart contract. - // Returns (success, error). + /// Verifies that a row/column root, from a Celestia block, was committed to by the Blobstream smart contract. + /// + /// # Arguments + /// + /// * `bridge` - The Blobstream smart contract instance. + /// * `row_root` - The row/column root to be proven. + /// * `row_proof` - The proof of the row/column root to the data root. + /// * `attestation_proof` - The proof of the data root tuple to the data root tuple root that was posted to the Blobstream contract. + /// * `root` - The data root of the block that contains the row. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, Error::NoError otherwise. fn verify_row_root_to_data_root_tuple_root( bridge: IDAOracleDispatcher, row_root: NamespaceNode, @@ -130,6 +179,10 @@ mod DAVerifier { ) -> (bool, felt252) { // check that the data root was commited to by the Blobstream smart contract // TODO: safe unwrap? + // a choice was made to use a u64 instead of a u256 (from the solidity contract) + // for the `state_proof_nonce` in the blobstreamx contract + // I suppose the `commit_nonce` field should actually be a u64 then which would + // solve the problem if !bridge .verify_attestation( attestation_proof.commit_nonce.try_into().unwrap(), @@ -139,9 +192,22 @@ mod DAVerifier { return (false, Error::InvalidDataRootTupleToDataRootTupleRootProof); } + // check that the row root commits to the data root return verify_row_root_to_data_root_tuple_root_proof(row_root, row_proof, root); } + /// Verifies that a row/column root proof, from a Celestia block, to its corresponding data root. + /// + /// # Arguments + /// + /// * `row_root` - The row/column root to be proven. + /// * `row_proof` - The proof of the row/column root to the data root. + /// * `root` - The data root of the block that contains the row. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, ErrorCodes.NoError otherwise. fn verify_row_root_to_data_root_tuple_root_proof( row_root: NamespaceNode, row_proof: BinaryMerkleProof, root: u256 ) -> (bool, felt252) { @@ -157,6 +223,20 @@ mod DAVerifier { return (true, Error::NoError); } + /// Verifies that a set of rows/columns, from a Celestia block, were committed to by the Blobstream smart contract. + /// + /// # Arguments + /// + /// * `bridge` - The Blobstream smart contract instance. + /// * `row_roots` - The set of row/column roots to be proved. + /// * `row_proofs` - The set of proofs of the _rowRoots in the same order. + /// * `attestation_proof` - The proof of the data root tuple to the data root tuple root that was posted to the Blobstream contract. + /// * `root` - The data root of the block that contains the rows. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, Error::NoError otherwise. fn verify_multi_row_roots_to_data_root_tuple_root( bridge: IDAOracleDispatcher, row_roots: Span, @@ -174,9 +254,22 @@ mod DAVerifier { return (false, Error::InvalidDataRootTupleToDataRootTupleRootProof); } + // check that the rows roots commit to the data root return verify_multi_row_roots_to_data_root_tuple_root_proof(row_roots, row_proofs, root); } + /// Verifies the proof of a set of rows/columns, from a Celestia block, to their corresponding data root. + /// + /// # Arguments + /// + /// * `row_roots` - The set of row/column roots to be proved. + /// * `row_proofs` - The set of proofs of the _rowRoots in the same order. + /// * `root` - The data root of the block that contains the rows. + /// + /// # Returns + /// + /// * `true` if the proof is valid, `false` otherwise. + /// * An error code if the proof is invalid, Error::NoError otherwise. fn verify_multi_row_roots_to_data_root_tuple_root_proof( row_roots: Span, row_proofs: Span, root: u256 ) -> (bool, felt252) { @@ -197,6 +290,7 @@ mod DAVerifier { error = Error::InvalidRowToDataRootProof; break; } + i += 1; }; if error != Error::NoError { return (false, error); @@ -205,6 +299,21 @@ mod DAVerifier { return (true, Error::NoError); } + /// Computes the Celestia block square size from a row/column root to data root binary Merkle proof. + /// + /// Note: The provided proof is not authenticated to the Blobstream smart contract. It is the user's responsibility + /// to verify that the proof is valid and was successfully committed to using + /// the `verify_row_root_to_data_root_tuple_root()` function. + /// Note: The minimum square size is 1. Thus, we don't expect the proof to have number of leaves equal to 0. + /// + /// # Arguments + /// + /// * `proof` - The proof of the row/column root to the data root. + /// + /// # Returns + /// + /// * The square size of the corresponding block. + /// * An error code if the `proof` is invalid, `Error::NoError` otherwise. fn compute_square_size_from_row_proof(proof: BinaryMerkleProof) -> (u256, felt252) { if proof.num_leaves % 4 != 0 { return (0, Error::InvalidNumberOfLeavesInProof); @@ -212,8 +321,35 @@ mod DAVerifier { return (proof.num_leaves / 4, Error::NoError); } + /// Computes the Celestia block square size from a shares to row/column root proof. + /// + /// Note: The provided proof is not authenticated to the Blobstream smart contract. It is the user's responsibility + /// to verify that the proof is valid and that the shares were successfully committed to using + /// the `verify_shares_to_data_root_tuple_root()` function. + /// Note: The minimum square size is 1. Thus, we don't expect the proof to be devoid of any side nodes. + /// + /// # Arguments + /// + /// * `proof` - The proof of the shares to the row/column root. + /// + /// # Returns + /// + /// * The square size of the corresponding block. fn compute_square_size_from_share_proof(proof: NamespaceMerkleMultiproof) -> u256 { - let extended_square_row_size = proof.side_nodes.len(); + // TODO + // `i` could actually fit in a u8 + // currently max square size is 128 => extended square size is 256 + // max number of side nodes in a a binary tree with 256 leaves is log2(256) = 8 + let mut i: u32 = 0; + // same for `extended_square_row_size`, but defining them as u32 may provide some + // flexibility if celestia protocol changes + let mut extended_square_row_size: u32 = 1; + while i < proof.side_nodes.len() { + extended_square_row_size *= 2; + i += 1; + }; + // we divide the extended square row size by 2 because the square size is the + // the size of the row of the original square size. return extended_square_row_size.into() / 2; } } diff --git a/src/verifier/tests/test_rollup_inclusion_proofs.cairo b/src/verifier/tests/test_rollup_inclusion_proofs.cairo index 43e5d3f..4f10b74 100644 --- a/src/verifier/tests/test_rollup_inclusion_proofs.cairo +++ b/src/verifier/tests/test_rollup_inclusion_proofs.cairo @@ -1,8 +1,16 @@ -use blobstream_sn::verifier::da_verifier::DAVerifier; /// This contains the tests and setup from the Blobstream Solidity tests. /// https://github.com/celestiaorg/blobstream-contracts/blob/master/src/lib/verifier/test/RollupInclusionProofs.t.sol +use alexandria_bytes::{Bytes, BytesTrait}; +use blobstream_sn::interfaces::{IDAOracleDispatcher, IDAOracleDispatcherTrait}; +use blobstream_sn::tests::common::setup_base; +use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; +use blobstream_sn::tree::namespace::Namespace; +use blobstream_sn::tree::namespace::merkle_tree::{NamespaceNode, NamespaceMerkleMultiproof}; +use blobstream_sn::verifier::da_verifier::DAVerifier; use blobstream_sn::verifier::types::{SharesProof, AttestationProof}; +use snforge_std as snf; +use starknet::ContractAddress; /// A span sequence defines the location of the rollup transaction data inside the Celestia block. #[derive(Drop)] @@ -26,127 +34,676 @@ struct RollupHeader { sequence: SpanSequence } +// The data used to generate the Celestia proofs: +// ============================================== + +// Original data square: +// ===================== + +// The block used contains four shares. In row major order: +// // PFB share +// 0x0000000000000000000000000000000000000000000000000000000004010000015c00000026da020ace020aa0010a9d010a202f63656c65737469612e626c6f622e76312e4d7367506179466f72426c6f627312790a2f63656c657374696131666e796e676c6175766a6c677472706e306c7a37793864346a67786339703775336e676e7735121d00000000000000000000000000000000000000121312324243243288991a0297022220adf9685b533f637df3943da5baf78310b673afd705f76fb67936889885a137da42010012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2102ef3c3faf700de2c13a40afc24aa17caebf2bb3a4213831682a5d359f7ba8f39f12040a020801180112130a0d0a04757469611205323130303010d0e80c1a40bec3bdabdcb154f9115e85384ad100ca1acca881b0851c078cbc16365f6ab05a591f2954e4a42da5334d7598b9c0132103c633b97e6d8e61996311be24839f6f1201011a04494e4458000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// // blob share +// 0x00000000000000000000000000000000000000121312324243243288990100000117283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932372c313030293b283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932382c3230303030293b283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932392c31303030302900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// // tail padding share +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// // tail padding share +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +// Extended data square: +// ===================== + +// The extended block contains 16 shares. In row major order: +// 0x0000000000000000000000000000000000000000000000000000000004010000015c00000026da020ace020aa0010a9d010a202f63656c65737469612e626c6f622e76312e4d7367506179466f72426c6f627312790a2f63656c657374696131666e796e676c6175766a6c677472706e306c7a37793864346a67786339703775336e676e7735121d00000000000000000000000000000000000000121312324243243288991a0297022220adf9685b533f637df3943da5baf78310b673afd705f76fb67936889885a137da42010012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2102ef3c3faf700de2c13a40afc24aa17caebf2bb3a4213831682a5d359f7ba8f39f12040a020801180112130a0d0a04757469611205323130303010d0e80c1a40bec3bdabdcb154f9115e85384ad100ca1acca881b0851c078cbc16365f6ab05a591f2954e4a42da5334d7598b9c0132103c633b97e6d8e61996311be24839f6f1201011a04494e4458000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x00000000000000000000000000000000000000121312324243243288990100000117283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932372c313030293b283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932382c3230303030293b283078623130393742314439393239623837336641304132413830383134313339446231323838323932362c3078623130393742314439393239623837336641304132413830383134313339446231323838323932392c31303030302900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x00000000000000000000000000000000000000292b291daba93e1ddcfa01000001f8391ebb849b1f15a6aa18e712157e129b0708e1645def6ed550ec00eeebe1e101d88f81fcd9e3c1e2d1fae2f4f74c6ae1da2ad5af015ce0e6ecd557e9ea32665cde5ced5decd5dae4e3ecd7d6646932ead131dd3aef35cbecdde013d41bd59769ece8db36902ea211111d119f13151f95aa1eaa1daa131e131c2f24243e4b750632477e23107a16260af60ee5cbc13255d2377437556a04402f6f51e691a7865f63de32487a40593325761f1d28e319c319f4372e03e0ebdacfe7fd004558dcd8d9e0b5d853efd73232bee838014761e345ec6a2808170b15b83a36ee5914b1a939f55da5fb75d3f4e90f635a0280315b01cb3e735e53807daea41aab18a92c1d282410141b1f6957e6e2281e323c333f1d269db61520d375bb7742887cdc9139d85c2bec8000bd3eb240527f5c310c5a743521dbf87fd6d43312dca34b1c482ce0cb6e79b9381702b62c79ccf45bff6dfe39751a5362f53b03033e0eeee2e6d7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x000000000000000000000000000000000000003b383b2fe9ea1a2f546701000001b3112ec3c0702d265fea23032a26d12af31f10b16770ba5c9378b51eb4b6bab21c97facd8098bca9b1918ebbaa855867b299019be71f7bbcb3bb9f41b8bc3066739773b870b59094bfbbba909d506533b4933e963bb9368dba95b3039f0490dc65bab69534c002e628282f28fd2b222cf3eb2eeb2feb2b2e2b2d090605354d541332f7df0b2bdf22281a2395bca0ab3a749e80d933c2e991fb08ea440876e34371ed973cf1d6f4cb3dbb562f2f02bc21aa218a11031cb3b4998bb3a61e5e48949894b8d99a72b59e323eeabb3e1c7155b14fbb52021b251b23663530054b2861503b87cb5587f89f223415e0c714fa317712af39d547c344d1dae120e822e90c2c02062e2822225841beb102233234313601077d6e290abacb78cae954cd88682886d913a6510077247ee8d3cfd92d0bd6c823178492cf8c8d2c3b8847ef31ed1fadbef6c0792b3601701fc0b299d59ef49d28cb3ed0fd9a290202240aa7aca28f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffe010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x616161616161616161616161616161616161616161616161616161616d01000003d90000001b870104b30104450304630304141dfef1f7f1c4c8faff1efdf7f5fd1ec92d1ee0c4f0d2ffc0e7f5c7e9f7f5fdc43bc0041dfef1f7f1c4c8faff2df3f6c0f6f0f7ffcbc9f8f7f0c8c7c6f62ef7c222c02bf220f8f0c3fe28c622cb2cf6f0f6ca233b32000000000000000000000000000000000000003b383b2fe9ea1a2f546d3e01670115144d91f9d5d027fece956526487b9b533a70c44c8f0d9bf570c021546e5c462287e903003bf004d204e704331dfef5c4f4f5c41efec7c0c6c8f51ec4f1fec62f2321fb2d1ed2cbfdeff1c03b16041701a425274cc608acba2ae84cb8ec46cd4f76137d4b172b2df910da2362c14095623b0e040105033f033b380408040ecbc8faff3b0d2f2d2e2e2e3a83a80b3ee875bb7742887cdc9139d85c2bec8000bd3eb240527f5c310c5a743521dbf87fd6d43312dca34b1c482ce0cb6e79b9381702b62c79ccf45bff6dfe39751a5362f53b03033e0eeee2e6d7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x616161616161616161616161616161616161615a595a4e888b7b4e350e0100000336112ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f211f2ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f221f2d2e2e1229112ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f211f2ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f2b1f2f2e2e2e2e1229112ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f211f2ec3fd2d2e2822e92de628282f28fd2b222cf3eb2eeb2feb2b2e2b2d202d2c28e6fd2d2f2b2b2f282f281f2d2e2e2e2e1200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x616161616161616161616161616161616161617372735323224553e9f001000003922830785f6c33374a413fa13b37cc3b6c0c05aef2daa4f68ed2a600a7aaaeae038658529c85afbaac8093ac999be3f8ae87108e4c03d9ada2a68edeaba92ff3d989d9a5daa68e87a3afa68f8cf2fa2fa9802d8b2aa423bea68bad388d3d8e67faa6a884216b1e443939323962383733664130413241383038311d1a1a24efcb0f2fe4cc163ac2351b04980aa0beba2fdf8222c822dff80ee81df5d1a268495edbfe892fedc2e8d42c19c9333211af3cbb3c99221e02adaa87b0a19f00e5d7888685ad7286d0a48f2f2f75a82b03e4ffafe5a6f811053607377a2a21a7d4347c43289ada4890cb8199ab09fed601512dd503be24c4d8d051ce4f4b3e423f431f32111a3a343d33fadea2ac11302f252c27321b6370371481cb78cae954cd88682886d913a6510077247ee8d3cfd92d0bd6c823178492cf8c8d2c3b8847ef31ed1fadbef6c0792b3601701fc0b299d59ef49d28cb3ed0fd9a290202240aa7aca28f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x61616161616161616161616161616161616161484a487ccac85f7cbd93010000037d391ebbb9c61c1bdba91602101b801095333a7cf0c67bd969c3723071707b7e316793b1516e74437c685b78415cd7f07e6d036ca133c1747d7862eb7a742ef3c467c47ac6726b6576787b6b63d2f12c71692464297921597b667d02620e6b88f17b706620b901a211111d119f13151f95aa1eaa1daa131e131c060f0d23e0dc382f9b8a07138a15113e16667445422ac86151852cb8ab689005a9e605c9afeac5a56725948c99be2678dd1d1d0174174117553902317d716d567d4a30d8ed656e657a856fc772612f24a9782431c5df7ce178d3013d193d16f3232e0def11ffd2295dbedf5d9262152037adb534932dca3b4c288ee4bbe68087ae14a815ab0b1f010f1e111515d7eb757c01162f202d21030ccef612047bbec3bdabdcb154f9115e85384ad100ca1acca881b0851c078cbc16365f6ab05a591f2954e4a42da5334d7598b9c0132103c633b97e6d8e61996311be24839f6f1201011a04494e4458000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9701000002850000003d5d030e7d030ee5020efe020e34329d949b94b7bc939e309f9b9a9f30bf1c30adb797829eb9a19ab5ab9b9a9fb729b90e329d949b94b7bc939e1c9598b998979b9ebebf929b97bcb5b6981e9bb815b91396149297bb9d11b615be1f989798bd16292f00000000000000000000000000000000000000292b291daba93e1ddcf42403f0033734e068918e83189db366f11bedc16cd02ac6b7e358086c9ac6b917dcf6d9e7155dab020029970e820ea10e2c329d9ab7999ab7309db5b9b6bc9a30b7949db61d1617901c3082be9fa494b929350e36034b1918e3b6054e7b10a8e37aa6e7b1e1c938ceef36131c913a8716fdbae866fd290a0e030d022702292b0e050e0abebc939e29081d1c1e1e1e2a53400724a8cb78cae954cd88682886d913a6510077247ee8d3cfd92d0bd6c823178492cf8c8d2c3b8847ef31ed1fadbef6c0792b3601701fc0b299d59ef49d28cb3ed0fd9a290202240aa7aca28f000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9eb7b5b7833537a08342690100000221391ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d17331ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d15331c1e1e3b12391ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d17331ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d13331d1e1e1e1e3b12391ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d17331ebb9f1c1e1115ab1ca211111d119f13151f95aa1eaa1daa131e131c141c1f11a29f1c1d13131d111d11331c1e1e1e1e3b00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9ea5a6a5b1777484b1caf4010000026a112ec3dbf72c22eceb27462922b229f70b0d4f96874b985b824a0049414f4f025ed7d3605c4c7b4e51694e6d6caf924f5d3a5be302854d444a5b8942431d9585578548874a5b5d474c4a585a96931d43511c56104b16754a564d2b59265bf0934a405f17fb30e628282f28fd2b222cf3eb2eeb2feb2b2e2b2d323e3e1aa4be091da3b2352ab8233d0e6e0445757b1d8a5015bc158a920aa8329a8044f9eed8849d571da5b8a88d1f3cbf2c2f394c2578256d1530014d415d7f466200a08f545e5c4dc75e834b581d1dcb401302a39e4ca04a92390d210c22c21017498d20cdea116f87ed6bbe526d42069d8c03d11c8e02751ab78683d1b3e1ef24e927ea332f393e2a20262c9389444e392e1d191f182f3dfec6223452bec3bdabdcb154f9115e85384ad100ca1acca881b0851c078cbc16365f6ab05a591f2954e4a42da5334d7598b9c0132103c633b97e6d8e61996311be24839f6f1201011a04494e4458000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e9e8c8d8cacdcddbaac160a01000002ce28307879b6313d844335013a3d513a662c2acd97b6c185fabbc72ec5c6c1cc2df0697cd1f6c8eacdf9d5c3ebd98f97ccf402f7462cbac8cec3fdaac2c81e95b7f0b7c2b6c7fbf1c9c3c1fbfe82941fc5fa1af212c017d4c1f3ce01fd0afb5494c1c6f3147903443939323962383733664130413241383038310f090816ad882b1d6c550c385537392435f3c8e5e910bcffd15c1f7a42f96b0d43a20dbf4ca9b448f019655a6d751bc38b323203c836eb36df28012dcec5f4ddceec2e86a5f1f6f1c25cf5b5c7ff1d1a43c31a2db48acdaec38103263c263595161e08a4399e8212da758ada6afd3714224d7220691cbd29e3115ba378a2515d4f3440374207330309303937378faacbcd03351d141c17020bb3983b0ec175bb7742887cdc9139d85c2bec8000bd3eb240527f5c310c5a743521dbf87fd6d43312dca34b1c482ce0cb6e79b9381702b62c79ccf45bff6dfe39751a5362f53b03033e0eeee2e6d7000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +// The row roots: +// ============== + +// 1. 0x00000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000012131232424324328899eca190450f1424f4c96f50142cae150261466dcf4d47fb52b5e1cccef047f2fe +// 2. 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffefffffffffffffffffffffffffffffffffffffffffffffffffffffffffe94ddc2da7e01f575f757fbb4fa42ba202d51a576b609a8aeb114fd226c6e7372 +// 3. 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff33622135c2a12b0e5b3f67fd2cdacddbd58b88abbe67a9ce42e456bb88e137c7 +// 4. 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc096faf6ef0b9f1d3439d619b29bbd4b810825e80b658ed1fa7525220dc796b0 + +// The column roots: +// ================= + +// 1. 0x0000000000000000000000000000000000000000000000000000000004fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe43e76fdc8b119c62dd02c197ad666b5c00ccc4736bf34573c9bd995558a2cf0d +// 2. 0x0000000000000000000000000000000000000012131232424324328899fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe37a4e02492e5c60e661b531861ae3f45cf4d6ae5237bab37d857b39763373556 +// 3. 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff215bd7509274803c556914e2a4b840826bb8b7d94de9344dfb2c2b0e71ba2d26 +// 4. 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff35543dab513d606fd51dddbecce5a0ff86d5a5a4b1e081e6fb9afeeaa36ff6 + +// The data root: 0xb9b0d94eae45e56a551e9415701bec462b18329d2a42bcce3e37a22a2ca83a6f + +// The height: 21 + +// The blocks data roots used to create the commitment: +// 21. 0xb9b0d94eae45e56a551e9415701bec462b18329d2a42bcce3e37a22a2ca83a6f +// 22. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 +// 23. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 +// 24. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 + +// The nonce: 2 + +// The data root tuple root: 0xd149f160dec348d8582b8c2629c91fab8189b8dca205c4c01bb378f2f5450c3b +// */ + +// /* +// The rollup state: +// ================= + +// We assume that the rollup state is comprised of the following key-values: + +// | key | value | +// |--------------------------------------------|-------| +// | 0xb1097B1D9929b873fA0A2A80814139Db12882926 | 20100 | +// | 0xb1097B1D9929b873fA0A2A80814139Db12882927 | 10000 | + +// Where the key is the EVM address of an account, and the value is its balance. +// */ + +// /* +// The rollup transactions: +// ======================== + +// We will define the rollup transfer transactions as follows: +// { +// address from; +// address to; +// uint256 amount; +// } + +// And we will encode them, to be sent to a Celestia block as follows: (from, to, amount). + +// Example transaction: +// (0xb1097B1D9929b873fA0A2A80814139Db12882926,0xb1097B1D9929b873fA0A2A80814139Db12882927,100) +// which sends 100 balance from the 0xb1097B1D9929b873fA0A2A80814139Db12882926 account to +// the 0xb1097B1D9929b873fA0A2A80814139Db12882927 account. + +// For this tutorial, the rollup block, which will be submitted to Celestia, will contain the following transactions: +// - (0xb1097B1D9929b873fA0A2A80814139Db12882926,0xb1097B1D9929b873fA0A2A80814139Db12882927,100) +// - (0xb1097B1D9929b873fA0A2A80814139Db12882926,0xb1097B1D9929b873fA0A2A80814139Db12882928,20000) +// - (0xb1097B1D9929b873fA0A2A80814139Db12882926,0xb1097B1D9929b873fA0A2A80814139Db12882929,10000) + +// Given the above state, the third transaction should fail because the 0xb1097B1D9929b873fA0A2A80814139Db12882926 account +// won't have anymore balance left after the first two transactions. + +// Also, if you look at the above raw shares, and take the blob share and convert it from its hex representation +// to ASCII, you will see the above transactions there. +// */ + +// /* +// Tests overview: +// =============== + +// Given the above setup, we will prove the following inside the tests: + +// 1. We will create an invalid header, which points to a sequence of spans outside of a Celestia block, and prove +// that that transaction data is missing. + +// 2. We will create another invalid header, which points to the correct location of the transaction data in the +// Celestia block, and prove that the third transaction, as defined above, is invalid. + +// For the other cases, where the inclusion proof is invalid: + +// - The data is available and was committed to in the Celestia block. That is intrinsically verified in the second test. +// Since checking the state comes after verifying that the data is available. + +// - The data is available, and also all the state transitions are valid, i.e. the transactions posted are valid. +// This verification is just changing the conclusion of the second test, given a different state. + +// Thus, there is no need to add more tests for these scenarios and only keep the first ones. + +// Note: We will not be generating the rollup state root for this test to test against it, we will leave that +// to be defined by rollups depending on how they handle their state. + +fn setup() -> ContractAddress { + let bsx_address = setup_base(); + + // store the commitment we verify against + let proof_nonce: u64 = TestFixture::data_root_tuple_root_nonce().try_into().unwrap(); + let data_commitment: u256 = TestFixture::data_root_tuple_root(); + snf::store( + bsx_address, + snf::map_entry_address( + selector!("state_data_commitments"), array![proof_nonce.into()].span() + ), + array![data_commitment.low.into(), data_commitment.high.into()].span(), + ); + snf::store(bsx_address, selector!("state_proof_nonce"), array![3].span(),); + + bsx_address +} + #[test] +// Test case 1: a rollup header pointing to data that was not published to Celestia fn test_unavailable_data() { + let bsx_address = setup(); + let bridge = IDAOracleDispatcher { contract_address: bsx_address }; + + // let's create an arbitrary span that is out of bounds of the target Celestia block let height: u256 = 21; let start_index: u256 = 0; let length: u256 = 10; - let sequence = SpanSequence { height, index: start_index, length }; + // an invalid header that points to data that doesn't exist in the target Celestia block let header = RollupHeader { state_root: 0x215bd7509274803c556914e2a4b840826bb8b7d94de9344dfb2c2b0e71ba2d26, sequence }; + + // let's first calculate the square size of the Celestia block referenced in the header + // note: the square size can also be computed from the shares proof let (square_size, error) = DAVerifier::compute_square_size_from_row_proof( TestFixture::get_row_root_to_data_root_proof() ); - assert_eq!(error, DAVerifier::Error::NoError, "Compute square size from row proof failed"); - assert_eq!(TestFixture::square_size(), square_size, "Square size mismatch"); + assert_eq!(error, DAVerifier::Error::NoError, "compute square size from row proof failed"); + assert_eq!(TestFixture::square_size(), square_size, "square size mismatch"); + // let's authenticate the row proof to the data root tuple root to be sure that + // the square size is valid. let attestation_proof = AttestationProof { commit_nonce: TestFixture::data_root_tuple_root_nonce(), data_root: TestFixture::get_data_root_tuple(), proof: TestFixture::get_data_root_tuple_proof() }; -//let (success, err) = DAVerifier::verify_row_root_to_data_root_tuple_root( -// bridge, + let (success, error) = DAVerifier::verify_row_root_to_data_root_tuple_root( + bridge: bridge, + row_root: TestFixture::get_first_row_root_node(), + row_proof: TestFixture::get_row_root_to_data_root_proof(), + attestation_proof: attestation_proof, + root: TestFixture::data_root() + ); + assert!(success, "proofs should be valid"); + assert_eq!(error, DAVerifier::Error::NoError, "expected no error"); + + // Now that we're sure that the proof is valid and the square size is valid, + // we can compare the square size against the sequence referenced in the rollup + // header to see if the data exists + let end_index: u256 = header.sequence.height + header.sequence.length; + // this checks that indeed the data referenced in the header is out of bounds of the square. + // thus the data doesn't exist => unavailable + assert!(!(square_size >= end_index)); +} + +#[test] +#[ignore] // test exceeds default steps limit, run with `--max-n-steps 4294967295` +// test case 2: a rollup header pointing to available data that is invalid +fn test_invalid_data() { + let bsx_address = setup(); + let bridge = IDAOracleDispatcher { contract_address: bsx_address }; + + // let's create the sequence span of the rollup data in the Celestia block + let height: u256 = 21; + let start_index: u256 = 1; + let length: u256 = 1; + let sequence = SpanSequence { height, index: start_index, length }; + // a header that points to the rollup data posted in Celestia + let header = RollupHeader { + state_root: 0x215bd7509274803c556914e2a4b840826bb8b7d94de9344dfb2c2b0e71ba2d26, sequence + }; + + // let's first calculate the square size of the Celestia block referenced in the header + let square_size = DAVerifier::compute_square_size_from_share_proof( + TestFixture::get_share_to_row_root_proof() + ); + assert_eq!(TestFixture::square_size(), square_size, "square size mismatch"); + + // let's create the share to data root tuple root proof to be able to validate the square + // size and the data + let data: Array = array![TestFixture::share_data()]; + let share_proofs: Array = array![ + TestFixture::get_share_to_row_root_proof() + ]; + let namespace: Namespace = TestFixture::get_namespace(); + let row_roots: Array = array![TestFixture::get_first_row_root_node()]; + let row_proofs: Array = array![ + TestFixture::get_row_root_to_data_root_proof() + ]; + let attestation_proof = AttestationProof { + commit_nonce: TestFixture::data_root_tuple_root_nonce(), + data_root: TestFixture::get_data_root_tuple(), + proof: TestFixture::get_data_root_tuple_proof() + }; + let share_proof = SharesProof { + data, share_proofs, namespace, row_roots, row_proofs, attestation_proof + }; + + // variables used later but computed here because `share_proof` is moved + let share_index_in_row: u256 = *share_proof.share_proofs.at(0).begin_key; + let share_index_in_row_major_order: u256 = share_index_in_row + + *share_proof.row_proofs.at(0).num_leaves * *share_proof.row_proofs.at(0).key; + + // let's authenticate the share proof to the data root tuple root to be sure that + // the square size is valid. + let (valid, error) = DAVerifier::verify_shares_to_data_root_tuple_root( + bridge, share_proof, root: TestFixture::data_root() + ); + assert!(valid, "proofs should be valid"); + assert_eq!(error, DAVerifier::Error::NoError, "expected no error"); + + // now that we're sure that the proof is valid and the square size is valid, + // we can compare the square size against the sequence referenced in the rollup + // header to see if the data exists + let end_index: u256 = header.sequence.index + header.sequence.length; + // this checks that indeed the data referenced in the header is part of the Celestia + // block, i.e. the sequence of spans in not out of the block's bounds + assert!(square_size >= end_index, "expected end index to be in the block's bounds"); + // the last step is to prove that the share is part of the rollup data + // referenced in the rollup header + // to do so, we will use the proof, already authenticated above, to get the index, + // then, we will compare it against the spans sequence + + // since we're using nmt multiproofs, we have a begin key and an end key of the shares proven + // however, in our case, we're only proving a single share + // thus, we can take the begin key as the index + // note: in the case of multiple shares in the proof, we will need to check all the shares + // if they're part of the sequence of spans, then only use the ones that are part of it + // check if the share is part of the sequence of spans + assert!( + header.sequence.index <= share_index_in_row_major_order, + "share index out of bounds: inferior" + ); + assert!(share_index_in_row_major_order <= end_index, "share index out of bounds: superior"); +// at this level we can parse the share to get the transactions, and compare them to +// the rollup state +// then, we would be able to know if there is an invalid transaction or not +// as explained in the test overview at the beginning of the file, the third transaction +// is an invalid transaction. +// for the sake of simplicity, we will not parse the shares as that is rollup specific } +/// TestFixture contains the necessary information to create proofs for the blob +/// that was posted to Celestia. It represents the data mentioned in the comment at +/// the beginning of this file. mod TestFixture { use alexandria_bytes::{Bytes, BytesTrait}; use blobstream_sn::interfaces::DataRoot; use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; - // - // const share_data: Bytes = BytesTrait::new(512, array![ - // 0x00000000000000000000000000000000, - // 0x00000012131232424324328899010000, - // 0x01172830786231303937423144393932, - // 0x39623837336641304132413830383134, - // 0x313339446231323838323932362c3078, - // 0x62313039374231443939323962383733, - // 0x66413041324138303831343133394462, - // 0x31323838323932372c313030293b2830, - // 0x78623130393742314439393239623837, - // 0x33664130413241383038313431333944, - // 0x6231323838323932362c307862313039, - // 0x37423144393932396238373366413041, - // 0x32413830383134313339446231323838, - // 0x323932382c3230303030293b28307862, - // 0x31303937423144393932396238373366, - // 0x41304132413830383134313339446231, - // 0x323838323932362c3078623130393742, - // 0x31443939323962383733664130413241, - // 0x38303831343133394462313238383239, - // 0x32392c31303030302900000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000000000000, - // ]); - // - // const first_row_root: Bytes = BytesTrait::new(90, array![ - // 0x00000000000000000000000000000000, - // 0x00000000000000000000000004000000, - // 0x00000000000000000000000000000000, - // 0x12131232424324328899eca190450f14, - // 0x24f4c96f50142cae150261466dcf4d47, - // 0xfb52b5e1cccef047f2fe000000000000, - // ]); - // - // const second_row_root: Bytes = BytesTrait::new(90, array![ - // 0xffffffffffffffffffffffffffffffff, - // 0xfffffffffffffffffffffffffeffffff, - // 0xffffffffffffffffffffffffffffffff, - // 0xfffffffffffffffffffe94ddc2da7e01, - // 0xf575f757fbb4fa42ba202d51a576b609, - // 0xa8aeb114fd226c6e7372000000000000, - // ]); - // - // const square_size: u256 = 0x2; - // + use blobstream_sn::tree::namespace::Namespace; + use blobstream_sn::tree::namespace::merkle_tree::{NamespaceNode, NamespaceMerkleMultiproof}; + /// The share containing the blob that was published to Celestia. + fn share_data() -> Bytes { + BytesTrait::new( + 512, + array![ + 0x00000000000000000000000000000000, + 0x00000012131232424324328899010000, + 0x01172830786231303937423144393932, + 0x39623837336641304132413830383134, + 0x313339446231323838323932362c3078, + 0x62313039374231443939323962383733, + 0x66413041324138303831343133394462, + 0x31323838323932372c313030293b2830, + 0x78623130393742314439393239623837, + 0x33664130413241383038313431333944, + 0x6231323838323932362c307862313039, + 0x37423144393932396238373366413041, + 0x32413830383134313339446231323838, + 0x323932382c3230303030293b28307862, + 0x31303937423144393932396238373366, + 0x41304132413830383134313339446231, + 0x323838323932362c3078623130393742, + 0x31443939323962383733664130413241, + 0x38303831343133394462313238383239, + 0x32392c31303030302900000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + ] + ) + } + + /// The first EDS row root. + fn first_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0x00000000000000000000000000000000, + 0x00000000000000000000000004000000, + 0x00000000000000000000000000000000, + 0x12131232424324328899eca190450f14, + 0x24f4c96f50142cae150261466dcf4d47, + 0xfb52b5e1cccef047f2fe000000000000, + ] + ) + } + + /// The second EDS row root. + fn second_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xfffffffffffffffffffffffffeffffff, + 0xffffffffffffffffffffffffffffffff, + 0xfffffffffffffffffffe94ddc2da7e01, + 0xf575f757fbb4fa42ba202d51a576b609, + 0xa8aeb114fd226c6e7372000000000000, + ] + ) + } + + /// The third EDS row root. + fn third_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffff33622135c2a1, + 0x2b0e5b3f67fd2cdacddbd58b88abbe67, + 0xa9ce42e456bb88e137c7000000000000, + ] + ) + } + + /// The fourth EDS row root. + fn fourth_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffc096faf6ef0b, + 0x9f1d3439d619b29bbd4b810825e80b65, + 0x8ed1fa7525220dc796b0000000000000, + ] + ) + } + + /// The first EDS colmun root. + fn first_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0x00000000000000000000000000000000, + 0x00000000000000000000000004ffffff, + 0xffffffffffffffffffffffffffffffff, + 0xfffffffffffffffffffe43e76fdc8b11, + 0x9c62dd02c197ad666b5c00ccc4736bf3, + 0x4573c9bd995558a2cf0d000000000000, + ] + ) + } + + /// The second EDS colmun root. + fn second_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0x00000000000000000000000000000000, + 0x00000012131232424324328899ffffff, + 0xffffffffffffffffffffffffffffffff, + 0xfffffffffffffffffffe37a4e02492e5, + 0xc60e661b531861ae3f45cf4d6ae5237b, + 0xab37d857b39763373556000000000000, + ] + ) + } + + /// The third EDS colmun root. + fn third_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffff215bd7509274, + 0x803c556914e2a4b840826bb8b7d94de9, + 0x344dfb2c2b0e71ba2d26000000000000, + ] + ) + } + + /// The fourth EDS colmun root. + fn fourth_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffff35543dab51, + 0x3d606fd51dddbecce5a0ff86d5a5a4b1, + 0xe081e6fb9afeeaa36ff6000000000000, + ] + ) + } + /// The data root of the block containing the submitted blob. + fn data_root() -> u256 { + 0xb9b0d94eae45e56a551e9415701bec462b18329d2a42bcce3e37a22a2ca83a6f + } + + /// The height of the block containing the submitted blob. + fn height() -> felt252 { + 21 + } + + /// The original square size of the block containing the submitted blob. fn square_size() -> u256 { - 0x2 + 2 } - fn data_root_tuple_root_nonce() -> u256 { - 0x2 + /// The data root tuple root committing to the Celestia block. + fn data_root_tuple_root() -> u256 { + 0xd149f160dec348d8582b8c2629c91fab8189b8dca205c4c01bb378f2f5450c3b } - fn get_data_root_tuple() -> DataRoot { - DataRoot { - height: 21, - data_root: 0xb9b0d94eae45e56a551e9415701bec462b18329d2a42bcce3e37a22a2ca83a6f - } + /// The data root tuple root nonce in the Blobstream contract. + fn data_root_tuple_root_nonce() -> u256 { + 2 } - fn get_data_root_tuple_proof() -> BinaryMerkleProof { - let data_root_tuple_proof_side_nodes: Array = array![ + /// The data root tuple to data root tuple root proof side nodes. + fn data_root_proof_side_nodes() -> Array { + array![ 0x062f1c98fda4619e8ce92c39d1fa02dc68a880fdcf2c28c9ac31cf3abb1d6ab2, 0x8aa95c4c4ef50468dc728d4e90a07560f1c0095d2df4491879e50ef96305751d - ]; - BinaryMerkleProof { - side_nodes: data_root_tuple_proof_side_nodes, key: 0x0, num_leaves: 0x4 - } + ] } - fn get_row_root_to_data_root_proof() -> BinaryMerkleProof { - let row_root_to_data_root_proof_side_nodes: Array = array![ + /// Shares to data root proof side nodes. + fn share_to_data_root_proof_side_nodes() -> Array { + let n1 = Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000004>() + }; + let n2 = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + array![ + NamespaceNode { + min: n1, + max: n1, + digest: 0x2505b82d3c2a3f9262539478dd5f3257fcc452496f0c159c3bc5aae77e8f85ce + }, + NamespaceNode { + min: n2, + max: n2, + digest: 0x9e80fef0ef39caacaa4e0f5b4ace155b42dd0ca70ecd72a047e68a5244a3c6fa + } + ] + } + + /// Row root to data root proof side nodes. + fn row_root_to_data_root_proof_side_nodes() -> Array { + array![ 0xba0a74b15f58344239a4e89847b45d39db30c257c1876a375e246c98c3666cab, 0x89d6a174bb5327c792535cb769d388e5e5904ebdf2c650dc5ff2e1c90b5eb764, 0x5e48d0e89322b5caac9925f7acf77621dc0b06844fef864a2ab92b108fae4101 - ]; + ] + } + + /// The share's namespace. + fn get_namespace() -> Namespace { + Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000012131232424324328899>() + } + } + + /// The data root tuple of the block containing the submitted blob. + fn get_data_root_tuple() -> DataRoot { + DataRoot { height: height(), data_root: data_root() } + } + + /// The data root tuple to data root tuple root proof. + fn get_data_root_tuple_proof() -> BinaryMerkleProof { + BinaryMerkleProof { side_nodes: data_root_proof_side_nodes(), key: 0x0, num_leaves: 0x4 } + } + + /// The first EDS row root. + fn get_first_row_root_node() -> NamespaceNode { + NamespaceNode { + min: Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000004>() + }, + max: Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000012131232424324328899>() + }, + digest: 0xeca190450f1424f4c96f50142cae150261466dcf4d47fb52b5e1cccef047f2fe + } + } + /// The second EDS row root. + fn get_second_row_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x94ddc2da7e01f575f757fbb4fa42ba202d51a576b609a8aeb114fd226c6e7372 + } + } + + /// The third EDS row root. + fn get_third_row_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x33622135c2a12b0e5b3f67fd2cdacddbd58b88abbe67a9ce42e456bb88e137c7 + } + } + + /// The fourth EDS row root. + fn get_fourth_row_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0xc096faf6ef0b9f1d3439d619b29bbd4b810825e80b658ed1fa7525220dc796b0 + } + } + + /// The first EDS column root. + fn get_first_column_root_node() -> NamespaceNode { + NamespaceNode { + min: Namespace { + version: 0xff, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000004>() + }, + max: Namespace { + version: 0xff, + id: bytes31_const::<0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe>() + }, + digest: 0x43e76fdc8b119c62dd02c197ad666b5c00ccc4736bf34573c9bd995558a2cf0d + } + } + + /// The second EDS column root. + fn get_second_column_root_node() -> NamespaceNode { + NamespaceNode { + min: Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000012131232424324328899>() + }, + max: Namespace { + version: 0xff, + id: bytes31_const::<0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe>() + }, + digest: 0x37a4e02492e5c60e661b531861ae3f45cf4d6ae5237bab37d857b39763373556 + } + } + + /// The third EDS column root. + fn get_third_column_root_node() -> NamespaceNode { + NamespaceNode { + min: Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000012131232424324328899>() + }, + max: Namespace { + version: 0xff, + id: bytes31_const::<0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe>() + }, + digest: 0x37a4e02492e5c60e661b531861ae3f45cf4d6ae5237bab37d857b39763373556 + } + } + + /// The fourth EDS column root. + fn get_fourth_column_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x215bd7509274803c556914e2a4b840826bb8b7d94de9344dfb2c2b0e71ba2d26 + } + } + + /// The shares to row root proof. + fn get_share_to_row_root_proof() -> NamespaceMerkleMultiproof { + NamespaceMerkleMultiproof { + begin_key: 1, end_key: 2, side_nodes: share_to_data_root_proof_side_nodes() + } + } + + /// Row root to data root proof. + fn get_row_root_to_data_root_proof() -> BinaryMerkleProof { BinaryMerkleProof { - side_nodes: row_root_to_data_root_proof_side_nodes, key: 0x0, num_leaves: 0x8 + side_nodes: row_root_to_data_root_proof_side_nodes(), key: 0, num_leaves: 8 } } } diff --git a/src/verifier/tests/test_verifier.cairo b/src/verifier/tests/test_verifier.cairo index 3f39205..030dec5 100644 --- a/src/verifier/tests/test_verifier.cairo +++ b/src/verifier/tests/test_verifier.cairo @@ -1,13 +1,429 @@ +use alexandria_bytes::{Bytes, BytesTrait}; +use blobstream_sn::interfaces::{IDAOracleDispatcher, IDAOracleDispatcherTrait}; +use blobstream_sn::tests::common::setup_base; use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; -use blobstream_sn::verifier::types::AttestationProof; +use blobstream_sn::tree::binary::merkle_tree as binary_merkle_tree; +use blobstream_sn::tree::namespace::Namespace; +use blobstream_sn::tree::namespace::merkle_tree::{ + NamespaceNode, NamespaceMerkleMultiproof, NamespaceMerkleTree +}; +use blobstream_sn::verifier::da_verifier::DAVerifier; use blobstream_sn::verifier::types::DataRoot; +use blobstream_sn::verifier::types::{AttestationProof, SharesProof}; +use snforge_std as snf; +use starknet::ContractAddress; + +// The data used to generate the proof: + +// The block used contains a single share: +// 0x0000000000000000000000000000000000000000000000000000000001010000014500000026c3020a95010a92010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412720a2f63656c657374696131746b376c776a77336676616578657770687237687833333472766b67646b736d636537666b66122f63656c65737469613167616b61646d63386a73667873646c676e6d64643867773736346739796165776e32726d386d1a0e0a0475746961120631303030303012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103f3e16481ff7c9c2a677f08a30a887e5f9c14313cb624b8c5f7f955d143c81d9212040a020801180112130a0d0a04757469611205323230303010d0e80c1a4068f074601f1bb923f6d6e69d2e3fc3af145c9252eceeb0ac4fba9f661ca0428326f0080478cc969129c0074c3d97ae925de34c5f9d98a458cd47a565a2bb08cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +// The extended block is: +// 0x0000000000000000000000000000000000000000000000000000000001010000014500000026c3020a95010a92010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412720a2f63656c657374696131746b376c776a77336676616578657770687237687833333472766b67646b736d636537666b66122f63656c65737469613167616b61646d63386a73667873646c676e6d64643867773736346739796165776e32726d386d1a0e0a0475746961120631303030303012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103f3e16481ff7c9c2a677f08a30a887e5f9c14313cb624b8c5f7f955d143c81d9212040a020801180112130a0d0a04757469611205323230303010d0e80c1a4068f074601f1bb923f6d6e69d2e3fc3af145c9252eceeb0ac4fba9f661ca0428326f0080478cc969129c0074c3d97ae925de34c5f9d98a458cd47a565a2bb08cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x0000000000000000000000000000000000000000000000000000000001010000014500000026c3020a95010a92010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412720a2f63656c657374696131746b376c776a77336676616578657770687237687833333472766b67646b736d636537666b66122f63656c65737469613167616b61646d63386a73667873646c676e6d64643867773736346739796165776e32726d386d1a0e0a0475746961120631303030303012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103f3e16481ff7c9c2a677f08a30a887e5f9c14313cb624b8c5f7f955d143c81d9212040a020801180112130a0d0a04757469611205323230303010d0e80c1a4068f074601f1bb923f6d6e69d2e3fc3af145c9252eceeb0ac4fba9f661ca0428326f0080478cc969129c0074c3d97ae925de34c5f9d98a458cd47a565a2bb08cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x0000000000000000000000000000000000000000000000000000000001010000014500000026c3020a95010a92010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412720a2f63656c657374696131746b376c776a77336676616578657770687237687833333472766b67646b736d636537666b66122f63656c65737469613167616b61646d63386a73667873646c676e6d64643867773736346739796165776e32726d386d1a0e0a0475746961120631303030303012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103f3e16481ff7c9c2a677f08a30a887e5f9c14313cb624b8c5f7f955d143c81d9212040a020801180112130a0d0a04757469611205323230303010d0e80c1a4068f074601f1bb923f6d6e69d2e3fc3af145c9252eceeb0ac4fba9f661ca0428326f0080478cc969129c0074c3d97ae925de34c5f9d98a458cd47a565a2bb08cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 +// 0x0000000000000000000000000000000000000000000000000000000001010000014500000026c3020a95010a92010a1c2f636f736d6f732e62616e6b2e763162657461312e4d736753656e6412720a2f63656c657374696131746b376c776a77336676616578657770687237687833333472766b67646b736d636537666b66122f63656c65737469613167616b61646d63386a73667873646c676e6d64643867773736346739796165776e32726d386d1a0e0a0475746961120631303030303012670a500a460a1f2f636f736d6f732e63727970746f2e736563703235366b312e5075624b657912230a2103f3e16481ff7c9c2a677f08a30a887e5f9c14313cb624b8c5f7f955d143c81d9212040a020801180112130a0d0a04757469611205323230303010d0e80c1a4068f074601f1bb923f6d6e69d2e3fc3af145c9252eceeb0ac4fba9f661ca0428326f0080478cc969129c0074c3d97ae925de34c5f9d98a458cd47a565a2bb08cc0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 + +// The row roots: +// 0x00000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000001787bf77b567506b6e1d0048bfd89edd352a4fbc102e62f07cc9fe6b4cbe5ee69 +// 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7329c7d336d0140840837fc0d8eafa2403f4f6b019b602581cd9f04e28026eae + +// The column roots: +// 0x00000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000001787bf77b567506b6e1d0048bfd89edd352a4fbc102e62f07cc9fe6b4cbe5ee69 +// 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7329c7d336d0140840837fc0d8eafa2403f4f6b019b602581cd9f04e28026eae + +// The data root: 0x55cfc29fc0cd263906122d5cb859091224495b141fc0c51529612d7ab8962950 + +// The height: 3 + +// The blocks data roots used to create the commitment: +// 1. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 +// 2. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 +// 3. 0x55cfc29fc0cd263906122d5cb859091224495b141fc0c51529612d7ab8962950 +// 4. 0x3d96b7d238e7e0456f6af8e7cdf0a67bd6cf9c2089ecb559c659dcaa1f880353 + +// The nonce: 2 + +// The data root tuple root: 0xf89859a09c0f2b1bbb039618d0fe60432b8c247f7ccde97814655f2acffb3434 + +fn setup() -> ContractAddress { + let bsx_address = setup_base(); + + // store the commitment we verify against + let proof_nonce: u64 = TestFixture::data_root_tuple_root_nonce().try_into().unwrap(); + let data_commitment: u256 = TestFixture::data_root_tuple_root(); + snf::store( + bsx_address, + snf::map_entry_address( + selector!("state_data_commitments"), array![proof_nonce.into()].span() + ), + array![data_commitment.low.into(), data_commitment.high.into()].span(), + ); + snf::store(bsx_address, selector!("state_proof_nonce"), array![3].span(),); + + bsx_address +} #[test] -fn attestation_proof_test() { - let checkpoint = AttestationProof { - commit_nonce: 1, - data_root: DataRoot { height: 2, data_root: 3 }, - proof: BinaryMerkleProof { side_nodes: array![1], key: 4, num_leaves: 5 }, +fn test_verify_shares_to_data_root_tuple_root() { + let bsx_address = setup(); + let bridge = IDAOracleDispatcher { contract_address: bsx_address }; + + let data: Array = array![TestFixture::share_data()]; + let share_proofs: Array = array![ + TestFixture::get_share_to_row_root_proof() + ]; + let namespace: Namespace = TestFixture::get_namespace(); + let row_roots: Array = array![TestFixture::get_first_row_root_node()]; + let row_proofs: Array = array![ + TestFixture::get_row_root_to_data_root_proof() + ]; + let attestation_proof = AttestationProof { + commit_nonce: TestFixture::data_root_tuple_root_nonce(), + data_root: TestFixture::get_data_root_tuple(), + proof: TestFixture::get_data_root_tuple_proof() + }; + let shares_proof = SharesProof { + data, share_proofs, namespace, row_roots, row_proofs, attestation_proof }; - assert!(checkpoint.commit_nonce == 1, "stub for verifier test"); + let root: u256 = TestFixture::data_root(); + + let (valid, error) = DAVerifier::verify_shares_to_data_root_tuple_root( + bridge, shares_proof, root + ); + assert!(valid, "proofs should be valid"); + assert_eq!(error, DAVerifier::Error::NoError, "expected no error"); +} + +#[test] +fn test_verify_row_root_to_data_root_tuple_root() { + let bsx_address = setup(); + let bridge = IDAOracleDispatcher { contract_address: bsx_address }; + + let attestation_proof = AttestationProof { + commit_nonce: TestFixture::data_root_tuple_root_nonce(), + data_root: TestFixture::get_data_root_tuple(), + proof: TestFixture::get_data_root_tuple_proof() + }; + + let (valid, error) = DAVerifier::verify_row_root_to_data_root_tuple_root( + bridge: bridge, + row_root: TestFixture::get_first_row_root_node(), + row_proof: TestFixture::get_row_root_to_data_root_proof(), + attestation_proof: attestation_proof, + root: TestFixture::data_root() + ); + assert!(valid, "proofs should be valid"); + assert_eq!(error, DAVerifier::Error::NoError, "expected no error"); +} + +#[test] +fn test_verify_multi_row_roots_to_data_root_tuple_root() { + let bsx_address = setup(); + let bridge = IDAOracleDispatcher { contract_address: bsx_address }; + + let row_roots: Span = array![TestFixture::get_first_row_root_node()].span(); + let row_proofs: Span = array![TestFixture::get_row_root_to_data_root_proof()] + .span(); + let attestation_proof = AttestationProof { + commit_nonce: TestFixture::data_root_tuple_root_nonce(), + data_root: TestFixture::get_data_root_tuple(), + proof: TestFixture::get_data_root_tuple_proof() + }; + let root: u256 = TestFixture::data_root(); + + let (valid, error) = DAVerifier::verify_multi_row_roots_to_data_root_tuple_root( + bridge, row_roots, row_proofs, attestation_proof, root + ); + assert!(valid, "proofs should be valid"); + assert_eq!(error, DAVerifier::Error::NoError, "expected no error"); +} + +#[test] +fn test_compute_square_size_from_row_proof() { + // check that the merkle proof is valid + let (valid_merkle_proof, error) = binary_merkle_tree::verify( + root: TestFixture::data_root(), + proof: @TestFixture::get_row_root_to_data_root_proof(), + data: @TestFixture::first_row_root() + ); + assert!(valid_merkle_proof, "merkle proof should be valid"); + assert_eq!(error, binary_merkle_tree::ErrorCodes::NoError, "expected no error"); + + // check that the computed square size is correct + let expected_square_size: u256 = 1; + let (actual_square_size, error) = DAVerifier::compute_square_size_from_row_proof( + TestFixture::get_row_root_to_data_root_proof() + ); + assert_eq!(expected_square_size, actual_square_size, "square size mismatch"); + assert_eq!(error, DAVerifier::Error::NoError, "compute square size from row proof failed"); +} + +#[test] +fn test_compute_square_size_from_share_proof() { + let data: Span = array![TestFixture::share_data()].span(); + + // check that the merkle proof is valid + let valid_merkle_proof = NamespaceMerkleTree::verify_multi( + root: TestFixture::get_first_row_root_node(), + proof: @TestFixture::get_share_to_row_root_proof(), + namespace: TestFixture::get_namespace(), + data: data + ); + assert!(valid_merkle_proof, "merkle proof should be valid"); + + // check that the computed square size is correct + let expected_square_size: u256 = 1; + let actual_square_size = DAVerifier::compute_square_size_from_share_proof( + TestFixture::get_share_to_row_root_proof() + ); + assert_eq!(expected_square_size, actual_square_size, "square size mismatch"); +} + +/// Contains the necessary information to create proofs for the token +/// transfer transaction that happened on Celestia. It represents the data mentioned in +/// the comment at the beginning of this file. +mod TestFixture { + use alexandria_bytes::{Bytes, BytesTrait}; + use alexandria_encoding::sol_abi::{SolBytesTrait, SolAbiEncodeTrait}; + use blobstream_sn::interfaces::DataRoot; + use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; + use blobstream_sn::tree::namespace::Namespace; + use blobstream_sn::tree::namespace::merkle_tree::{NamespaceNode, NamespaceMerkleMultiproof}; + + /// The share containing the token transfer transaction on Celestia. + fn share_data() -> Bytes { + BytesTrait::new( + 512, + array![ + 0x00000000000000000000000000000000, + 0x00000000000000000000000001010000, + 0x014500000026c3020a95010a92010a1c, + 0x2f636f736d6f732e62616e6b2e763162, + 0x657461312e4d736753656e6412720a2f, + 0x63656c657374696131746b376c776a77, + 0x33667661657865777068723768783333, + 0x3472766b67646b736d636537666b6612, + 0x2f63656c65737469613167616b61646d, + 0x63386a73667873646c676e6d64643867, + 0x773736346739796165776e32726d386d, + 0x1a0e0a04757469611206313030303030, + 0x12670a500a460a1f2f636f736d6f732e, + 0x63727970746f2e736563703235366b31, + 0x2e5075624b657912230a2103f3e16481, + 0xff7c9c2a677f08a30a887e5f9c14313c, + 0xb624b8c5f7f955d143c81d9212040a02, + 0x0801180112130a0d0a04757469611205, + 0x323230303010d0e80c1a4068f074601f, + 0x1bb923f6d6e69d2e3fc3af145c9252ec, + 0xeeb0ac4fba9f661ca0428326f0080478, + 0xcc969129c0074c3d97ae925de34c5f9d, + 0x98a458cd47a565a2bb08cc0000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + 0x00000000000000000000000000000000, + ] + ) + } + + /// The first EDS row root. + fn first_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0x00000000000000000000000000000000, + 0x00000000000000000000000001000000, + 0x00000000000000000000000000000000, + 0x00000000000000000001787bf77b5675, + 0x06b6e1d0048bfd89edd352a4fbc102e6, + 0x2f07cc9fe6b4cbe5ee69000000000000, + ] + ) + } + + /// The second EDS row root. + fn second_row_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffff7329c7d336d0, + 0x140840837fc0d8eafa2403f4f6b019b6, + 0x02581cd9f04e28026eae000000000000, + ] + ) + } + + /// The first EDS column root. + fn first_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0x00000000000000000000000000000000, + 0x00000000000000000000000001000000, + 0x00000000000000000000000000000000, + 0x00000000000000000001787bf77b5675, + 0x06b6e1d0048bfd89edd352a4fbc102e6, + 0x2f07cc9fe6b4cbe5ee69000000000000, + ] + ) + } + + /// The second EDS column root. + fn second_column_root() -> Bytes { + BytesTrait::new( + 90, + array![ + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffffffffffffffff, + 0xffffffffffffffffffff7329c7d336d0, + 0x140840837fc0d8eafa2403f4f6b019b6, + 0x02581cd9f04e28026eae000000000000, + ] + ) + } + + /// The data root of the block containing the token transfer transaction. + fn data_root() -> u256 { + 0x55cfc29fc0cd263906122d5cb859091224495b141fc0c51529612d7ab8962950 + } + + /// The height of the block containing the submitted token transfer transaction. + fn height() -> felt252 { + 3 + } + + /// The data root tuple root committing to the Celestia block. + fn data_root_tuple_root() -> u256 { + 0xf89859a09c0f2b1bbb039618d0fe60432b8c247f7ccde97814655f2acffb3434 + } + + /// The data root tuple root nonce in the Blobstream contract. + fn data_root_tuple_root_nonce() -> u256 { + 2 + } + + /// The data root tuple to data root tuple root proof side nodes. + fn data_root_proof_side_nodes() -> Array { + array![ + 0xb5d4d27ec6b206a205bf09dde3371ffba62e5b53d27bbec4255b7f4f27ef5d90, + 0x406e22ba94989ca721453057a1391fc531edb342c86a0ab4cc722276b54036ec + ] + } + + /// Shares to data root proof side nodes. + fn share_to_data_root_proof_side_nodes() -> Array { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + array![ + NamespaceNode { + min: n, + max: n, + digest: 0x0ec8148c743a4a4db384f40f487cae2fd1ca0d18442d1f162916bdf1cc61b679 + } + ] + } + + /// Row root to data root proof side nodes. + fn row_root_to_data_root_proof_side_nodes() -> Array { + array![ + 0x5bc0cf3322dd5c9141a2dcd76947882351690c9aec61015802efc6742992643f, + 0xff576381b02abadc50e414f6b4efcae31091cd40a5aba75f56be52d1bb2efcae + ] + } + + /// The share's namespace. + fn get_namespace() -> Namespace { + Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000001>() + } + } + + /// The data root tuple of the block containing the token transfer transaction. + fn get_data_root_tuple() -> DataRoot { + DataRoot { height: height(), data_root: data_root() } + } + + /// The data root tuple to data root tuple root proof. + fn get_data_root_tuple_proof() -> BinaryMerkleProof { + BinaryMerkleProof { side_nodes: data_root_proof_side_nodes(), key: 2, num_leaves: 4 } + } + + /// The first EDS row root. + fn get_first_row_root_node() -> NamespaceNode { + let n = Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000001>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x787bf77b567506b6e1d0048bfd89edd352a4fbc102e62f07cc9fe6b4cbe5ee69 + } + } + + /// The second EDS row root. + fn get_second_row_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x7329c7d336d0140840837fc0d8eafa2403f4f6b019b602581cd9f04e28026eae + } + } + + /// The first EDS column root. + fn get_first_column_root_node() -> NamespaceNode { + let n = Namespace { + version: 0x00, + id: bytes31_const::<0x00000000000000000000000000000000000000000000000000000001>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x787bf77b567506b6e1d0048bfd89edd352a4fbc102e62f07cc9fe6b4cbe5ee69 + } + } + + /// The second EDS column root. + fn get_second_column_root_node() -> NamespaceNode { + let n = Namespace { + version: 0xff, + id: bytes31_const::<0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff>() + }; + NamespaceNode { + min: n, + max: n, + digest: 0x7329c7d336d0140840837fc0d8eafa2403f4f6b019b602581cd9f04e28026eae + } + } + + /// The shares to row root proof. + fn get_share_to_row_root_proof() -> NamespaceMerkleMultiproof { + NamespaceMerkleMultiproof { + begin_key: 0, end_key: 1, side_nodes: share_to_data_root_proof_side_nodes() + } + } + + /// Row root to data root proof. + fn get_row_root_to_data_root_proof() -> BinaryMerkleProof { + BinaryMerkleProof { + side_nodes: row_root_to_data_root_proof_side_nodes(), key: 0, num_leaves: 4 + } + } } diff --git a/src/verifier/types.cairo b/src/verifier/types.cairo index f4c763e..c4bf386 100644 --- a/src/verifier/types.cairo +++ b/src/verifier/types.cairo @@ -5,6 +5,9 @@ use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; use blobstream_sn::tree::namespace::Namespace; use blobstream_sn::tree::namespace::merkle_tree::{NamespaceNode, NamespaceMerkleMultiproof}; +// Data needed to verify that some shares, posted to the Celestia +// network, were committed to by the Blobstream smart contract. +#[derive(Drop)] struct SharesProof { // The shares that were committed to. data: Array,