forked from cerc-io/ipld-eth-server
Merge pull request #94 from vulcanize/empty_data_encoder
fix encoding when storage is empty
This commit is contained in:
commit
0f2b6fd843
@ -1,4 +1,20 @@
|
||||
-- +goose Up
|
||||
-- +goose StatementBegin
|
||||
-- returns if a state leaf node was removed within the provided block number
|
||||
CREATE OR REPLACE FUNCTION was_state_leaf_removed(key character varying, hash character varying)
|
||||
RETURNS boolean AS $$
|
||||
SELECT state_cids.node_type = 3
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
WHERE state_leaf_key = key
|
||||
AND block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = hash)
|
||||
ORDER BY block_number DESC LIMIT 1;
|
||||
$$
|
||||
language sql;
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose StatementBegin
|
||||
CREATE TYPE child_result AS (
|
||||
has_child BOOLEAN,
|
||||
@ -115,6 +131,7 @@ LANGUAGE 'plpgsql';
|
||||
-- +goose StatementEnd
|
||||
|
||||
-- +goose Down
|
||||
DROP FUNCTION was_state_leaf_removed;
|
||||
DROP FUNCTION canonical_header_id;
|
||||
DROP FUNCTION canonical_header_from_array;
|
||||
DROP FUNCTION has_child;
|
||||
|
@ -20,7 +20,7 @@ services:
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
image: vulcanize/statediff-migrations:v0.7.0
|
||||
image: vulcanize/statediff-migrations:v0.8.0
|
||||
environment:
|
||||
DATABASE_USER: vdbm
|
||||
DATABASE_NAME: vulcanize_public
|
||||
@ -39,6 +39,7 @@ services:
|
||||
- vdb_db_eth_server:/var/lib/postgresql/data
|
||||
ports:
|
||||
- "127.0.0.1:8077:5432"
|
||||
command: ["postgres", "-c", "log_statement=all"]
|
||||
|
||||
eth-server:
|
||||
restart: unless-stopped
|
||||
@ -61,6 +62,7 @@ services:
|
||||
DATABASE_USER: "vdbm"
|
||||
DATABASE_PASSWORD: "password"
|
||||
ETH_CHAIN_ID: 4
|
||||
RUN_DB_MIGRATION: "no"
|
||||
volumes:
|
||||
- type: bind
|
||||
source: ./chain.json
|
||||
|
@ -4,18 +4,21 @@
|
||||
# Construct the connection string for postgres
|
||||
VDB_PG_CONNECT=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable
|
||||
|
||||
# Run the DB migrations
|
||||
echo "Connecting with: $VDB_PG_CONNECT"
|
||||
echo "Running database migrations"
|
||||
./goose -dir migrations/vulcanizedb postgres "$VDB_PG_CONNECT" up
|
||||
rv=$?
|
||||
|
||||
if [ $rv != 0 ]; then
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
exit 1
|
||||
if [ "$RUN_DB_MIGRATION" != "no" ]
|
||||
then
|
||||
# Run the DB migrations
|
||||
echo "Connecting with: $VDB_PG_CONNECT"
|
||||
echo "Running database migrations"
|
||||
./goose -dir migrations/vulcanizedb postgres "$VDB_PG_CONNECT" up
|
||||
rv=$?
|
||||
|
||||
if [ $rv != 0 ]; then
|
||||
echo "Could not run migrations. Are the database details correct?"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
echo "Beginning the ipld-eth-server process"
|
||||
|
||||
echo running: ./ipld-eth-server ${VDB_COMMAND} --config=config.toml
|
||||
|
@ -476,16 +476,16 @@ var _ = Describe("eth state reading tests", func() {
|
||||
It("Returns empty slice if it tries to access a contract which does not exist", func() {
|
||||
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
Expect(storage).To(Equal(hexutil.Bytes(eth.EmptyNodeValue)))
|
||||
|
||||
storage, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
Expect(storage).To(Equal(hexutil.Bytes(eth.EmptyNodeValue)))
|
||||
})
|
||||
It("Returns empty slice if it tries to access a contract slot which does not exist", func() {
|
||||
storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(storage).To(Equal(hexutil.Bytes(make([]byte, 32))))
|
||||
Expect(storage).To(Equal(hexutil.Bytes(eth.EmptyNodeValue)))
|
||||
})
|
||||
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash or number", func() {
|
||||
// After deployment
|
||||
@ -506,7 +506,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
|
||||
val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(5))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(val).To(Equal(hexutil.Bytes{}))
|
||||
Expect(val).To(Equal(hexutil.Bytes(eth.EmptyNodeValue)))
|
||||
})
|
||||
It("Throws an error for a non-existing block hash", func() {
|
||||
_, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithHash(randomHash, true))
|
||||
|
@ -127,7 +127,7 @@ const (
|
||||
AND block_number <= $2
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr = `SELECT storage_cids.cid, data, storage_cids.node_type
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr = `SELECT storage_cids.cid, data, storage_cids.node_type, was_state_leaf_removed($1, $3) AS state_leaf_removed
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
@ -137,7 +137,7 @@ const (
|
||||
AND block_number <= $3
|
||||
ORDER BY block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT storage_cids.cid, data, storage_cids.node_type
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT storage_cids.cid, data, storage_cids.node_type, was_state_leaf_removed($1, $3) AS state_leaf_removed
|
||||
FROM eth.storage_cids
|
||||
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id)
|
||||
INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
|
||||
@ -152,6 +152,8 @@ const (
|
||||
LIMIT 1`
|
||||
)
|
||||
|
||||
var EmptyNodeValue = make([]byte, common.HashLength)
|
||||
|
||||
type rctIpldResult struct {
|
||||
LeafCID string `db:"leaf_cid"`
|
||||
Data []byte `db:"data"`
|
||||
@ -425,9 +427,10 @@ func (r *IPLDRetriever) RetrieveReceiptByHash(hash common.Hash) (string, []byte,
|
||||
}
|
||||
|
||||
type nodeInfo struct {
|
||||
CID string `db:"cid"`
|
||||
Data []byte `db:"data"`
|
||||
NodeType int `db:"node_type"`
|
||||
CID string `db:"cid"`
|
||||
Data []byte `db:"data"`
|
||||
NodeType int `db:"node_type"`
|
||||
StateLeafRemoved bool `db:"state_leaf_removed"`
|
||||
}
|
||||
|
||||
// RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash
|
||||
@ -438,9 +441,11 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockHash(address common.Addr
|
||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if accountResult.NodeType == removedNode {
|
||||
return "", []byte{}, nil
|
||||
return "", EmptyNodeValue, nil
|
||||
}
|
||||
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
|
||||
return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error())
|
||||
@ -459,9 +464,11 @@ func (r *IPLDRetriever) RetrieveAccountByAddressAndBlockNumber(address common.Ad
|
||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
if accountResult.NodeType == removedNode {
|
||||
return "", []byte{}, nil
|
||||
return "", EmptyNodeValue, nil
|
||||
}
|
||||
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
|
||||
return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error())
|
||||
@ -480,8 +487,8 @@ func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(add
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if storageResult.NodeType == removedNode {
|
||||
return "", []byte{}, []byte{}, nil
|
||||
if storageResult.StateLeafRemoved || storageResult.NodeType == removedNode {
|
||||
return "", EmptyNodeValue, EmptyNodeValue, nil
|
||||
}
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
|
||||
@ -502,8 +509,9 @@ func (r *IPLDRetriever) RetrieveStorageAtByAddressAndStorageKeyAndBlockNumber(ad
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), number); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
if storageResult.NodeType == removedNode {
|
||||
return "", []byte{}, nil
|
||||
|
||||
if storageResult.StateLeafRemoved || storageResult.NodeType == removedNode {
|
||||
return "", EmptyNodeValue, nil
|
||||
}
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
|
||||
|
@ -18,6 +18,7 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"errors"
|
||||
@ -31,6 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
)
|
||||
|
||||
@ -1011,6 +1013,10 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Compare(rlpValue, eth.EmptyNodeValue) == 0 {
|
||||
return &StorageResult{value: eth.EmptyNodeValue, cid: cid, ipldBlock: ipldBlock}, nil
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
err = rlp.DecodeBytes(rlpValue, &value)
|
||||
if err != nil {
|
||||
|
@ -5,8 +5,8 @@ set -o xtrace
|
||||
docker-compose down --remove-orphans --volumes
|
||||
|
||||
# Build and start the containers.
|
||||
# Note: Build only if `ipld-eth-server` code is modified. Otherwise comment this line.
|
||||
docker build -t ipld-eth-server_eth-server:latest .
|
||||
# Note: Build only if `ipld-eth-server` or other container code is modified. Otherwise comment this line.
|
||||
docker-compose -f docker-compose.test.yml -f docker-compose.yml build eth-server
|
||||
docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
|
||||
|
||||
export PGPASSWORD=password
|
||||
|
@ -4,4 +4,7 @@ contract GLDToken is ERC20 {
|
||||
constructor() ERC20("Gold", "GLD") {
|
||||
_mint(msg.sender, 1000000000000000000000);
|
||||
}
|
||||
function destroy() public {
|
||||
selfdestruct(payable(msg.sender));
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const fastify = require('fastify')({ logger: true });
|
||||
const hre = require("hardhat");
|
||||
|
||||
|
||||
// readiness check
|
||||
fastify.get('/v1/healthz', async (req, reply) => {
|
||||
reply
|
||||
@ -22,6 +23,20 @@ fastify.get('/v1/deployContract', async (req, reply) => {
|
||||
}
|
||||
});
|
||||
|
||||
fastify.get('/v1/destroyContract', async (req, reply) => {
|
||||
const addr = req.query.addr;
|
||||
|
||||
const Token = await hre.ethers.getContractFactory("GLDToken");
|
||||
const token = await Token.attach(addr);
|
||||
|
||||
await token.destroy();
|
||||
const blockNum = await hre.ethers.provider.getBlockNumber()
|
||||
|
||||
return {
|
||||
blockNumber: blockNum,
|
||||
}
|
||||
})
|
||||
|
||||
fastify.get('/v1/sendEth', async (req, reply) => {
|
||||
const to = req.query.to;
|
||||
const value = req.query.value;
|
||||
|
@ -14,6 +14,10 @@ type ContractDeployed struct {
|
||||
BlockHash string `json:"blockHash"`
|
||||
}
|
||||
|
||||
type ContractDestroyed struct {
|
||||
BlockNumber int64 `json:"blockNumber"`
|
||||
}
|
||||
|
||||
type Tx struct {
|
||||
From string `json:"from"`
|
||||
To string `json:"to"`
|
||||
@ -43,6 +47,19 @@ func DeployContract() (*ContractDeployed, error) {
|
||||
return &contract, nil
|
||||
}
|
||||
|
||||
func DestroyContract(addr string) (*ContractDestroyed, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/destroyContract?addr=%s", srvUrl, addr))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var data ContractDestroyed
|
||||
decoder := json.NewDecoder(res.Body)
|
||||
|
||||
return &data, decoder.Decode(&data)
|
||||
}
|
||||
|
||||
func SendEth(to string, value string) (*Tx, error) {
|
||||
res, err := http.Get(fmt.Sprintf("%s/v1/sendEth?to=%s&value=%s", srvUrl, to, value))
|
||||
if err != nil {
|
||||
|
@ -9,12 +9,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
integration "github.com/vulcanize/ipld-eth-server/test"
|
||||
|
||||
"github.com/ethereum/go-ethereum/ethclient"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
integration "github.com/vulcanize/ipld-eth-server/test"
|
||||
)
|
||||
|
||||
const nonExistingBlockHash = "0x111111111111111111111111111111111111111111111111111111111111111"
|
||||
@ -391,6 +392,37 @@ var _ = Describe("Integration test", func() {
|
||||
Expect(err).To(MatchError("header not found"))
|
||||
Expect(gethStorage).To(Equal(ipldStorage))
|
||||
})
|
||||
|
||||
It("get storage after self destruct", func() {
|
||||
totalSupplyIndex := "0x2"
|
||||
|
||||
tx, err := integration.DestroyContract(contract.Address)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
time.Sleep(sleepInterval)
|
||||
|
||||
gethStorage1, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(tx.BlockNumber-1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
gethStorage2, err := gethClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(tx.BlockNumber))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(gethStorage1).NotTo(Equal(gethStorage2))
|
||||
Expect(gethStorage2).To(Equal(eth.EmptyNodeValue))
|
||||
|
||||
ipldStorage1, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(tx.BlockNumber-1))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
ipldStorage2, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), big.NewInt(tx.BlockNumber))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(ipldStorage1).To(Equal(gethStorage1))
|
||||
Expect(ipldStorage2).To(Equal(gethStorage2))
|
||||
|
||||
// Query the current block
|
||||
ipldStorage3, err := ipldClient.StorageAt(ctx, common.HexToAddress(contract.Address), common.HexToHash(totalSupplyIndex), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(ipldStorage2).To(Equal(ipldStorage3))
|
||||
})
|
||||
})
|
||||
|
||||
Describe("eth call", func() {
|
||||
|
Loading…
Reference in New Issue
Block a user