ipld-eth-server/pkg/graphql/graphql_test.go

388 lines
14 KiB
Go
Raw Normal View History

// VulcanizeDB
// Copyright © 2020 Vulcanize
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
package graphql_test
import (
2021-07-02 09:00:48 +00:00
"context"
"fmt"
"math/big"
2022-06-01 09:15:48 +00:00
"strconv"
2021-07-02 09:00:48 +00:00
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/statediff"
2022-06-01 09:15:48 +00:00
"github.com/ethereum/go-ethereum/statediff/indexer/models"
2021-08-12 06:23:41 +00:00
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
2022-03-11 04:32:22 +00:00
"github.com/jmoiron/sqlx"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
2022-05-18 08:05:16 +00:00
"github.com/vulcanize/ipld-eth-server/v3/pkg/eth"
"github.com/vulcanize/ipld-eth-server/v3/pkg/eth/test_helpers"
"github.com/vulcanize/ipld-eth-server/v3/pkg/graphql"
"github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
ethServerShared "github.com/vulcanize/ipld-eth-server/v3/pkg/shared"
)
var _ = Describe("GraphQL", func() {
2021-07-02 09:00:48 +00:00
const (
gqlEndPoint = "127.0.0.1:8083"
)
var (
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
blocks []*types.Block
receipts []types.Receipts
chain *core.BlockChain
2022-03-11 04:32:22 +00:00
db *sqlx.DB
2021-07-02 09:00:48 +00:00
blockHashes []common.Hash
backend *eth.Backend
graphQLServer *graphql.Service
chainConfig = params.TestChainConfig
mockTD = big.NewInt(1337)
client = graphql.NewClient(fmt.Sprintf("http://%s/graphql", gqlEndPoint))
ctx = context.Background()
blockHash common.Hash
contractAddress common.Address
)
It("test init", func() {
var err error
2022-03-17 10:48:18 +00:00
db = shared.SetupDB()
transformer := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
2021-07-02 09:00:48 +00:00
backend, err = eth.NewEthBackend(db, &eth.Config{
ChainConfig: chainConfig,
VMConfig: vm.Config{},
2021-07-02 09:00:48 +00:00
RPCGasCap: big.NewInt(10000000000),
GroupCacheConfig: &ethServerShared.GroupCacheConfig{
StateDB: ethServerShared.GroupConfig{
Name: "graphql_test",
CacheSizeInMB: 8,
CacheExpiryInMins: 60,
LogStatsIntervalInSecs: 0,
},
2021-08-20 07:37:11 +00:00
},
2021-07-02 09:00:48 +00:00
})
Expect(err).ToNot(HaveOccurred())
// make the test blockchain (and state)
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen)
params := statediff.Params{
IntermediateStateNodes: true,
IntermediateStorageNodes: true,
}
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
builder := statediff.NewBuilder(chain.StateCache())
for i, block := range blocks {
blockHashes = append(blockHashes, block.Hash())
var args statediff.Args
var rcts types.Receipts
if i == 0 {
args = statediff.Args{
OldStateRoot: common.Hash{},
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
} else {
args = statediff.Args{
OldStateRoot: blocks[i-1].Root(),
NewStateRoot: block.Root(),
BlockNumber: block.Number(),
BlockHash: block.Hash(),
}
rcts = receipts[i-1]
}
2022-03-11 04:32:22 +00:00
var diff sdtypes.StateObject
2021-07-02 09:00:48 +00:00
diff, err = builder.BuildStateDiffObject(args, params)
Expect(err).ToNot(HaveOccurred())
2021-08-12 06:23:41 +00:00
tx, err := transformer.PushBlock(block, rcts, mockTD)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
2021-08-12 06:23:41 +00:00
for _, node := range diff.Nodes {
2022-03-11 04:32:22 +00:00
err = transformer.PushStateNode(tx, node, block.Hash().String())
2021-08-12 06:23:41 +00:00
Expect(err).ToNot(HaveOccurred())
2021-07-02 09:00:48 +00:00
}
2022-03-11 04:32:22 +00:00
err = tx.Submit(err)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
}
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
indexAndPublisher := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
2021-07-02 09:00:48 +00:00
blockHash = test_helpers.MockBlock.Hash()
contractAddress = test_helpers.ContractAddr
2021-08-12 06:23:41 +00:00
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
Expect(err).ToNot(HaveOccurred())
2022-03-11 04:32:22 +00:00
err = tx.Submit(err)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
// The non-canonical header has a child
2021-08-12 06:23:41 +00:00
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
2021-08-12 06:23:41 +00:00
ccHash := sdtypes.CodeAndCodeHash{
Hash: test_helpers.CodeHash,
Code: test_helpers.ContractCode,
}
err = indexAndPublisher.PushCodeAndCodeHash(tx, ccHash)
Expect(err).ToNot(HaveOccurred())
2022-03-11 04:32:22 +00:00
err = tx.Submit(err)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
graphQLServer, err = graphql.New(backend, gqlEndPoint, nil, []string{"*"}, rpc.HTTPTimeouts{})
Expect(err).ToNot(HaveOccurred())
err = graphQLServer.Start(nil)
Expect(err).ToNot(HaveOccurred())
})
2021-07-02 09:00:48 +00:00
defer It("test teardown", func() {
err := graphQLServer.Stop()
Expect(err).ToNot(HaveOccurred())
2022-03-17 10:48:18 +00:00
shared.TearDownDB(db)
2021-07-02 09:00:48 +00:00
chain.Stop()
})
Describe("eth_getLogs", func() {
It("Retrieves logs that matches the provided blockHash and contract address", func() {
logs, err := client.GetLogs(ctx, blockHash, &contractAddress)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
expectedLogs := []graphql.LogResponse{
{
Topics: test_helpers.MockLog1.Topics,
Data: hexutil.Bytes(test_helpers.MockLog1.Data),
Transaction: graphql.TransactionResp{Hash: test_helpers.MockTransactions[0].Hash()},
2021-09-01 07:02:28 +00:00
ReceiptCID: test_helpers.Rct1CID.String(),
2021-08-30 15:29:54 +00:00
Status: int32(test_helpers.MockReceipts[0].Status),
2021-07-02 09:00:48 +00:00
},
}
2021-09-01 07:02:28 +00:00
Expect(logs).To(Equal(expectedLogs))
})
It("Retrieves logs for the failed receipt status that matches the provided blockHash and another contract address", func() {
logs, err := client.GetLogs(ctx, blockHash, &test_helpers.AnotherAddress2)
2021-09-01 07:02:28 +00:00
Expect(err).ToNot(HaveOccurred())
expectedLogs := []graphql.LogResponse{
{
Topics: test_helpers.MockLog6.Topics,
Data: hexutil.Bytes(test_helpers.MockLog6.Data),
Transaction: graphql.TransactionResp{Hash: test_helpers.MockTransactions[3].Hash()},
ReceiptCID: test_helpers.Rct4CID.String(),
Status: int32(test_helpers.MockReceipts[3].Status),
},
}
2021-07-02 09:00:48 +00:00
Expect(logs).To(Equal(expectedLogs))
})
It("Retrieves all the logs for the receipt that matches the provided blockHash and nil contract address", func() {
logs, err := client.GetLogs(ctx, blockHash, nil)
Expect(err).ToNot(HaveOccurred())
Expect(len(logs)).To(Equal(6))
})
2021-07-02 09:00:48 +00:00
It("Retrieves logs with random hash", func() {
logs, err := client.GetLogs(ctx, randomHash, &contractAddress)
2021-07-02 09:00:48 +00:00
Expect(err).ToNot(HaveOccurred())
Expect(len(logs)).To(Equal(0))
})
})
Describe("eth_getStorageAt", func() {
It("Retrieves the storage value at the provided contract address and storage leaf key at the block with the provided hash", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[2], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("01")))
storageRes, err = client.GetStorageAt(ctx, blockHashes[3], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("03")))
storageRes, err = client.GetStorageAt(ctx, blockHashes[4], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.HexToHash("09")))
})
It("Retrieves empty data if it tries to access a contract at a blockHash which does not exist", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[0], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
storageRes, err = client.GetStorageAt(ctx, blockHashes[1], contractAddress, test_helpers.IndexOne)
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
})
It("Retrieves empty data if it tries to access a contract slot which does not exist", func() {
storageRes, err := client.GetStorageAt(ctx, blockHashes[3], contractAddress, randomHash.Hex())
Expect(err).ToNot(HaveOccurred())
Expect(storageRes.Value).To(Equal(common.Hash{}))
})
})
2022-06-01 09:15:48 +00:00
Describe("allEthHeaderCids", func() {
It("Retrieves header_cids that matches the provided blockNumber", func() {
allEthHeaderCidsResp, err := client.AllEthHeaderCids(ctx, graphql.EthHeaderCidCondition{BlockNumber: new(graphql.BigInt).SetUint64(2)})
Expect(err).ToNot(HaveOccurred())
headerCIDs, txCIDs, err := backend.Retriever.RetrieveHeaderAndTxCIDsByBlockNumber(2)
Expect(err).ToNot(HaveOccurred())
// Begin tx
tx, err := backend.DB.Beginx()
Expect(err).ToNot(HaveOccurred())
defer func() {
if p := recover(); p != nil {
shared.Rollback(tx)
panic(p)
} else if err != nil {
shared.Rollback(tx)
} else {
err = tx.Commit()
}
}()
headerIPLDs, err := backend.Fetcher.FetchHeaders(tx, headerCIDs)
Expect(err).ToNot(HaveOccurred())
for idx, headerCID := range headerCIDs {
ethHeaderCid := allEthHeaderCidsResp.Nodes[idx]
compareEthHeaderCid(ethHeaderCid, headerCID, txCIDs[idx], headerIPLDs[idx])
}
})
It("Retrieves header_cids that matches the provided blockHash", func() {
blockHash := blocks[1].Hash().String()
2022-06-01 09:15:48 +00:00
allEthHeaderCidsResp, err := client.AllEthHeaderCids(ctx, graphql.EthHeaderCidCondition{BlockHash: &blockHash})
Expect(err).ToNot(HaveOccurred())
headerCID, txCIDs, err := backend.Retriever.RetrieveHeaderAndTxCIDsByBlockHash(blocks[1].Hash())
2022-06-01 09:15:48 +00:00
Expect(err).ToNot(HaveOccurred())
// Begin tx
tx, err := backend.DB.Beginx()
Expect(err).ToNot(HaveOccurred())
defer func() {
if p := recover(); p != nil {
shared.Rollback(tx)
panic(p)
} else if err != nil {
shared.Rollback(tx)
} else {
err = tx.Commit()
}
}()
headerIPLDs, err := backend.Fetcher.FetchHeaders(tx, []models.HeaderModel{headerCID})
Expect(err).ToNot(HaveOccurred())
Expect(len(allEthHeaderCidsResp.Nodes)).To(Equal(1))
Expect(len(allEthHeaderCidsResp.Nodes)).To(Equal(1))
ethHeaderCid := allEthHeaderCidsResp.Nodes[0]
compareEthHeaderCid(ethHeaderCid, headerCID, txCIDs, headerIPLDs[0])
})
})
Describe("ethTransactionCidByTxHash", func() {
It("Retrieves tx_cid that matches the provided txHash", func() {
txHash := blocks[2].Transactions()[0].Hash().String()
ethTransactionCidResp, err := client.EthTransactionCidByTxHash(ctx, txHash)
Expect(err).ToNot(HaveOccurred())
txCID, err := backend.Retriever.RetrieveTxCIDByHash(txHash)
Expect(err).ToNot(HaveOccurred())
compareEthTxCid(*ethTransactionCidResp, txCID)
// Begin tx
tx, err := backend.DB.Beginx()
Expect(err).ToNot(HaveOccurred())
defer func() {
if p := recover(); p != nil {
shared.Rollback(tx)
panic(p)
} else if err != nil {
shared.Rollback(tx)
} else {
err = tx.Commit()
}
}()
txIPLDs, err := backend.Fetcher.FetchTrxs(tx, []models.TxModel{txCID})
Expect(err).ToNot(HaveOccurred())
Expect(len(txIPLDs)).To(Equal(1))
Expect(ethTransactionCidResp.BlockByMhKey.Data).To(Equal(graphql.Bytes(txIPLDs[0].Data).String()))
})
})
})
2022-06-01 09:15:48 +00:00
func compareEthHeaderCid(ethHeaderCid graphql.EthHeaderCidResp, headerCID models.HeaderModel, txCIDs []models.TxModel, headerIPLD models.IPLDModel) {
blockNumber, err := strconv.ParseInt(headerCID.BlockNumber, 10, 64)
Expect(err).ToNot(HaveOccurred())
td, err := strconv.ParseInt(headerCID.TotalDifficulty, 10, 64)
Expect(err).ToNot(HaveOccurred())
Expect(ethHeaderCid.Cid).To(Equal(headerCID.CID))
Expect(ethHeaderCid.BlockNumber).To(Equal(*new(graphql.BigInt).SetUint64(uint64(blockNumber))))
Expect(ethHeaderCid.BlockHash).To(Equal(headerCID.BlockHash))
Expect(ethHeaderCid.ParentHash).To(Equal(headerCID.ParentHash))
Expect(ethHeaderCid.Timestamp).To(Equal(*new(graphql.BigInt).SetUint64(headerCID.Timestamp)))
Expect(ethHeaderCid.StateRoot).To(Equal(headerCID.StateRoot))
Expect(ethHeaderCid.Td).To(Equal(*new(graphql.BigInt).SetUint64(uint64(td))))
Expect(ethHeaderCid.TxRoot).To(Equal(headerCID.TxRoot))
Expect(ethHeaderCid.ReceiptRoot).To(Equal(headerCID.RctRoot))
Expect(ethHeaderCid.UncleRoot).To(Equal(headerCID.UncleRoot))
Expect(ethHeaderCid.Bloom).To(Equal(graphql.Bytes(headerCID.Bloom).String()))
for tIdx, txCID := range txCIDs {
ethTxCid := ethHeaderCid.EthTransactionCidsByHeaderId.Nodes[tIdx]
compareEthTxCid(ethTxCid, txCID)
2022-06-01 09:15:48 +00:00
}
Expect(ethHeaderCid.BlockByMhKey.Data).To(Equal(graphql.Bytes(headerIPLD.Data).String()))
Expect(ethHeaderCid.BlockByMhKey.Key).To(Equal(headerIPLD.Key))
}
func compareEthTxCid(ethTxCid graphql.EthTransactionCidResp, txCID models.TxModel) {
Expect(ethTxCid.Cid).To(Equal(txCID.CID))
Expect(ethTxCid.TxHash).To(Equal(txCID.TxHash))
Expect(ethTxCid.Index).To(Equal(int32(txCID.Index)))
Expect(ethTxCid.Src).To(Equal(txCID.Src))
Expect(ethTxCid.Dst).To(Equal(txCID.Dst))
}