diff --git a/pkg/eth/backend.go b/pkg/eth/backend.go index 999174b6..cb746556 100644 --- a/pkg/eth/backend.go +++ b/pkg/eth/backend.go @@ -767,7 +767,7 @@ func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address, return nil, err } - _, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash) + _, _, storageRlp, err := b.IPLDRetriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash) return storageRlp, err } diff --git a/pkg/eth/ipld_retriever.go b/pkg/eth/ipld_retriever.go index 4d50cfaa..3e54e28d 100644 --- a/pkg/eth/ipld_retriever.go +++ b/pkg/eth/ipld_retriever.go @@ -29,15 +29,15 @@ import ( const ( RetrieveHeadersByHashesPgStr = `SELECT cid, data - FROM eth.header_cids + FROM eth.header_cids INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key) WHERE block_hash = ANY($1::VARCHAR(66)[])` RetrieveHeadersByBlockNumberPgStr = `SELECT cid, data - FROM eth.header_cids + FROM eth.header_cids INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key) WHERE block_number = $1` RetrieveHeaderByHashPgStr = `SELECT cid, data - FROM eth.header_cids + FROM eth.header_cids INNER JOIN public.blocks ON (header_cids.mh_key = blocks.key) WHERE block_hash = $1` RetrieveUnclesByHashesPgStr = `SELECT cid, data @@ -429,25 +429,25 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Ad } // RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash -func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, error) { +func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) { storageResult := new(nodeInfo) stateLeafKey := crypto.Keccak256Hash(address.Bytes()) storageHash := crypto.Keccak256Hash(key.Bytes()) if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil { - return "", nil, err + return "", nil, nil, err } if storageResult.Removed { - return "", []byte{}, nil + return "", []byte{}, []byte{}, nil } var i []interface{} if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil { err = fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error()) - return "", nil, err + return "", nil, nil, err } if len(i) != 2 { - return "", nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements") + return "", nil, nil, fmt.Errorf("eth IPLDRetriever expected storage leaf node rlp to decode into two elements") } - return storageResult.CID, i[1].([]byte), nil + return storageResult.CID, storageResult.Data, i[1].([]byte), nil } // RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber returns the cid and rlp bytes for the storage value corresponding to the provided address, storage key, and block number diff --git a/pkg/graphql/graphql.go b/pkg/graphql/graphql.go index 6a590ea7..11cc226d 100644 --- a/pkg/graphql/graphql.go +++ b/pkg/graphql/graphql.go @@ -956,12 +956,47 @@ func (r *Resolver) Logs(ctx context.Context, args struct{ Filter FilterCriteria return runFilter(ctx, r.backend, filter) } +// StorageResult represents a storage slot value. All arguments are mandatory. +type StorageResult struct { + value []byte + cid string + ipldBlock []byte +} + +func (s *StorageResult) Value(ctx context.Context) common.Hash { + return common.BytesToHash(s.value) +} + +func (s *StorageResult) Cid(ctx context.Context) string { + return s.cid +} + +func (s *StorageResult) IpldBlock(ctx context.Context) hexutil.Bytes { + return hexutil.Bytes(s.ipldBlock) +} + func (r *Resolver) GetStorageAt(ctx context.Context, args struct { BlockHash common.Hash Contract common.Address Slot common.Hash -}) (*common.Hash, error) { - ret := common.BytesToHash([]byte{}) +}) (*StorageResult, error) { + cid, ipldBlock, rlpValue, err := r.backend.IPLDRetriever.RetrieveStorageAtByAddressAndStorageKeyAndBlockHash(args.Contract, args.Slot, args.BlockHash) + + if err != nil { + return nil, err + } + + var value interface{} + err = rlp.DecodeBytes(rlpValue, &value) + if err != nil { + return nil, err + } + + if err != nil { + return nil, err + } + + ret := StorageResult{value: value.([]byte), cid: cid, ipldBlock: ipldBlock} return &ret, nil } diff --git a/pkg/graphql/schema.go b/pkg/graphql/schema.go index f2208db9..2d49abe5 100644 --- a/pkg/graphql/schema.go +++ b/pkg/graphql/schema.go @@ -265,6 +265,17 @@ const schema string = ` topics: [[Bytes32!]!] } + # Storage trie value with IPLD data. + type StorageResult { + value: Bytes32! + + # CID for the storage trie IPLD block. + cid: String! + + # Storage trie IPLD block. + ipldBlock: Bytes! + } + type Query { # Block fetches an Ethereum block by number or by hash. If neither is # supplied, the most recent known block is returned. @@ -281,7 +292,7 @@ const schema string = ` logs(filter: FilterCriteria!): [Log!]! # Get storage slot by block hash and contract address. - getStorageAt(blockHash: Bytes32!, contract: Address!, slot: Bytes32!): Bytes32 + getStorageAt(blockHash: Bytes32!, contract: Address!, slot: Bytes32!): StorageResult # Get contract logs by block hash and contract address. getLogs(blockHash: Bytes32!, contract: Address!): [Log!]