-
Notifications
You must be signed in to change notification settings - Fork 28
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Herodotus Data Commitment Facts (#120)
* Inital scripts to request data commitment proof and relay from L1 to Starknet, also includes a wait script to hold until relay is done and in Herodotus Fact Registry on Starknet * Setup update_data_commitments_from_facts on BlobstreamX contract with tests and mock of evm_facts_registry * Scarb fmt * Test errors in fact registry, fix hardcoded values, scripts properly parse arguments, and verbose options * Scarb fmt * Hardcoded 4 requests for DC * Remove latest-proof-nonce script
- Loading branch information
1 parent
ea5ba8a
commit e5885e3
Showing
8 changed files
with
642 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,231 @@ | ||
#!/bin/bash | ||
# | ||
# Request relay of the latest BlobstreamX data commitments from L1 to Starknet | ||
# Uses the Herodotus API to submit a batch query for all data commitments | ||
# up to the latest state_proofNonce | ||
|
||
# Constants | ||
HERODOTUS_API_URL="https://api.herodotus.cloud/" | ||
BLOBSTREAMX_SOURCE_CHAIN_ID="11155111" | ||
BLOBSTREAMX_DESTINATION_CHAIN_ID="SN_SEPOLIA" | ||
STATE_PROOF_NONCE_SLOT="0x00000000000000000000000000000000000000000000000000000000000000fc" | ||
STATE_DATA_COMMITMENT_MAP_SLOT="0x00000000000000000000000000000000000000000000000000000000000000fe" | ||
|
||
# Optional arguments | ||
L1_RPC_URL="https://sepolia.infura.io/v3/bed8a8401c894421bd7cd31050e7ced6" | ||
STARKNET_RPC_URL="https://starknet-sepolia.infura.io/v3/bed8a8401c894421bd7cd31050e7ced6" | ||
BLOBSTREAMX_L1_ADDRESS="0x48B257EC1610d04191cC2c528d0c940AdbE1E439" | ||
#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/.. | ||
|
||
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 " -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" | ||
} | ||
|
||
# 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 ":hnvNb:B:r:s:-:" opt; do | ||
case ${opt} in | ||
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 | ||
exit 1 | ||
;; | ||
: ) | ||
echo "Invalid option: $OPTARG requires an argument" 1>&2 | ||
display_help | ||
exit 1 | ||
;; | ||
esac | ||
done | ||
|
||
# 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 | ||
|
||
L1_BLOCK_NUM_RES=$(curl $L1_RPC_URL \ | ||
-X POST \ | ||
-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') | ||
|
||
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"'","'"$L1_BLOCK_NUM"'"],"id":1}' 2>/dev/null) | ||
LATEST_PROOF_NONCE=$(echo $LATEST_PROOF_NONCE_RES | jq -r '.result') | ||
|
||
#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) | ||
#TODO: Remove the hardcoded value once the blobstreamx contract is deployed on Starknet | ||
#STARKNET_PROOF_NONCE_RES=$(echo '{"jsonrpc":"2.0","id":1,"result":"0x00000000000000000000000000000000000000000000000000000000000006cb"}') | ||
#STARKNET_PROOF_NONCE=$(echo $STARKNET_PROOF_NONCE_RES | jq -r '.result') | ||
STARKNET_PROOF_NONCE=$((LATEST_PROOF_NONCE - 4)) | ||
|
||
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='' | ||
# Loop through each missing state_proofNonce to build the batch query slots | ||
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) | ||
DATA_COMMITMENT_SLOT=$(echo $DATA_COMMITMENT_ENCODED_SLOT | keccak-256sum -x -l | awk '{print $1}') | ||
|
||
if [ -z "$DATA_COMMITMENT_SLOTS" ]; then | ||
DATA_COMMITMENT_SLOTS='"0x'$DATA_COMMITMENT_SLOT'"' | ||
else | ||
DATA_COMMITMENT_SLOTS=$DATA_COMMITMENT_SLOTS',"0x'$DATA_COMMITMENT_SLOT'"' | ||
fi | ||
done | ||
|
||
if [ -z "$DATA_COMMITMENT_SLOTS" ]; then | ||
echo "No missing data commitments found." | ||
exit 0 | ||
fi | ||
|
||
# Convert L1 Block number from hex to decimal | ||
L1_BLOCK_NUM_DEC=$(printf "%d" $L1_BLOCK_NUM) | ||
HERODOTUS_QUERY='{ | ||
"destinationChainId": "'$BLOBSTREAMX_DESTINATION_CHAIN_ID'", | ||
"fee": "0", | ||
"data": { | ||
"'$BLOBSTREAMX_SOURCE_CHAIN_ID'": { | ||
"block:'$L1_BLOCK_NUM_DEC'": { | ||
"header": [ | ||
"PARENT_HASH" | ||
], | ||
"accounts": { | ||
"'$BLOBSTREAMX_L1_ADDRESS'": { | ||
"slots": [ | ||
"'$STATE_PROOF_NONCE_SLOT'", | ||
'$DATA_COMMITMENT_SLOTS' | ||
], | ||
"props": [] | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
"webhook": { | ||
"url": "https://webhook.site/1f3a9b5d-5c8c-4e2a-9d7e-6c3c5a0a0e2f", | ||
"headers": { | ||
"Content-Type": "application/json" | ||
} | ||
} | ||
}' | ||
# Clean up the query formatting for readability | ||
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..." | ||
HERODOTUS_ID=$(curl -X 'POST' \ | ||
$HERODOTUS_QUERY_URL \ | ||
-H 'accept: application/json' \ | ||
-H 'Content-Type: application/json' \ | ||
-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 | ||
|
||
# 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 $([ "$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 $([ "$VERBOSE" = true ] && echo "-v") | ||
done |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
#!/bin/bash | ||
# | ||
# Wait for the requested slot value relay to be fulfilled by Herodotus. | ||
# 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 " -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 - Hex (required)" | ||
|
||
echo | ||
echo " -h, --help display help" | ||
echo " -v, --verbose display verbose output" | ||
|
||
echo | ||
echo "Example: $0" | ||
} | ||
|
||
# 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 ":hvs:F:b:a:S:" opt; do | ||
case ${opt} in | ||
h ) | ||
display_help | ||
exit 0 | ||
;; | ||
v ) | ||
VERBOSE=true | ||
;; | ||
s ) | ||
STARKNET_RPC_URL="${OPTARG}" | ||
;; | ||
F ) | ||
HERODOTUS_FACT_REGISTRY="${OPTARG}" | ||
;; | ||
b ) | ||
BLOCK_NUMBER="${OPTARG}" | ||
;; | ||
a ) | ||
ADDRESS="${OPTARG}" | ||
;; | ||
S ) | ||
SLOT="${OPTARG}" | ||
;; | ||
\? ) | ||
echo "Invalid option: $OPTARG" 1>&2 | ||
display_help | ||
exit 1 | ||
;; | ||
: ) | ||
echo "Invalid option: $OPTARG requires an argument" 1>&2 | ||
display_help | ||
exit 1 | ||
;; | ||
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} | ||
|
||
# Left pad the slot value to 32 bytes | ||
if [[ $SLOT == 0x* ]]; then | ||
SLOT=${SLOT:2} | ||
fi | ||
while [ ${#SLOT} -lt 64 ]; do | ||
SLOT="0$SLOT" | ||
done | ||
# Get the low and high 16 bytes | ||
SLOT_HIGH=0x${SLOT:0:32} | ||
SLOT_LOW=0x${SLOT:32:32} | ||
|
||
HERODOTUS_FACT_QUERY='{ | ||
"id": 1, | ||
"jsonrpc": "2.0", | ||
"method": "starknet_call", | ||
"params": [ | ||
{ | ||
"calldata": [ | ||
"'$ADDRESS'", | ||
"'$BLOCK_NUMBER_LOW'", | ||
"'$BLOCK_NUMBER_HIGH'", | ||
"'$SLOT_LOW'", | ||
"'$SLOT_HIGH'" | ||
], | ||
"entry_point_selector": "'$HERODOTUS_GET_SLOT_VALUE_SELECTOR'", | ||
"contract_address": "'$HERODOTUS_FACT_REGISTRY'" | ||
}, | ||
"pending" | ||
] | ||
}' | ||
|
||
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 \ | ||
-X 'POST' \ | ||
-H "Content-Type: application/json" \ | ||
-d "$HERODOTUS_FACT_QUERY" 2>/dev/null) | ||
if [ $(echo $RES | jq -r '.result[0]') == "0x0" ]; then | ||
VAL_LOW=$(echo $RES | jq -r '.result[1]') | ||
VAL_HIGH=$(echo $RES | jq -r '.result[2]') | ||
# Pad and remove 0x prefix | ||
VAL_LOW=${VAL_LOW:2} | ||
while [ ${#VAL_LOW} -lt 32 ]; do | ||
VAL_LOW="0$VAL_LOW" | ||
done | ||
VAL_HIGH=${VAL_HIGH:2} | ||
while [ ${#VAL_HIGH} -lt 32 ]; do | ||
VAL_HIGH="0$VAL_HIGH" | ||
done | ||
VAL="0x$VAL_HIGH$VAL_LOW" | ||
echo "Slot fact was fulfilled w/ value: $VAL" | ||
exit 0 | ||
fi | ||
sleep 5 | ||
done |
Oops, something went wrong.