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

fix encoding when storage is empty #94

Merged
merged 14 commits into from
Sep 28, 2021
17 changes: 17 additions & 0 deletions db/migrations/00014_create_stored_functions.sql
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 2 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
21 changes: 12 additions & 9 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,20 @@
# 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
fi
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"

Expand Down
8 changes: 4 additions & 4 deletions pkg/eth/eth_state_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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))
Expand Down
30 changes: 19 additions & 11 deletions pkg/eth/ipld_retriever.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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)
Expand All @@ -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"`
Expand Down Expand Up @@ -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
Expand All @@ -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())
Expand All @@ -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())
Expand All @@ -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 {
Expand All @@ -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 {
Expand Down
6 changes: 6 additions & 0 deletions pkg/graphql/graphql.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package graphql

import (
"bytes"
"context"
"database/sql"
"errors"
Expand All @@ -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"
)

Expand Down Expand Up @@ -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 {
Expand Down
4 changes: 2 additions & 2 deletions scripts/run_intregration_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 3 additions & 0 deletions test/contract/contracts/GLDToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,7 @@ contract GLDToken is ERC20 {
constructor() ERC20("Gold", "GLD") {
_mint(msg.sender, 1000000000000000000000);
}
function destroy() public {
selfdestruct(payable(msg.sender));
}
}
15 changes: 15 additions & 0 deletions test/contract/src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const fastify = require('fastify')({ logger: true });
const hre = require("hardhat");


// readiness check
fastify.get('/v1/healthz', async (req, reply) => {
reply
Expand All @@ -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;
Expand Down
17 changes: 17 additions & 0 deletions test/helper.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"`
Expand Down Expand Up @@ -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 {
Expand Down
36 changes: 34 additions & 2 deletions test/integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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() {
Expand Down