Add graphql tests.
This commit is contained in:
parent
9a5581b543
commit
f1a61d0991
1
go.mod
1
go.mod
@ -12,6 +12,7 @@ require (
|
||||
github.com/ipfs/go-ipld-format v0.2.0
|
||||
github.com/jmoiron/sqlx v1.2.0
|
||||
github.com/lib/pq v1.8.0
|
||||
github.com/machinebox/graphql v0.2.2 // indirect
|
||||
github.com/multiformats/go-multihash v0.0.14
|
||||
github.com/onsi/ginkgo v1.15.0
|
||||
github.com/onsi/gomega v1.10.1
|
||||
|
2
go.sum
2
go.sum
@ -697,6 +697,8 @@ github.com/libp2p/go-yamux v1.3.3/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZ
|
||||
github.com/libp2p/go-yamux v1.3.5/go.mod h1:FGTiPvoV/3DVdgWpX+tM0OW3tsM+W5bSE3gZwqQTcow=
|
||||
github.com/lucas-clemente/quic-go v0.15.7/go.mod h1:Myi1OyS0FOjL3not4BxT7KN29bRkcMUV5JVVFLKtDp8=
|
||||
github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI=
|
||||
github.com/machinebox/graphql v0.2.2 h1:dWKpJligYKhYKO5A2gvNhkJdQMNZeChZYyBbrZkBZfo=
|
||||
github.com/machinebox/graphql v0.2.2/go.mod h1:F+kbVMHuwrQ5tYgU9JXlnskM8nOaFxCAEolaQybkjWA=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
|
104
pkg/graphql/client.go
Normal file
104
pkg/graphql/client.go
Normal file
@ -0,0 +1,104 @@
|
||||
package graphql
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
gqlclient "github.com/machinebox/graphql"
|
||||
)
|
||||
|
||||
type StorageResponse struct {
|
||||
Cid string `json:"cid"`
|
||||
Value common.Hash `json:"value"`
|
||||
IpldBlock hexutil.Bytes `json:"ipldBlock"`
|
||||
}
|
||||
|
||||
type GetStorageAt struct {
|
||||
Response StorageResponse `json:"getStorageAt"`
|
||||
}
|
||||
|
||||
type LogResponse struct {
|
||||
Topics []common.Hash `json:"topics"`
|
||||
Data hexutil.Bytes `json:"data"`
|
||||
}
|
||||
|
||||
type GetLogs struct {
|
||||
Responses []LogResponse `json:"getLogs"`
|
||||
}
|
||||
|
||||
type Client struct {
|
||||
client *gqlclient.Client
|
||||
}
|
||||
|
||||
func NewClient(endpoint string) *Client {
|
||||
client := gqlclient.NewClient(endpoint)
|
||||
return &Client{client: client}
|
||||
}
|
||||
|
||||
func (c *Client) GetLogs(ctx context.Context, hash common.Hash, address common.Address) ([]LogResponse, error) {
|
||||
getLogsQuery := fmt.Sprintf(`
|
||||
query{
|
||||
getLogs(blockHash: "%s", contract: "%s") {
|
||||
data
|
||||
topics
|
||||
}
|
||||
}
|
||||
`, hash.String(), address.String())
|
||||
|
||||
req := gqlclient.NewRequest(getLogsQuery)
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
|
||||
var respData map[string]interface{}
|
||||
err := c.client.Run(ctx, req, &respData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonStr, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var logs GetLogs
|
||||
err = json.Unmarshal(jsonStr, &logs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return logs.Responses, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetStorageAt(ctx context.Context, hash common.Hash, address common.Address, slot string) (*StorageResponse, error) {
|
||||
getLogsQuery := fmt.Sprintf(`
|
||||
query{
|
||||
getStorageAt(blockHash: "%s", contract: "%s",slot: "%s") {
|
||||
cid
|
||||
value
|
||||
ipldBlock
|
||||
}
|
||||
}
|
||||
`, hash.String(), address.String(), common.HexToHash(slot))
|
||||
|
||||
req := gqlclient.NewRequest(getLogsQuery)
|
||||
req.Header.Set("Cache-Control", "no-cache")
|
||||
|
||||
var respData map[string]interface{}
|
||||
err := c.client.Run(ctx, req, &respData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
jsonStr, err := json.Marshal(respData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var storageAt GetStorageAt
|
||||
err = json.Unmarshal(jsonStr, &storageAt)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &storageAt.Response, nil
|
||||
}
|
@ -985,7 +985,7 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
|
||||
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ret := StorageResult{value: ([]byte{}), cid: "", ipldBlock: ([]byte{})}
|
||||
ret := StorageResult{value: []byte{}, cid: "", ipldBlock: []byte{}}
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
@ -999,12 +999,7 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := StorageResult{value: value.([]byte), cid: cid, ipldBlock: ipldBlock}
|
||||
|
||||
return &ret, nil
|
||||
}
|
||||
|
||||
|
@ -17,15 +17,213 @@
|
||||
package graphql_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"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/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
eth2 "github.com/vulcanize/ipld-eth-indexer/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/postgres"
|
||||
"github.com/vulcanize/ipld-eth-indexer/pkg/shared"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||
"github.com/vulcanize/ipld-eth-server/pkg/graphql"
|
||||
)
|
||||
|
||||
var _ = Describe("GraphQL", func() {
|
||||
It("Builds the schema and creates a new handler", func() {
|
||||
_, err := graphql.NewHandler(nil)
|
||||
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
|
||||
db *postgres.DB
|
||||
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
|
||||
db, err = shared.SetupDB()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
transformer := eth2.NewStateDiffTransformer(chainConfig, db)
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VmConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
})
|
||||
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]
|
||||
}
|
||||
|
||||
var diff statediff.StateObject
|
||||
diff, err = builder.BuildStateDiffObject(args, params)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
diffRlp, err := rlp.EncodeToBytes(diff)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
blockRlp, err := rlp.EncodeToBytes(block)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
receiptsRlp, err := rlp.EncodeToBytes(rcts)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
payload := statediff.Payload{
|
||||
StateObjectRlp: diffRlp,
|
||||
BlockRlp: blockRlp,
|
||||
ReceiptsRlp: receiptsRlp,
|
||||
TotalDifficulty: mockTD,
|
||||
}
|
||||
|
||||
_, err = transformer.Transform(0, payload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
|
||||
indexAndPublisher := eth2.NewIPLDPublisher(db)
|
||||
blockHash = test_helpers.MockBlock.Hash()
|
||||
contractAddress = test_helpers.ContractAddr
|
||||
|
||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayload)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// The non-canonical header has a child
|
||||
err = indexAndPublisher.Publish(test_helpers.MockConvertedPayloadForChild)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = publishCode(db, test_helpers.ContractCodeHash, test_helpers.ContractCode)
|
||||
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())
|
||||
})
|
||||
|
||||
defer It("test teardown", func() {
|
||||
err := graphQLServer.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
eth.TearDownDB(db)
|
||||
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)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expectedLogs := []graphql.LogResponse{
|
||||
{
|
||||
Topics: test_helpers.MockLog1.Topics,
|
||||
Data: hexutil.Bytes(test_helpers.MockLog1.Data),
|
||||
},
|
||||
}
|
||||
Expect(logs).To(Equal(expectedLogs))
|
||||
})
|
||||
|
||||
It("Retrieves logs with random hash", func() {
|
||||
logs, err := client.GetLogs(ctx, randomHash, contractAddress)
|
||||
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{}))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
func publishCode(db *postgres.DB, codeHash common.Hash, code []byte) error {
|
||||
tx, err := db.Beginx()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mhKey, err := shared.MultihashKeyFromKeccak256(codeHash)
|
||||
if err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
if err := shared.PublishDirect(tx, mhKey, code); err != nil {
|
||||
_ = tx.Rollback()
|
||||
return err
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user