-
Notifications
You must be signed in to change notification settings - Fork 181
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
package rpc | ||
|
||
import ( | ||
"github.com/NethermindEth/juno/core" | ||
"github.com/NethermindEth/juno/core/felt" | ||
"github.com/NethermindEth/juno/core/trie" | ||
"github.com/NethermindEth/juno/jsonrpc" | ||
) | ||
|
||
/**************************************************** | ||
Storage Handlers | ||
*****************************************************/ | ||
|
||
// StorageAt gets the value of the storage at the given address and key. | ||
// | ||
// It follows the specification defined here: | ||
// https://github.com/starkware-libs/starknet-specs/blob/a789ccc3432c57777beceaa53a34a7ae2f25fda0/api/starknet_api_openrpc.json#L110 | ||
func (h *Handler) StorageAt(address, key felt.Felt, id BlockID) (*felt.Felt, *jsonrpc.Error) { | ||
stateReader, stateCloser, rpcErr := h.stateByBlockID(&id) | ||
if rpcErr != nil { | ||
return nil, rpcErr | ||
} | ||
defer h.callAndLogErr(stateCloser, "Error closing state reader in getStorageAt") | ||
|
||
value, err := stateReader.ContractStorage(&address, &key) | ||
if err != nil { | ||
return nil, ErrContractNotFound | ||
} | ||
|
||
return value, nil | ||
} | ||
|
||
// StorageProof returns the merkle paths in one of the state tries: global state, classes, individual contract | ||
// | ||
// It follows the specification defined here: | ||
// https://github.com/starkware-libs/starknet-specs/blob/647caa00c0223e1daab1b2f3acc4e613ba2138aa/api/starknet_api_openrpc.json#L910 | ||
func (h *Handler) StorageProof(classes, contracts []felt.Felt, storageKeys []StorageKeys) (*felt.Felt, *jsonrpc.Error) { | ||
stateReader, stateCloser, err := h.bcReader.HeadState() | ||
if err != nil { | ||
return nil, ErrInternal.CloneWithData(err) | ||
} | ||
defer h.callAndLogErr(stateCloser, "Error closing state reader in getStorageProof") | ||
|
||
// TODO: Extend state reader interface? | ||
s := stateReader.(*core.State) | ||
clt, _, err := s.ClassTrie() | ||
if err != nil { | ||
return nil, ErrInternal.CloneWithData(err) | ||
} | ||
for _, elt := range classes { | ||
feltBytes := elt.Bytes() | ||
key := trie.NewKey(core.ContractStorageTrieHeight, feltBytes[:]) | ||
nodes, err := trie.GetProof(&key, clt) | ||
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / lint
Check failure on line 53 in rpc/storage.go GitHub Actions / Run Tests (ubuntu-latest)
Check failure on line 53 in rpc/storage.go GitHub Actions / Run Tests (ubuntu-latest)
Check failure on line 53 in rpc/storage.go GitHub Actions / Run Tests (macos-latest)
Check failure on line 53 in rpc/storage.go GitHub Actions / Run Tests (macos-latest)
Check failure on line 53 in rpc/storage.go GitHub Actions / Run Tests (ubuntu-arm64-4-core)
|
||
// adapt proofs to the expected format | ||
} | ||
|
||
return nil, ErrUnexpectedError | ||
} | ||
|
||
// https://github.com/starkware-libs/starknet-specs/blob/647caa00c0223e1daab1b2f3acc4e613ba2138aa/api/starknet_api_openrpc.json#L938 | ||
type StorageKeys struct { | ||
Contract felt.Felt `json:"contract_address"` | ||
Keys []felt.Felt `json:"storage_keys"` | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
package rpc_test | ||
|
||
import ( | ||
"errors" | ||
"github.com/NethermindEth/juno/core/felt" | ||
"github.com/NethermindEth/juno/db" | ||
"github.com/NethermindEth/juno/mocks" | ||
"github.com/NethermindEth/juno/rpc" | ||
"github.com/NethermindEth/juno/utils" | ||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
"go.uber.org/mock/gomock" | ||
"testing" | ||
) | ||
|
||
func TestStorageAt(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
t.Cleanup(mockCtrl.Finish) | ||
|
||
mockReader := mocks.NewMockReader(mockCtrl) | ||
log := utils.NewNopZapLogger() | ||
handler := rpc.New(mockReader, nil, nil, "", log) | ||
|
||
t.Run("empty blockchain", func(t *testing.T) { | ||
mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Latest: true}) | ||
require.Nil(t, storage) | ||
assert.Equal(t, rpc.ErrBlockNotFound, rpcErr) | ||
}) | ||
|
||
t.Run("non-existent block hash", func(t *testing.T) { | ||
mockReader.EXPECT().StateAtBlockHash(&felt.Zero).Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Hash: &felt.Zero}) | ||
require.Nil(t, storage) | ||
assert.Equal(t, rpc.ErrBlockNotFound, rpcErr) | ||
}) | ||
|
||
t.Run("non-existent block number", func(t *testing.T) { | ||
mockReader.EXPECT().StateAtBlockNumber(uint64(0)).Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Number: 0}) | ||
require.Nil(t, storage) | ||
assert.Equal(t, rpc.ErrBlockNotFound, rpcErr) | ||
}) | ||
|
||
mockState := mocks.NewMockStateHistoryReader(mockCtrl) | ||
|
||
t.Run("non-existent contract", func(t *testing.T) { | ||
mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil) | ||
mockState.EXPECT().ContractStorage(gomock.Any(), gomock.Any()).Return(nil, errors.New("non-existent contract")) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Latest: true}) | ||
require.Nil(t, storage) | ||
assert.Equal(t, rpc.ErrContractNotFound, rpcErr) | ||
}) | ||
|
||
t.Run("non-existent key", func(t *testing.T) { | ||
mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil) | ||
mockState.EXPECT().ContractStorage(gomock.Any(), gomock.Any()).Return(&felt.Zero, errors.New("non-existent key")) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Latest: true}) | ||
require.Nil(t, storage) | ||
assert.Equal(t, rpc.ErrContractNotFound, rpcErr) | ||
}) | ||
|
||
expectedStorage := new(felt.Felt).SetUint64(1) | ||
|
||
t.Run("blockID - latest", func(t *testing.T) { | ||
mockReader.EXPECT().HeadState().Return(mockState, nopCloser, nil) | ||
mockState.EXPECT().ContractStorage(gomock.Any(), gomock.Any()).Return(expectedStorage, nil) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Latest: true}) | ||
require.Nil(t, rpcErr) | ||
assert.Equal(t, expectedStorage, storage) | ||
}) | ||
|
||
t.Run("blockID - hash", func(t *testing.T) { | ||
mockReader.EXPECT().StateAtBlockHash(&felt.Zero).Return(mockState, nopCloser, nil) | ||
mockState.EXPECT().ContractStorage(gomock.Any(), gomock.Any()).Return(expectedStorage, nil) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Hash: &felt.Zero}) | ||
require.Nil(t, rpcErr) | ||
assert.Equal(t, expectedStorage, storage) | ||
}) | ||
|
||
t.Run("blockID - number", func(t *testing.T) { | ||
mockReader.EXPECT().StateAtBlockNumber(uint64(0)).Return(mockState, nopCloser, nil) | ||
mockState.EXPECT().ContractStorage(gomock.Any(), gomock.Any()).Return(expectedStorage, nil) | ||
|
||
storage, rpcErr := handler.StorageAt(felt.Zero, felt.Zero, rpc.BlockID{Number: 0}) | ||
require.Nil(t, rpcErr) | ||
assert.Equal(t, expectedStorage, storage) | ||
}) | ||
} | ||
|
||
func TestStorageProof(t *testing.T) { | ||
mockCtrl := gomock.NewController(t) | ||
t.Cleanup(mockCtrl.Finish) | ||
|
||
mockReader := mocks.NewMockReader(mockCtrl) | ||
log := utils.NewNopZapLogger() | ||
handler := rpc.New(mockReader, nil, nil, "", log) | ||
|
||
t.Run("empty blockchain", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("class trie hash does not exist in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("class trie hash exists in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("storage trie address does not exist in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("storage trie address exists in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("contract storage trie address does not exist in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("contract storage trie key slot does not exist in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("contract storage trie address/key exists in a trie", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
t.Run("class & storage tries proofs requested", func(t *testing.T) { | ||
//mockReader.EXPECT().HeadState().Return(nil, nil, db.ErrKeyNotFound) | ||
|
||
proof, rpcErr := handler.StorageProof(nil, nil, nil) | ||
require.Nil(t, proof) | ||
|
||
assert.Equal(t, rpc.ErrUnexpectedError, rpcErr) | ||
}) | ||
} |