From 23dedec77a7bfe2552756ad8838a990c1c8c9306 Mon Sep 17 00:00:00 2001 From: 0xK2 <65908739+thomas192@users.noreply.github.com> Date: Mon, 18 Mar 2024 17:45:57 +0100 Subject: [PATCH] feat: verify attestation (#119) Verify attestation + test --- src/blobstreamx.cairo | 15 ++++++++---- src/interfaces.cairo | 4 ++-- src/tests/test_blobstreamx.cairo | 39 +++++++++++++++++++++++++++++++- 3 files changed, 50 insertions(+), 8 deletions(-) diff --git a/src/blobstreamx.cairo b/src/blobstreamx.cairo index 6327f55..00f7756 100644 --- a/src/blobstreamx.cairo +++ b/src/blobstreamx.cairo @@ -5,6 +5,7 @@ mod blobstreamx { DataRoot, TendermintXErrors, IBlobstreamX, IDAOracle, ITendermintX }; use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; + use blobstream_sn::tree::binary::merkle_tree; use core::starknet::event::EventEmitter; use core::traits::Into; use openzeppelin::access::ownable::OwnableComponent; @@ -135,7 +136,7 @@ mod blobstreamx { #[abi(embed_v0)] impl IDAOracleImpl of IDAOracle { fn verify_attestation( - self: @ContractState, proof_nonce: u64, root: DataRoot, proof: BinaryMerkleProof + self: @ContractState, proof_nonce: u64, data_root: DataRoot, proof: BinaryMerkleProof ) -> bool { assert(!self.frozen.read(), Errors::ContractFrozen); @@ -144,11 +145,15 @@ mod blobstreamx { } // load the tuple root at the given index from storage. - let _data_root = self.state_data_commitments.read(proof_nonce); + let root: u256 = self.state_data_commitments.read(proof_nonce); - // return isProofValid; - // TODO(#69 + #24): BinaryMerkleTree.verify(root, _proof, abi.encode(_tuple)); - false + let mut data_root_bytes = BytesTrait::new_empty(); + data_root_bytes.append_felt252(data_root.height); + 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/interfaces.cairo b/src/interfaces.cairo index 38995ce..e614281 100644 --- a/src/interfaces.cairo +++ b/src/interfaces.cairo @@ -35,10 +35,10 @@ trait ITendermintX { trait IDAOracle { /// Verify a Data Availability attestation. /// * `proof_nonce` - Nonce of the tuple root to prove against. - /// * `root` - Data root tuple to prove inclusion of. + /// * `data_root` - Data root tuple to prove inclusion of. /// * `proof` - Binary Merkle tree proof that `tuple` is in the root at `_tupleRootNonce`. fn verify_attestation( - self: @TContractState, proof_nonce: u64, root: DataRoot, proof: BinaryMerkleProof + self: @TContractState, proof_nonce: u64, data_root: DataRoot, proof: BinaryMerkleProof ) -> bool; } diff --git a/src/tests/test_blobstreamx.cairo b/src/tests/test_blobstreamx.cairo index c525846..fe05962 100644 --- a/src/tests/test_blobstreamx.cairo +++ b/src/tests/test_blobstreamx.cairo @@ -2,11 +2,12 @@ use alexandria_bytes::{Bytes, BytesTrait}; use blobstream_sn::blobstreamx::blobstreamx; use blobstream_sn::interfaces::{ IBlobstreamXDispatcher, IBlobstreamXDispatcherTrait, Validator, ITendermintXDispatcher, - ITendermintXDispatcherTrait + ITendermintXDispatcherTrait, DataRoot, IDAOracleDispatcher, IDAOracleDispatcherTrait }; use blobstream_sn::tests::common::{ setup_base, setup_spied, setup_succinct_gateway, TEST_START_BLOCK, TEST_END_BLOCK, TEST_HEADER, }; +use blobstream_sn::tree::binary::merkle_proof::BinaryMerkleProof; use openzeppelin::tests::utils::constants::OWNER; use snforge_std as snf; use snforge_std::{CheatTarget, EventSpy, EventAssertions}; @@ -44,6 +45,42 @@ fn blobstreamx_constructor_vals() { assert!(bsx.get_state_proof_nonce() == 1, "state proof nonce invalid"); } +#[test] +fn test_verify_attestation() { + let bsx_address = setup_base(); + let bsx = IDAOracleDispatcher { contract_address: bsx_address }; + + // Test data: https://github.com/celestiaorg/blobstream-contracts/blob/3a552d8f7bfbed1f3175933260e6e440915d2da4/src/lib/verifier/test/DAVerifier.t.sol#L295 + + // Store the commitment we verify against. + let proof_nonce: u64 = 2; + let data_commitment: u256 = 0xf89859a09c0f2b1bbb039618d0fe60432b8c247f7ccde97814655f2acffb3434; + 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(),); + + // Construct a valid proof. + let data = DataRoot { + height: 3, data_root: 0x55cfc29fc0cd263906122d5cb859091224495b141fc0c51529612d7ab8962950, + }; + let side_nodes: Array = array![ + 0xb5d4d27ec6b206a205bf09dde3371ffba62e5b53d27bbec4255b7f4f27ef5d90, + 0x406e22ba94989ca721453057a1391fc531edb342c86a0ab4cc722276b54036ec, + ]; + let key: u256 = 2; + let num_leaves: u256 = 4; + let proof = BinaryMerkleProof { side_nodes, key, num_leaves, }; + + let is_proof_valid: bool = bsx.verify_attestation(proof_nonce, data, proof); + assert!(is_proof_valid, "valid proof should be accepted"); +} + + #[test] fn blobstreamx_fulfill_commit_header_range() { let bsx = setup_blobstreamx();