Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds GetStorageProof method #635

Merged
merged 2 commits into from
Oct 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -683,3 +683,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
Loading