Skip to content

Commit

Permalink
Adds GetStorageProof method (#635)
Browse files Browse the repository at this point in the history
* implement rpcv8 GetStorageProof method
  • Loading branch information
thiagodeev authored Oct 28, 2024
1 parent 6449e31 commit 470f18c
Show file tree
Hide file tree
Showing 6 changed files with 152 additions and 0 deletions.
15 changes: 15 additions & 0 deletions mocks/mock_rpc_provider.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions rpc/contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,3 +160,21 @@ func (provider *Provider) EstimateMessageFee(ctx context.Context, msg MsgFromL1,
}
return &raw, nil
}

// Get merkle paths in one of the state tries: global state, classes, individual contract.
// A single request can query for any mix of the three types of storage proofs (classes, contracts, and storage)
//
// Parameters:
// - ctx: The context of the function call
// - storageProofInput: an input containing at least one of the fields filled
// Returns:
// - *StorageProofResult: the proofs of the field passed in the input
// - error: an error if any occurred during the execution
func (provider *Provider) GetStorageProof(ctx context.Context, storageProofInput StorageProofInput) (*StorageProofResult, error) {
var raw StorageProofResult
if err := do(ctx, provider.c, "starknet_getStorageProof", &raw, storageProofInput); err != nil {

return nil, tryUnwrapToRPCErr(err, ErrBlockNotFound, ErrStorageProofNotSupported)
}
return &raw, nil
}
4 changes: 4 additions & 0 deletions rpc/contract_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -692,3 +692,7 @@ func TestEstimateFee(t *testing.T) {
}
}
}

func TestGetStorageProof(t *testing.T) {
t.Skip("TODO: create a test before merge")
}
4 changes: 4 additions & 0 deletions rpc/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ var (
Code: 41,
Message: "Transaction execution error",
}
ErrStorageProofNotSupported = &RPCError{
Code: 42,
Message: "the node doesn't support storage proofs for blocks that are too far in the past",
}
ErrInvalidContractClass = &RPCError{
Code: 50,
Message: "Invalid contract class",
Expand Down
1 change: 1 addition & 0 deletions rpc/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ type RpcProvider interface {
EstimateMessageFee(ctx context.Context, msg MsgFromL1, blockID BlockID) (*FeeEstimation, error)
Events(ctx context.Context, input EventsInput) (*EventChunk, error)
BlockWithReceipts(ctx context.Context, blockID BlockID) (interface{}, error)
GetStorageProof(ctx context.Context, storageProofInput StorageProofInput) (*StorageProofResult, error)
GetTransactionStatus(ctx context.Context, transactionHash *felt.Felt) (*TxnStatusResp, error)
Nonce(ctx context.Context, blockID BlockID, contractAddress *felt.Felt) (*felt.Felt, error)
SimulateTransactions(ctx context.Context, blockID BlockID, txns []BroadcastTxn, simulationFlags []SimulationFlag) ([]SimulatedTransaction, error)
Expand Down
110 changes: 110 additions & 0 deletions rpc/types_contract.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,79 @@ type ContractClass struct {
ABI string `json:"abi,omitempty"`
}

type StorageProofInput struct {
// The hash of the requested block, or number (height) of the requested block, or a block tag
BlockID BlockID `json:"block_id"`
// A list of the class hashes for which we want to prove membership in the classes trie
ClassHashes []*felt.Felt `json:"class_hashes,omitempty"`
// A list of contracts for which we want to prove membership in the global state trie
ContractAddresses []*felt.Felt `json:"contract_addresses,omitempty"`
// A list of (contract_address, storage_keys) pairs
ContractsStorageKeys []ContractStorageKeys `json:"contracts_storage_keys,omitempty"`
}

type ContractStorageKeys struct {
ContractAddress *felt.Felt `json:"contract_address"`
StorageKeys []*felt.Felt `json:"storage_keys"`
}

// The requested storage proofs. Note that if a requested leaf has the default value,
// the path to it may end in an edge node whose path is not a prefix of the requested leaf,
// thus effecitvely proving non-membership
type StorageProofResult struct {
ClassesProof NodeHashToNode `json:"classes_proof"`
ContractsProof ContractsProof `json:"contracts_proof"`
ContractsStorageProofs []NodeHashToNode `json:"contracts_storage_proofs"`
GlobalRoots []NodeHashToNode `json:"global_roots"`
}

type ContractsProof struct {
// The nodes in the union of the paths from the contracts tree root to the requested leaves
Nodes NodeHashToNode `json:"nodes"`
ContractLeavesData []ContractLeavesData `json:"contract_leaves_data"`
}

// The nonce and class hash for each requested contract address, in the order in which
// they appear in the request. These values are needed to construct the associated leaf node
type ContractLeavesData struct {
Nonce *felt.Felt `json:"nonce"`
ClassHash *felt.Felt `json:"class_hash"`
}

type GlobalRoots struct {
ContractsTreeRoot *felt.Felt `json:"contracts_tree_root"`
ClassesTreeRoot *felt.Felt `json:"classes_tree_root"`
// the associated block hash (needed in case the caller used a block tag for the block_id parameter)
BlockHash *felt.Felt `json:"block_hash"`
}

// A node_hash -> node mapping of all the nodes in the union of the paths between the requested leaves and the root
type NodeHashToNode struct {
NodeHash *felt.Felt `json:"node_hash"`
Node MerkleNode `json:"node"`
}

// A node in the Merkle-Patricia tree, can be a leaf, binary node, or an edge node
type MerkleNode interface{} // it should be an EdgeNode or BinaryNode

// Represents a path to the highest non-zero descendant node
type EdgeNode struct {
// an integer whose binary representation represents the path from the current node to its highest non-zero descendant (bounded by 2^251)
Path NumAsHex `json:"path"`
// the length of the path (bounded by 251)
Length uint `json:"length"`
// the hash of the unique non-zero maximal-height descendant node
Child *felt.Felt `json:"child"`
}

// An internal node whose both children are non-zero
type BinaryNode struct {
// the hash of the left child
Left *felt.Felt `json:"left"`
// the hash of the right child
Right *felt.Felt `json:"right"`
}

// UnmarshalJSON unmarshals the JSON content into the DeprecatedContractClass struct.
//
// It takes a byte array `content` as a parameter and returns an error if there is any.
Expand Down Expand Up @@ -168,6 +241,43 @@ func (c *DeprecatedContractClass) UnmarshalJSON(content []byte) error {
return nil
}

func (nodeHashToNode *NodeHashToNode) UnmarshalJSON(bytes []byte) error {
valueMap := make(map[string]any)
if err := json.Unmarshal(bytes, &valueMap); err != nil {
return err
}

nodeHash, ok := valueMap["node_hash"]
if !ok {
return fmt.Errorf("missing 'node_hash' in json object")
}
nodeHashFelt, ok := nodeHash.(felt.Felt)
if !ok {
return fmt.Errorf("error casting 'node_hash' to felt.Felt")
}

node, ok := valueMap["node"]
if !ok {
return fmt.Errorf("missing 'node' in json object")
}
var merkleNode MerkleNode
switch nodeT := node.(type) {
case BinaryNode:
merkleNode = nodeT
case EdgeNode:
merkleNode = nodeT
default:
return fmt.Errorf("'node' should be an EdgeNode or BinaryNode")
}

*nodeHashToNode = NodeHashToNode{
NodeHash: &nodeHashFelt,
Node: merkleNode,
}

return nil
}

type SierraEntryPoint struct {
// The index of the function in the program
FunctionIdx int `json:"function_idx"`
Expand Down

0 comments on commit 470f18c

Please sign in to comment.