diff --git a/scripts/request-data-commitments.sh b/scripts/request-data-commitments.sh index 5b08075..a8412c4 100755 --- a/scripts/request-data-commitments.sh +++ b/scripts/request-data-commitments.sh @@ -12,55 +12,87 @@ STATE_PROOF_NONCE_SLOT="0x000000000000000000000000000000000000000000000000000000 STATE_DATA_COMMITMENT_MAP_SLOT="0x00000000000000000000000000000000000000000000000000000000000000fe" # Optional arguments -# TODO: To public api? L1_RPC_URL="https://sepolia.infura.io/v3/bed8a8401c894421bd7cd31050e7ced6" STARKNET_RPC_URL="https://starknet-sepolia.infura.io/v3/bed8a8401c894421bd7cd31050e7ced6" BLOBSTREAMX_L1_ADDRESS="0x48B257EC1610d04191cC2c528d0c940AdbE1E439" -#TODO +#TODO : Change address to the Sepolia address once it is deployed BLOBSTREAMX_STARKNET_ADDRESS="0x48B257EC1610d04191cC2c528d0c940AdbE1E439" +NO_SEND=false +VERBOSE=false +WAIT=true + + SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )" WORK_DIR=$SCRIPT_DIR/.. -#TODO: w/o webhook, verbose, w/o header access -#TODO: resume from id - display_help() { echo "Usage: $0 [option...] {arguments...}" echo + echo " -b, --blobstreamx-l1 ADDR BlobstreamX contract address on L1" + echo " (default: $BLOBSTREAMX_L1_ADDRESS (SEPOLIA))" + echo " -B, --blobstreamx-starknet ADDR BlobstreamX contract address on Starknet" + echo " (default: $BLOBSTREAMX_STARKNET_ADDRESS (SEPOLIA))" + echo " -r, --l1-rpc URL URL of the L1 RPC server" + echo " (default: $L1_RPC_URL)" + echo " -s, --starknet-rpc URL URL of the Starknet RPC server" + echo " (default: $STARKNET_RPC_URL)" + + echo echo " -h, --help display help" - echo " -r, --l1-rpc=URL URL of the L1 RPC server (default: $L1_RPC_URL)" - echo " -s, --starknet-rpc=URL URL of the Starknet RPC server (default: $STARKNET_RPC_URL)" - echo " -b, --blobstreamx-l1=ADDR BlobstreamX contract address on L1 (default: $BLOBSTREAMX_L1_ADDRESS)" - echo " -B, --blobstreamx-starknet=ADDR BlobstreamX contract address on Starknet (default: $BLOBSTREAMX_STARKNET_ADDRESS)" + echo " -n, --no-send Do not send the batch query to Herodotus, only print the query" + echo " -N, --no-wait Do not wait for the state proof nonce and data commitments to be relayed" + echo " -v, --verbose verbose output" echo echo "Example: $0" } -#TODO: Add support for optional arguments +# Transform long options to short ones +for arg in "$@"; do + shift + case "$arg" in + "--blobstreamx-l1") set -- "$@" "-b" ;; + "--blobstreamx-starknet") set -- "$@" "-B" ;; + "--l1-rpc") set -- "$@" "-r" ;; + "--starknet-rpc") set -- "$@" "-s" ;; + "--help") set -- "$@" "-h" ;; + "--no-send") set -- "$@" "-n" ;; + "--no-wait") set -- "$@" "-N" ;; + "--verbose") set -- "$@" "-v" ;; + *) set -- "$@" "$arg" + esac +done # Parse command line arguments -while getopts ":hr:-:" opt; do +while getopts ":hnvNb:B:r:s:-:" opt; do case ${opt} in - - ) - case "${OPTARG}" in - help ) - display_help - exit 0 - ;; - * ) - echo "Invalid option: --$OPTARG" 1>&2 - display_help - exit 1 - ;; - esac - ;; h ) display_help exit 0 ;; + n ) + NO_SEND=true + ;; + v ) + VERBOSE=true + ;; + N ) + WAIT=false + ;; + b ) + BLOBSTREAMX_L1_ADDRESS="$OPTARG" + ;; + B ) + BLOBSTREAMX_STARKNET_ADDRESS="$OPTARG" + ;; + r ) + L1_RPC_URL="$OPTARG" + ;; + s ) + STARKNET_RPC_URL="$OPTARG" + ;; \? ) echo "Invalid option: $OPTARG" 1>&2 display_help @@ -74,7 +106,8 @@ while getopts ":hr:-:" opt; do esac done -if [ -z "$HERODOTUS_API_KEY" ]; then +# Check if HERODOTUS_API_KEY is set if not set and no-send is not set, exit +if [ -z "$HERODOTUS_API_KEY" ] && [ "$NO_SEND" = false ]; then echo "HERODOTUS_API_KEY is not set. Get your API key from https://dashboard.herodotus.dev by signing up with your GitHub." exit 1 fi @@ -84,36 +117,30 @@ L1_BLOCK_NUM_RES=$(curl $L1_RPC_URL \ -H "Content-Type: application/json" \ -d '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":1}' 2>/dev/null) L1_BLOCK_NUM=$(echo $L1_BLOCK_NUM_RES | jq -r '.result') -echo "Latest L1 block number: $L1_BLOCK_NUM" -# echo -# echo "Getting current Starknet block number..." -# curl $STARKNET_RPC_URL \ -# -X POST \ -# -H "Content-Type: application/json" \ -# -d '{"jsonrpc":"2.0","method":"starknet_blockNumber","params":[],"id":1}' - -#TODO: latest -> block number LATEST_PROOF_NONCE_RES=$(curl $L1_RPC_URL \ -X POST \ -H "Content-Type: application/json" \ - -d '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'"$BLOBSTREAMX_L1_ADDRESS"'","'"$STATE_PROOF_NONCE_SLOT"'","latest"],"id":1}' 2>/dev/null) + -d '{"jsonrpc":"2.0","method":"eth_getStorageAt","params":["'"$BLOBSTREAMX_L1_ADDRESS"'","'"$STATE_PROOF_NONCE_SLOT"'","'"$L1_BLOCK_NUM"'"],"id":1}' 2>/dev/null) LATEST_PROOF_NONCE=$(echo $LATEST_PROOF_NONCE_RES | jq -r '.result') -echo "Latest L1 state_proofNonce: $LATEST_PROOF_NONCE" -# TODO : This or latest option for just one proofNonce +#TODO: Do a call to get_state_proof_nonce() once the function is deployed & replace hardcoded #STARKNET_PROOF_NONCE_RES=$(curl $STARKNET_RPC_URL \ # -X POST \ # -H "Content-Type: application/json" \ # -d '{"jsonrpc":"2.0","method":"starknet_getState","params":["'"$BLOBSTREAMX_STARKNET_ADDRESS"'",'"$LATEST_PROOF_NONCE"'],"id":1}' 2>/dev/null) -STARKNET_PROOF_NONCE_RES=$(echo '{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000006a4"}') +STARKNET_PROOF_NONCE_RES=$(echo '{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000006cb"}') STARKNET_PROOF_NONCE=$(echo $STARKNET_PROOF_NONCE_RES | jq -r '.result') -echo "Latest Starknet state_proofNonce: $STARKNET_PROOF_NONCE" + +if [ "$VERBOSE" = true ]; then + echo "Latest L1 block number: $L1_BLOCK_NUM" + echo "Latest L1 state_proofNonce: $LATEST_PROOF_NONCE" + echo "Latest Starknet state_proofNonce: $STARKNET_PROOF_NONCE" +fi DATA_COMMITMENT_SLOTS='' -# TODO: Think about edge cases # Loop through each missing state_proofNonce to build the batch query slots -for ((i = STARKNET_PROOF_NONCE; i <= LATEST_PROOF_NONCE - 1; i++)); do +for ((i = $STARKNET_PROOF_NONCE; i <= LATEST_PROOF_NONCE - 1; i++)); do # Data commitment slot for each proofNonce is located at # keccak256(abi.encode(proofNonce, STATE_DATA_COMMITMENT_MAP_SLOT)) DATA_COMMITMENT_ENCODED_SLOT=$(printf "%064x%064x" $i $STATE_DATA_COMMITMENT_MAP_SLOT) @@ -165,25 +192,38 @@ HERODOTUS_QUERY='{ HERODOTUS_QUERY=$(echo $HERODOTUS_QUERY | jq '.') HERODOTUS_QUERY_URL=$(echo $HERODOTUS_API_URL | sed 's/\/$//')'/submit-batch-query?apiKey='$HERODOTUS_API_KEY +if [ "$VERBOSE" = true ] || [ "$NO_SEND" = true ]; then + echo + echo "Batch query to Herodotus API:" + echo "$HERODOTUS_QUERY" +fi + +if [ "$NO_SEND" = true ]; then + exit 0 +fi + echo echo "Submitting batch query to Herodotus API..." -echo "Query: $HERODOTUS_QUERY" -curl -X 'POST' \ +HERODOTUS_ID=$(curl -X 'POST' \ $HERODOTUS_QUERY_URL \ -H 'accept: application/json' \ -H 'Content-Type: application/json' \ - -d "$HERODOTUS_QUERY" + -d "$HERODOTUS_QUERY" 2>/dev/null) +echo "Batch query submitted. Herodotus Internal ID: $HERODOTUS_ID" + +if [ "$WAIT" = false ]; then + echo + echo "Query fulfillment can take some time, please wait for state proof nonce and data commitments to be relayed." + exit 0 +fi -#TODO: Wait option # Wait for state proof nonce slot to be relayed echo echo "Waiting for state proof nonce slot $STATE_PROOF_NONCE_SLOT to be relayed..." -$SCRIPT_DIR/wait-for-herodotus-fulfill.sh -b $L1_BLOCK_NUM_DEC -a $BLOBSTREAMX_L1_ADDRESS -S $STATE_PROOF_NONCE_SLOT +$SCRIPT_DIR/wait-for-herodotus-fulfill.sh -b $L1_BLOCK_NUM_DEC -a $BLOBSTREAMX_L1_ADDRESS -S $STATE_PROOF_NONCE_SLOT $([ "$VERBOSE" = true ] && echo "-v") # Loop thru each data commitment slot to wait for the data to be relayed (comma separated) for slot in $(echo $DATA_COMMITMENT_SLOTS | tr ',' '\n' | tr -d '"'); do echo echo "Waiting for data commitment at slot $slot to be relayed..." - $SCRIPT_DIR/wait-for-herodotus-fulfill.sh -b $L1_BLOCK_NUM_DEC -a $BLOBSTREAMX_L1_ADDRESS -S $slot + $SCRIPT_DIR/wait-for-herodotus-fulfill.sh -b $L1_BLOCK_NUM_DEC -a $BLOBSTREAMX_L1_ADDRESS -S $slot $([ "$VERBOSE" = true ] && echo "-v") done - -#TODO: do call to herodotus reg to update latest l1 block?, PR for verifying attestations, use herodotus to get data commitment and verify attestation from DC diff --git a/scripts/wait-for-herodotus-fulfill.sh b/scripts/wait-for-herodotus-fulfill.sh index 3308528..79554c3 100755 --- a/scripts/wait-for-herodotus-fulfill.sh +++ b/scripts/wait-for-herodotus-fulfill.sh @@ -4,72 +4,66 @@ # Monitors the Herodotus Fact registry on Starknet for the requested slot values. # Constants +HERODOTUS_GET_SLOT_VALUE_SELECTOR="0x01d02b5043fe08831f4d75f1582080c274c6b4d5245fae933747a6990009adff" # Optional arguments STARKNET_RPC_URL="https://starknet-sepolia.infura.io/v3/bed8a8401c894421bd7cd31050e7ced6" HERODOTUS_FACT_REGISTRY="0x07d3550237ecf2d6ddef9b78e59b38647ee511467fe000ce276f245a006b40bc" +VERBOSE=false + display_help() { echo "Usage: $0 [option...] {arguments...}" echo - echo " -h, --help display help" - echo " -s, --starknet-rpc=URL URL of the Starknet RPC server (default: $STARKNET_RPC_URL)" - echo " -F, --herodotus-fact-registry=ADDRESS Address of the Herodotus Fact registry on Starknet (default: $HERODOTUS_FACT_REGISTRY)" + echo " -s, --starknet-rpc URL URL of the Starknet RPC server" + echo " (default: $STARKNET_RPC_URL)" + echo " -F, --herodotus-fact-registry ADDRESS Address of the Herodotus Fact registry on Starknet" + echo " (default: $HERODOTUS_FACT_REGISTRY (SN_SEPOLIA))" echo - echo " -b, --block-number=NUMBER Block number for the slot fact (required)" - echo " -a, --address=ADDRESS Address of the contract for the slot fact (required)" - echo " -S, --slot=NUMBER Slot number for the slot fact (required)" + echo " -b, --block-number NUMBER Block number for the slot fact (required)" + echo " -a, --address ADDRESS Address of the contract for the slot fact (required)" + echo " -S, --slot NUMBER Slot number for the slot fact - Hex (required)" + + echo + echo " -h, --help display help" + echo " -v, --verbose display verbose output" echo echo "Example: $0" } -#TODO: Add support for optional arguments -#TODO: Add support for required arguments -#TODO: parse arg formats +# Transform long options to short ones +for arg in "$@"; do + shift + case "$arg" in + "--starknet-rpc") set -- "$@" "-s" ;; + "--herodotus-fact-registry") set -- "$@" "-F" ;; + "--block-number") set -- "$@" "-b" ;; + "--address") set -- "$@" "-a" ;; + "--slot") set -- "$@" "-S" ;; + "--help") set -- "$@" "-h" ;; + "--verbose") set -- "$@" "-v" ;; + *) set -- "$@" "$arg" + esac +done # Parse command line arguments -while getopts ":hb:a:S:-:" opt; do +while getopts ":hvs:F:b:a:S:" opt; do case ${opt} in - - ) - case "${OPTARG}" in - help ) - display_help - exit 0 - ;; - block-number ) - BLOCK_NUMBER="${!OPTIND}" - OPTIND=$((OPTIND + 1)) - ;; - block-number=* ) - BLOCK_NUMBER="${OPTARG#*=}" - ;; - address ) - ADDRESS="${!OPTIND}" - OPTIND=$((OPTIND + 1)) - ;; - address=* ) - ADDRESS="${OPTARG#*=}" - ;; - slot ) - SLOT="${!OPTIND}" - OPTIND=$((OPTIND + 1)) - ;; - slot=* ) - SLOT="${OPTARG#*=}" - ;; - * ) - echo "Invalid option: --$OPTARG" 1>&2 - display_help - exit 1 - ;; - esac - ;; h ) display_help exit 0 ;; + v ) + VERBOSE=true + ;; + s ) + STARKNET_RPC_URL="${OPTARG}" + ;; + F ) + HERODOTUS_FACT_REGISTRY="${OPTARG}" + ;; b ) BLOCK_NUMBER="${OPTARG}" ;; @@ -92,13 +86,18 @@ while getopts ":hb:a:S:-:" opt; do esac done +if [ -z $BLOCK_NUMBER ] || [ -z $ADDRESS ] || [ -z $SLOT ]; then + echo "Missing required arguments: block-number, address, slot" 1>&2 + display_help + exit 1 +fi + # Convert block number to 32-byte hex BLOCK_NUMBER=$(printf "%064x" $BLOCK_NUMBER) # Get the low and high 16 bytes BLOCK_NUMBER_HIGH=0x${BLOCK_NUMBER:0:32} BLOCK_NUMBER_LOW=0x${BLOCK_NUMBER:32:32} -# TODO: allow slots of non hex values # Left pad the slot value to 32 bytes if [[ $SLOT == 0x* ]]; then SLOT=${SLOT:2} @@ -110,7 +109,6 @@ done SLOT_HIGH=0x${SLOT:0:32} SLOT_LOW=0x${SLOT:32:32} -#TODO: Compute entry point selector from get_slot_value HERODOTUS_FACT_QUERY='{ "id": 1, "jsonrpc": "2.0", @@ -124,16 +122,20 @@ HERODOTUS_FACT_QUERY='{ "'$SLOT_LOW'", "'$SLOT_HIGH'" ], - "entry_point_selector": "0x01d02b5043fe08831f4d75f1582080c274c6b4d5245fae933747a6990009adff", + "entry_point_selector": "'$HERODOTUS_GET_SLOT_VALUE_SELECTOR'", "contract_address": "'$HERODOTUS_FACT_REGISTRY'" }, "pending" ] }' -echo "Query: $HERODOTUS_FACT_QUERY" -echo -echo "Waiting for Herodotus to fulfill the slot fact..." +if [ $VERBOSE == true ]; then + echo "Query: $HERODOTUS_FACT_QUERY" + + echo + echo "Waiting for Herodotus to fulfill the slot fact..." +fi + RES='{"jsonrpc":"2.0","id":1,"result":["0x1"]}' while [ $(echo $RES | jq -r '.result[0]') == "0x1" ]; do RES=$(curl $STARKNET_RPC_URL \ @@ -158,5 +160,3 @@ while [ $(echo $RES | jq -r '.result[0]') == "0x1" ]; do fi sleep 5 done - -#TODO: verbose, ... diff --git a/src/blobstreamx.cairo b/src/blobstreamx.cairo index fb13e3d..899bb61 100644 --- a/src/blobstreamx.cairo +++ b/src/blobstreamx.cairo @@ -36,6 +36,7 @@ mod blobstreamx { next_header_function_id: u256, frozen: bool, herodotus_facts_registry: ContractAddress, + blobstreamx_l1_contract: felt252, #[substorage(v0)] ownable: OwnableComponent::Storage, #[substorage(v0)] @@ -116,7 +117,8 @@ mod blobstreamx { header: u256, header_range_function_id: u256, next_header_function_id: u256, - herodotus_facts_registry: ContractAddress + herodotus_facts_registry: ContractAddress, + blobstreamx_l1_contract: felt252 ) { self.data_commitment_max.write(1000); self.gateway.write(gateway); @@ -128,6 +130,7 @@ mod blobstreamx { self.next_header_function_id.write(next_header_function_id); self.frozen.write(false); self.herodotus_facts_registry.write(herodotus_facts_registry); + self.blobstreamx_l1_contract.write(blobstreamx_l1_contract); } #[abi(embed_v0)] @@ -226,6 +229,13 @@ mod blobstreamx { self.ownable.assert_only_owner(); self.herodotus_facts_registry.write(facts_registry); } + fn get_blobstreamx_l1_contract(self: @ContractState) -> felt252 { + self.blobstreamx_l1_contract.read() + } + fn set_blobstreamx_l1_contract(ref self: ContractState, l1_contract: felt252) { + self.ownable.assert_only_owner(); + self.blobstreamx_l1_contract.write(l1_contract); + } /// Request a header_range proof for the next header hash and a data commitment for the block range [latest_block, _target_block). /// Used to skip from the latest block to the target block. @@ -395,25 +405,21 @@ mod blobstreamx { self.latest_block.write(next_block); } + /// This assumes all existing data_commitments mappings match L1 Blobstream fn update_data_commitments_from_facts(ref self: ContractState, l1_block: u256) { - // TODO: block_height_to_header_hash, latest_block, events - // TODO: Issue where this will only work if we are using the same data commitments from l1 from genesis - // Could be resolved if we only use the latest data commitments from l1 and check latest block assert(!self.frozen.read(), Errors::ContractFrozen); let herodotus_facts_registry = IEVMFactsRegistryMockDispatcher { contract_address: self.get_herodotus_facts_registry() }; - // TODO: use non-mock, and non-hardcoded values - let blobstreamx_l1_contract: felt252 = 0x48B257EC1610d04191cC2c528d0c940AdbE1E439; - // Get the proof nonce for the new state data commitments let blobstreamx_l1_proof_nonce_slot: u256 = 0xfc; let new_state_proof_nonce = herodotus_facts_registry - .get_slot_value(blobstreamx_l1_contract, l1_block, blobstreamx_l1_proof_nonce_slot); + .get_slot_value( + self.blobstreamx_l1_contract.read(), l1_block, blobstreamx_l1_proof_nonce_slot + ); assert!(new_state_proof_nonce.is_some(), "No proof nonce found for block {}", l1_block); - // TODO: error handling on try_into let new_state_proof_nonce: u64 = new_state_proof_nonce.unwrap().try_into().unwrap(); assert!( new_state_proof_nonce > self.get_state_proof_nonce(), @@ -421,24 +427,24 @@ mod blobstreamx { l1_block ); - //// Loop though all the new state data commitments + // Loop though all the new state data commitments let blobstreamx_l1_data_commitment_map_slot: u256 = 0xfe; - let mut current_dc_id = self.get_state_proof_nonce(); - while current_dc_id < new_state_proof_nonce { + let mut current_proof_nonce = self.get_state_proof_nonce(); + while current_proof_nonce < new_state_proof_nonce { let mut dc_slot_encoded: Bytes = BytesTrait::new_empty(); - dc_slot_encoded.append_u256(current_dc_id.into()); + dc_slot_encoded.append_u256(current_proof_nonce.into()); dc_slot_encoded.append_u256(blobstreamx_l1_data_commitment_map_slot); let dc_slot: u256 = dc_slot_encoded.keccak(); let data_commitment = herodotus_facts_registry - .get_slot_value(blobstreamx_l1_contract, l1_block, dc_slot); + .get_slot_value(self.blobstreamx_l1_contract.read(), l1_block, dc_slot); assert!( data_commitment.is_some(), "No data commitment found for block {} and proof nonce {}", l1_block, - current_dc_id + current_proof_nonce ); - self.state_data_commitments.write(current_dc_id, data_commitment.unwrap()); - current_dc_id += 1; + self.state_data_commitments.write(current_proof_nonce, data_commitment.unwrap()); + current_proof_nonce += 1; }; self.state_proof_nonce.write(new_state_proof_nonce); } diff --git a/src/interfaces.cairo b/src/interfaces.cairo index 1c06be1..7ebbf36 100644 --- a/src/interfaces.cairo +++ b/src/interfaces.cairo @@ -66,6 +66,9 @@ trait IBlobstreamX { // Address to the Herodotus Fact Registry contract. fn get_herodotus_facts_registry(self: @TContractState) -> ContractAddress; fn set_herodotus_facts_registry(ref self: TContractState, facts_registry: ContractAddress); + // L1 Address to the BlobstreamX proxy contract. + fn get_blobstreamx_l1_contract(self: @TContractState) -> felt252; + fn set_blobstreamx_l1_contract(ref self: TContractState, l1_contract: felt252); // Prove the validity of the header at the target block and a data commitment for the block range [latestBlock, _targetBlock). fn request_header_range(ref self: TContractState, _target_block: u64); // Commits the new header at targetBlock and the data commitment for the block range [trustedBlock, targetBlock). diff --git a/src/tests/common.cairo b/src/tests/common.cairo index e8037ea..f37d030 100644 --- a/src/tests/common.cairo +++ b/src/tests/common.cairo @@ -17,6 +17,9 @@ const HEADER_RANGE_DIGEST: u256 = 0xb646edd6dbb2e5482b2449404cf1888b8f4cd6958c79 const NEXT_HEADER_DIGEST: u256 = 0xfd6c88812a160ff288fe557111815b3433c539c77a3561086cfcdd9482bceb8; const TOTAL_SUPPLY: u256 = 0x100000000000000000000000000000001; +// Current Sepolia proxy contract address +const BLOBSTREAMX_L1_ADDRESS: felt252 = 0x48B257EC1610d04191cC2c528d0c940AdbE1E439; + fn setup_base() -> ContractAddress { // deploy the token associated with the fee vault let mut calldata = array![]; @@ -71,6 +74,7 @@ fn setup_base() -> ContractAddress { next_header_func_id.low.into(), next_header_func_id.high.into(), herodotus_facts_registry.into(), + BLOBSTREAMX_L1_ADDRESS.into() ]; blobstreamx_class.deploy(@calldata).unwrap() } diff --git a/src/tests/test_blobstreamx.cairo b/src/tests/test_blobstreamx.cairo index 8ce50d2..168a082 100644 --- a/src/tests/test_blobstreamx.cairo +++ b/src/tests/test_blobstreamx.cairo @@ -9,6 +9,7 @@ use blobstream_sn::mocks::evm_facts_registry::{ }; use blobstream_sn::tests::common::{ setup_base, setup_spied, setup_succinct_gateway, TEST_START_BLOCK, TEST_END_BLOCK, TEST_HEADER, + BLOBSTREAMX_L1_ADDRESS }; use openzeppelin::tests::utils::constants::OWNER; use snforge_std as snf; @@ -203,7 +204,6 @@ fn blobstreamx_data_commitments_from_herodotus_facts() { let bsx = setup_blobstreamx(); let hfr_addr = bsx.get_herodotus_facts_registry(); - let l1_addr: felt252 = 0x48B257EC1610d04191cC2c528d0c940AdbE1E439; let l1_block_num: u256 = 0x100; let hfr_dispatcher = IEVMFactsRegistryMockDispatcher { contract_address: hfr_addr }; @@ -211,26 +211,28 @@ fn blobstreamx_data_commitments_from_herodotus_facts() { // Set the state_proofNonce slot to 3 hfr_dispatcher - .set_slot_value(l1_addr, l1_block_num, 0xfc, 3.into()); // state_proofNonce at slot 0xfc + .set_slot_value( + BLOBSTREAMX_L1_ADDRESS, l1_block_num, 0xfc, 3.into() + ); // state_proofNonce at slot 0xfc // Add the 2 data commitments - // Slot pos = keccak256(abi.encode(map_key, state_dataCommitment_slot)) ie keccak256(abi.encode(map_key, 0xfe)) + // Slot pos = keccak256(abi.encode(map_key, state_dataCommitments_slot)) ie keccak256(abi.encode(map_key, 0xfe)) let data_commitment1_slot: u256 = 0x457c8a48b4735f56b938837eb0a8a5f9c55f23c1a85767ce3b65c3e59d3d32b7; let data_commitment1: u256 = 0xe1078369756a0b28e3b8cc1fa6e0133630ccdf9d2bd5bde1d40d197793c3c8b4; hfr_dispatcher .set_slot_value( - l1_addr, l1_block_num, data_commitment1_slot, data_commitment1 - ); // state_dataCommitment[1] + BLOBSTREAMX_L1_ADDRESS, l1_block_num, data_commitment1_slot, data_commitment1 + ); // state_dataCommitments[1] let data_commitment2_slot: u256 = 0xeeac6037a1009734a3fd8a7d8347d53da92d0725658242afb43dd0d755dbe634; let data_commitment2: u256 = 0xc2b2d9e303ad14a5aeeda362d3d4177eedb43e1e0e4e6d42f6922f2ebfb23cc6; hfr_dispatcher .set_slot_value( - l1_addr, l1_block_num, data_commitment2_slot, data_commitment2 - ); // state_dataCommitment[2] + BLOBSTREAMX_L1_ADDRESS, l1_block_num, data_commitment2_slot, data_commitment2 + ); // state_dataCommitments[2] bsx.update_data_commitments_from_facts(l1_block_num); assert!(bsx.get_state_proof_nonce() == 3, "state proof nonce invalid"); @@ -238,5 +240,71 @@ fn blobstreamx_data_commitments_from_herodotus_facts() { assert!(bsx.get_state_data_commitment(1) == data_commitment1, "data commitment 1 invalid"); assert!(bsx.get_state_data_commitment(2) == data_commitment2, "data commitment 2 invalid"); assert!(bsx.get_state_data_commitment(3) == 0, "data commitment 3 invalid"); -// TODO: test invalid inputs and things +} + +#[test] +#[should_panic(expected: ("No proof nonce found for block 256",))] +fn blobstreamx_invalid_request_for_herodotus_facts() { + let bsx = setup_blobstreamx(); + let l1_block_num: u256 = 0x100; + + bsx.update_data_commitments_from_facts(l1_block_num); +} + +#[test] +#[should_panic(expected: ("No data commitment found for block 256 and proof nonce 2",))] +fn blobstreamx_incomplete_data_commitments_relayed() { + let bsx = setup_blobstreamx(); + + let hfr_addr = bsx.get_herodotus_facts_registry(); + let l1_block_num: u256 = 0x100; + + let hfr_dispatcher = IEVMFactsRegistryMockDispatcher { contract_address: hfr_addr }; + + // Set the state_proofNonce slot to 3 + + hfr_dispatcher + .set_slot_value( + BLOBSTREAMX_L1_ADDRESS, l1_block_num, 0xfc, 3.into() + ); // state_proofNonce at slot 0xfc + + // Add only 1 of 2 data commitments + + // Slot pos = keccak256(abi.encode(map_key, state_dataCommitments_slot)) ie keccak256(abi.encode(map_key, 0xfe)) + let data_commitment1_slot: u256 = + 0x457c8a48b4735f56b938837eb0a8a5f9c55f23c1a85767ce3b65c3e59d3d32b7; + let data_commitment1: u256 = 0xe1078369756a0b28e3b8cc1fa6e0133630ccdf9d2bd5bde1d40d197793c3c8b4; + hfr_dispatcher + .set_slot_value( + BLOBSTREAMX_L1_ADDRESS, l1_block_num, data_commitment1_slot, data_commitment1 + ); // state_dataCommitments[1] + + bsx.update_data_commitments_from_facts(l1_block_num); +} + +#[test] +#[should_panic(expected: ("State proof nonce does not increase on block 256",))] +fn blobstreamx_invalid_proof_nonce_from_facts() { + let bsx = setup_blobstreamx(); + + let hfr_addr = bsx.get_herodotus_facts_registry(); + let l1_block_num: u256 = 0x100; + + let hfr_dispatcher = IEVMFactsRegistryMockDispatcher { contract_address: hfr_addr }; + + // Set the state_proofNonce slot to 2 + + hfr_dispatcher + .set_slot_value( + BLOBSTREAMX_L1_ADDRESS, l1_block_num, 0xfc, 2.into() + ); // state_proofNonce at slot 0xfc + + // Set state_proof_nonce to 3 in BlobstreamX + snf::store( + bsx.contract_address, + selector!("state_proof_nonce"), + array![3.into()].span() + ); + + bsx.update_data_commitments_from_facts(l1_block_num) }