Merge pull request #94 from vulcanize/empty_data_encoder

fix encoding when storage is empty
This commit is contained in:
Arijit Das 2021-09-28 10:47:43 +05:30 committed by GitHub
commit 0f2b6fd843
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 132 additions and 29 deletions

View File

@ -1,4 +1,20 @@
-- +goose Up -- +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 -- +goose StatementBegin
CREATE TYPE child_result AS ( CREATE TYPE child_result AS (
has_child BOOLEAN, has_child BOOLEAN,
@ -115,6 +131,7 @@ LANGUAGE 'plpgsql';
-- +goose StatementEnd -- +goose StatementEnd
-- +goose Down -- +goose Down
DROP FUNCTION was_state_leaf_removed;
DROP FUNCTION canonical_header_id; DROP FUNCTION canonical_header_id;
DROP FUNCTION canonical_header_from_array; DROP FUNCTION canonical_header_from_array;
DROP FUNCTION has_child; DROP FUNCTION has_child;

View File

@ -20,7 +20,7 @@ services:
restart: on-failure restart: on-failure
depends_on: depends_on:
- db - db
image: vulcanize/statediff-migrations:v0.7.0 image: vulcanize/statediff-migrations:v0.8.0
environment: environment:
DATABASE_USER: vdbm DATABASE_USER: vdbm
DATABASE_NAME: vulcanize_public DATABASE_NAME: vulcanize_public
@ -39,6 +39,7 @@ services:
- vdb_db_eth_server:/var/lib/postgresql/data - vdb_db_eth_server:/var/lib/postgresql/data
ports: ports:
- "127.0.0.1:8077:5432" - "127.0.0.1:8077:5432"
command: ["postgres", "-c", "log_statement=all"]
eth-server: eth-server:
restart: unless-stopped restart: unless-stopped
@ -61,6 +62,7 @@ services:
DATABASE_USER: "vdbm" DATABASE_USER: "vdbm"
DATABASE_PASSWORD: "password" DATABASE_PASSWORD: "password"
ETH_CHAIN_ID: 4 ETH_CHAIN_ID: 4
RUN_DB_MIGRATION: "no"
volumes: volumes:
- type: bind - type: bind
source: ./chain.json source: ./chain.json

View File

@ -4,6 +4,9 @@
# Construct the connection string for postgres # Construct the connection string for postgres
VDB_PG_CONNECT=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable VDB_PG_CONNECT=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable
if [ "$RUN_DB_MIGRATION" != "no" ]
then
# Run the DB migrations # Run the DB migrations
echo "Connecting with: $VDB_PG_CONNECT" echo "Connecting with: $VDB_PG_CONNECT"
echo "Running database migrations" echo "Running database migrations"
@ -14,7 +17,7 @@ if [ $rv != 0 ]; then
echo "Could not run migrations. Are the database details correct?" echo "Could not run migrations. Are the database details correct?"
exit 1 exit 1
fi fi
fi
echo "Beginning the ipld-eth-server process" echo "Beginning the ipld-eth-server process"

View File

@ -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() { 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)) storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(0))
Expect(err).NotTo(HaveOccurred()) 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)) storage, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.ContractSlotKeyHash.Hex(), rpc.BlockNumberOrHashWithNumber(1))
Expect(err).NotTo(HaveOccurred()) 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() { 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)) storage, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, randomHash.Hex(), rpc.BlockNumberOrHashWithNumber(2))
Expect(err).NotTo(HaveOccurred()) 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() { 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 // 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)) val, err = api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithNumber(5))
Expect(err).ToNot(HaveOccurred()) 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() { 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)) _, err := api.GetStorageAt(ctx, test_helpers.ContractAddr, test_helpers.IndexOne, rpc.BlockNumberOrHashWithHash(randomHash, true))

View File

@ -127,7 +127,7 @@ const (
AND block_number <= $2 AND block_number <= $2
ORDER BY block_number DESC ORDER BY block_number DESC
LIMIT 1` 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 FROM eth.storage_cids
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id) 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) INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
@ -137,7 +137,7 @@ const (
AND block_number <= $3 AND block_number <= $3
ORDER BY block_number DESC ORDER BY block_number DESC
LIMIT 1` 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 FROM eth.storage_cids
INNER JOIN eth.state_cids ON (storage_cids.state_id = state_cids.id) 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) INNER JOIN eth.header_cids ON (state_cids.header_id = header_cids.id)
@ -152,6 +152,8 @@ const (
LIMIT 1` LIMIT 1`
) )
var EmptyNodeValue = make([]byte, common.HashLength)
type rctIpldResult struct { type rctIpldResult struct {
LeafCID string `db:"leaf_cid"` LeafCID string `db:"leaf_cid"`
Data []byte `db:"data"` Data []byte `db:"data"`
@ -428,6 +430,7 @@ type nodeInfo struct {
CID string `db:"cid"` CID string `db:"cid"`
Data []byte `db:"data"` Data []byte `db:"data"`
NodeType int `db:"node_type"` 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 // 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 { if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
return "", nil, err return "", nil, err
} }
if accountResult.NodeType == removedNode { if accountResult.NodeType == removedNode {
return "", []byte{}, nil return "", EmptyNodeValue, nil
} }
var i []interface{} var i []interface{}
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil { if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error()) 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 { if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockNumberPgStr, leafKey.Hex(), number); err != nil {
return "", nil, err return "", nil, err
} }
if accountResult.NodeType == removedNode { if accountResult.NodeType == removedNode {
return "", []byte{}, nil return "", EmptyNodeValue, nil
} }
var i []interface{} var i []interface{}
if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil { if err := rlp.DecodeBytes(accountResult.Data, &i); err != nil {
return "", nil, fmt.Errorf("error decoding state leaf node rlp: %s", err.Error()) 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 { if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
return "", nil, nil, err return "", nil, nil, err
} }
if storageResult.NodeType == removedNode { if storageResult.StateLeafRemoved || storageResult.NodeType == removedNode {
return "", []byte{}, []byte{}, nil return "", EmptyNodeValue, EmptyNodeValue, nil
} }
var i []interface{} var i []interface{}
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil { 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 { if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockNumberPgStr, stateLeafKey.Hex(), storageLeafKey.Hex(), number); err != nil {
return "", nil, err return "", nil, err
} }
if storageResult.NodeType == removedNode {
return "", []byte{}, nil if storageResult.StateLeafRemoved || storageResult.NodeType == removedNode {
return "", EmptyNodeValue, nil
} }
var i []interface{} var i []interface{}
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil { if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {

View File

@ -18,6 +18,7 @@
package graphql package graphql
import ( import (
"bytes"
"context" "context"
"database/sql" "database/sql"
"errors" "errors"
@ -31,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/eth/filters" "github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/vulcanize/ipld-eth-server/pkg/eth" "github.com/vulcanize/ipld-eth-server/pkg/eth"
) )
@ -1011,6 +1013,10 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
return nil, err return nil, err
} }
if bytes.Compare(rlpValue, eth.EmptyNodeValue) == 0 {
return &StorageResult{value: eth.EmptyNodeValue, cid: cid, ipldBlock: ipldBlock}, nil
}
var value interface{} var value interface{}
err = rlp.DecodeBytes(rlpValue, &value) err = rlp.DecodeBytes(rlpValue, &value)
if err != nil { if err != nil {

View File

@ -5,8 +5,8 @@ set -o xtrace
docker-compose down --remove-orphans --volumes docker-compose down --remove-orphans --volumes
# Build and start the containers. # Build and start the containers.
# Note: Build only if `ipld-eth-server` code is modified. Otherwise comment this line. # Note: Build only if `ipld-eth-server` or other container code is modified. Otherwise comment this line.
docker build -t ipld-eth-server_eth-server:latest . 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 docker-compose -f docker-compose.test.yml -f docker-compose.yml up -d db dapptools contract eth-server
export PGPASSWORD=password export PGPASSWORD=password

View File

@ -4,4 +4,7 @@ contract GLDToken is ERC20 {
constructor() ERC20("Gold", "GLD") { constructor() ERC20("Gold", "GLD") {
_mint(msg.sender, 1000000000000000000000); _mint(msg.sender, 1000000000000000000000);
} }
function destroy() public {
selfdestruct(payable(msg.sender));
}
} }

View File

@ -1,6 +1,7 @@
const fastify = require('fastify')({ logger: true }); const fastify = require('fastify')({ logger: true });
const hre = require("hardhat"); const hre = require("hardhat");
// readiness check // readiness check
fastify.get('/v1/healthz', async (req, reply) => { fastify.get('/v1/healthz', async (req, reply) => {
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) => { fastify.get('/v1/sendEth', async (req, reply) => {
const to = req.query.to; const to = req.query.to;
const value = req.query.value; const value = req.query.value;

View File

@ -14,6 +14,10 @@ type ContractDeployed struct {
BlockHash string `json:"blockHash"` BlockHash string `json:"blockHash"`
} }
type ContractDestroyed struct {
BlockNumber int64 `json:"blockNumber"`
}
type Tx struct { type Tx struct {
From string `json:"from"` From string `json:"from"`
To string `json:"to"` To string `json:"to"`
@ -43,6 +47,19 @@ func DeployContract() (*ContractDeployed, error) {
return &contract, nil 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) { func SendEth(to string, value string) (*Tx, error) {
res, err := http.Get(fmt.Sprintf("%s/v1/sendEth?to=%s&value=%s", srvUrl, to, value)) res, err := http.Get(fmt.Sprintf("%s/v1/sendEth?to=%s&value=%s", srvUrl, to, value))
if err != nil { if err != nil {

View File

@ -9,12 +9,13 @@ import (
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rlp" "github.com/ethereum/go-ethereum/rlp"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "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" const nonExistingBlockHash = "0x111111111111111111111111111111111111111111111111111111111111111"
@ -391,6 +392,37 @@ var _ = Describe("Integration test", func() {
Expect(err).To(MatchError("header not found")) Expect(err).To(MatchError("header not found"))
Expect(gethStorage).To(Equal(ipldStorage)) 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() { Describe("eth call", func() {