go-ethereum/graphql/graphql_test.go
Péter Garamvölgyi 983d630244
feat: Sync and relay L1 messages (#350)
* add l1 config in genesis config (#249)

* add l1 config in genesis config

* fix lint

* Update params/config.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* extend node configuration (#251)

* extend node configuration

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* fix typos

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* Fix/improve node config parsing (#260)

* raise error on failed parsing

* default value

* add l1-message-type, transaction methods (#252)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* txpool l1 check, pointer change, marhsal test

* draft: start implementing l1message gas behavior

* draft: start implementing l1message gas behavior

* change to gas usage

* error comment typo

Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* goimports

* update nonce, add hash test (fails), marshal test

* goimports

* target addr cant be nil

* change call msg

* comment out test

* lint

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* Add L1 message database (#255)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* remove comments

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* add range check

* fix tests

* update comments

* nit

* support blocks with 0 l1 messages

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269)

* deep copy value field, add tx hash test

comment

* typo

* Rename nonce to queueindex, increment sender nonce on L1 message execution (#271)

* change nonce to queueindex, increment nonce on L1 message

* fix db acccessors

* Update core/types/transaction_marshalling.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* Fix db inspect command (#276)

fix db inspect command

* Add l1 sync service (#256)

* extend node configuration

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* add L1 message store to rawdb

* remove comments

* fix typos

* add L1 message sync service

* use l1 contract address and chain ID

* use L1DeploymentBlock

* add confirmation config

* move bridge client to separate file

* use uint64 block number

* fix bigint comparison

* rename constants

* add more logs

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* check if config is provided

* improve sync service DB batched writes

* add range check

* fix tests

* update comments

* nit

* fix flush range and improve comments

* solve circular dependency

* update stress tests

* initialize l1 client for geth

* start sync service

* add more comments

* check nil correctly

* address comments

* fix merge

* fix genesis l1config deserialization

* add sync progress logs

* initial sync

* handle leveldb not found error

* use errors.Is

* address comments

* update DefaultPollInterval

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>
Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* Add L1 message validation (#272)

* add L1 message validation

* add comments and better error handling

* handle leveldb not found error

* update incorrect condition for genesis block

* typo

* change inclusion index logic

* disable L1 message check for legacy tests

* set NumL1MessagesPerBlock to 0 in tests

* update default genesis config

* Add L1 msg validation tests (#303)

add L1 msg validation tests

* Update miner include l1 messages (#265)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* add L1 message sync service

* remove comments

* use l1 contract address and chain ID

* extend node configuration

* use block number instead of hash

* accept safe, finalized and numbers for L1Confirmations

* fix typos

* use L1DeploymentBlock

* add confirmation config

* move bridge client to separate file

* use uint64 block number

* fix bigint comparison

* rename constants

* add more logs

* Fix/improve node config parsing (#260)

* raise error on failed parsing

* default value

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* check if config is provided

* improve sync service DB batched writes

* include l1 messages in blocks: part 1

* add l1-message-type, transaction methods (#252)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* txpool l1 check, pointer change, marhsal test

* draft: start implementing l1message gas behavior

* draft: start implementing l1message gas behavior

* change to gas usage

* error comment typo

Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* goimports

* update nonce, add hash test (fails), marshal test

* goimports

* target addr cant be nil

* change call msg

* comment out test

* lint

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>

* Add L1 message database (#255)

* add l1-message-type, transaction methods

* goimports

* Update core/types/transaction.go

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* add L1 message store to rawdb

* remove comments

* rename to l1_message

* rename variables and add comments

* write l1 msgs in a batch

* add more comments

* update tests

* allow batched and non-batched writes

* rename to accessors_l1_message

* handle error

* add range check

* fix tests

* update comments

* nit

* support blocks with 0 l1 messages

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>

* build(docker): auto docker push when pushing git tags (#258)

* build(docker): update docker trigger tag prefix (#259)

* Fix L1Message Deep Copy, Complete Bridge Tx Hash test (#269)

* deep copy value field, add tx hash test

comment

* typo

* commitl1messages

* lint

* Revert "add L1 message sync service"

This reverts commit 5305e8a5de14766ed249e1a7d64042c7a72cf5c2.

* Revert "move bridge client to separate file"

This reverts commit 0b220bee37de93c3250545e23430db2c401a2f90.

* update branch

* use commitMessages for l1Txs

* little fix

* fix config

* fix test

* comment fixes

* fix

* fix config check

---------

Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>
Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>

* Add ErrUnknownAncestor tests (#305)

add ErrUnknownAncestor tests

* worker test include l1 msgs (#306)

* worker test include l1 msgs

* move L1 message index update next to block insertion

---------

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* exclude l1 messages from transaction count limit in block (#307)

* exclude l1 messages from transaction count limit in block

* fix comments

* trigger ci

* nit

---------

Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>
Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* Expose queueIndex on Transaction (#316)

expose queueIndex on Transaction

* test that l1msg doesn't count in maxTxPerBlock limit (#312)

* test that l1msg doesn't count in maxTxPerBlock limit

* fix, comment

* retrigger ci

* change order inside test

---------

Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: Péter Garamvölgyi <th307q@gmail.com>

* reuse trace nonce field for queueIndex

* expose scroll APIs on the geth console

* add L1 message query APIs

* Trigger new block on new l1 messages (#343)

* trigger new block on new l1 messages

* typo

* initialize l1MsgCh

* fix worker l1msg tests (#345)

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>

* test(worker): ensure that l1 messages are included in the correct order (#346)

test that l1msgs added in correct order

* rename enqueueIndex --> queueIndex

* move QueueIndex into transaction

* improve l1 db interface

* formatting

* bump version

* print l1config

* add API to query latest included message queue index

* clean up tx limit logic

* add clarifying comments and todos to ValidateL1Messages

* improve db comments and logs

* clean up L1MessageTx type handling

* format

* format

* improve L1 message block check

* fix missing L1 event handling

* fix TestL1MessageValidationFailure

* simplify sync height resume logic

* make l1Config.l1MessageQueueAddress non-pointer

* improve command line flags

* remove todo

* use abigen tools for log filtering

* cache block L1 message count

* nit: fix variable name case

* improve logs

* flush pending writes to DB before shutdown

---------

Co-authored-by: Nazarii Denha <dengaaa2002@gmail.com>
Co-authored-by: Max Wolff <max@scroll.io>
Co-authored-by: Haichen Shen <shenhaichen@gmail.com>
Co-authored-by: Max Wolff <maxcwolff@gmail.com>
Co-authored-by: HAOYUatHZ <37070449+HAOYUatHZ@users.noreply.github.com>
Co-authored-by: HAOYUatHZ <haoyu@protonmail.com>
2023-06-07 07:24:19 -07:00

357 lines
12 KiB
Go

// Copyright 2019 The go-ethereum Authors
// This file is part of the go-ethereum library.
//
// The go-ethereum library is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// The go-ethereum library 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 Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
package graphql
import (
"fmt"
"io/ioutil"
"math/big"
"net/http"
"strings"
"testing"
"time"
"github.com/scroll-tech/go-ethereum/common"
"github.com/scroll-tech/go-ethereum/consensus/ethash"
"github.com/scroll-tech/go-ethereum/core"
"github.com/scroll-tech/go-ethereum/core/types"
"github.com/scroll-tech/go-ethereum/core/vm"
"github.com/scroll-tech/go-ethereum/crypto"
"github.com/scroll-tech/go-ethereum/eth"
"github.com/scroll-tech/go-ethereum/eth/ethconfig"
"github.com/scroll-tech/go-ethereum/node"
"github.com/scroll-tech/go-ethereum/params"
"github.com/stretchr/testify/assert"
)
func TestBuildSchema(t *testing.T) {
ddir, err := ioutil.TempDir("", "graphql-buildschema")
if err != nil {
t.Fatalf("failed to create temporary datadir: %v", err)
}
// Copy config
conf := node.DefaultConfig
conf.DataDir = ddir
stack, err := node.New(&conf)
if err != nil {
t.Fatalf("could not create new node: %v", err)
}
// Make sure the schema can be parsed and matched up to the object model.
if err := newHandler(stack, nil, []string{}, []string{}); err != nil {
t.Errorf("Could not construct GraphQL handler: %v", err)
}
}
// Tests that a graphQL request is successfully handled when graphql is enabled on the specified endpoint
func TestGraphQLBlockSerialization(t *testing.T) {
stack := createNode(t, true, false)
defer stack.Close()
// start node
if err := stack.Start(); err != nil {
t.Fatalf("could not start node: %v", err)
}
for i, tt := range []struct {
body string
want string
code int
}{
{ // Should return latest block
body: `{"query": "{block{number}}","variables": null}`,
want: `{"data":{"block":{"number":10}}}`,
code: 200,
},
{ // Should return info about latest block
body: `{"query": "{block{number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":{"number":10,"gasUsed":0,"gasLimit":11500000}}}`,
code: 200,
},
{
body: `{"query": "{block(number:0){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`,
code: 200,
},
{
body: `{"query": "{block(number:-1){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":null}}`,
code: 200,
},
{
body: `{"query": "{block(number:-500){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":null}}`,
code: 200,
},
{
body: `{"query": "{block(number:\"0\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":{"number":0,"gasUsed":0,"gasLimit":11500000}}}`,
code: 200,
},
{
body: `{"query": "{block(number:\"-33\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":null}}`,
code: 200,
},
{
body: `{"query": "{block(number:\"1337\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"data":{"block":null}}`,
code: 200,
},
{
body: `{"query": "{block(number:\"0xbad\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0xbad\": invalid syntax"}],"data":{}}`,
code: 400,
},
{ // hex strings are currently not supported. If that's added to the spec, this test will need to change
body: `{"query": "{block(number:\"0x0\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"errors":[{"message":"strconv.ParseInt: parsing \"0x0\": invalid syntax"}],"data":{}}`,
code: 400,
},
{
body: `{"query": "{block(number:\"a\"){number,gasUsed,gasLimit}}","variables": null}`,
want: `{"errors":[{"message":"strconv.ParseInt: parsing \"a\": invalid syntax"}],"data":{}}`,
code: 400,
},
{
body: `{"query": "{bleh{number}}","variables": null}"`,
want: `{"errors":[{"message":"Cannot query field \"bleh\" on type \"Query\".","locations":[{"line":1,"column":2}]}]}`,
code: 400,
},
// should return `estimateGas` as decimal
{
body: `{"query": "{block{ estimateGas(data:{}) }}"}`,
want: `{"data":{"block":{"estimateGas":53000}}}`,
code: 200,
},
// should return `status` as decimal
{
body: `{"query": "{block {number call (data : {from : \"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b\", to: \"0x6295ee1b4f6dd65047762f924ecd367c17eabf8f\", data :\"0x12a7b914\"}){data status}}}"}`,
want: `{"data":{"block":{"number":10,"call":{"data":"0x","status":1}}}}`,
code: 200,
},
} {
resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", strings.NewReader(tt.body))
if err != nil {
t.Fatalf("could not post: %v", err)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("could not read from response body: %v", err)
}
if have := string(bodyBytes); have != tt.want {
t.Errorf("testcase %d %s,\nhave:\n%v\nwant:\n%v", i, tt.body, have, tt.want)
}
if tt.code != resp.StatusCode {
t.Errorf("testcase %d %s,\nwrong statuscode, have: %v, want: %v", i, tt.body, resp.StatusCode, tt.code)
}
}
}
func TestGraphQLBlockSerializationEIP2718(t *testing.T) {
stack := createNode(t, true, true)
defer stack.Close()
// start node
if err := stack.Start(); err != nil {
t.Fatalf("could not start node: %v", err)
}
for i, tt := range []struct {
body string
want string
code int
}{
{
body: `{"query": "{block {number transactions { from { address } to { address } value hash type accessList { address storageKeys } index}}}"}`,
want: `{"data":{"block":{"number":1,"transactions":[{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x64","hash":"0xd864c9d7d37fade6b70164740540c06dd58bb9c3f6b46101908d6339db6a6a7b","type":0,"accessList":[],"index":0},{"from":{"address":"0x71562b71999873db5b286df957af199ec94617f7"},"to":{"address":"0x0000000000000000000000000000000000000dad"},"value":"0x32","hash":"0x19b35f8187b4e15fb59a9af469dca5dfa3cd363c11d372058c12f6482477b474","type":1,"accessList":[{"address":"0x0000000000000000000000000000000000000dad","storageKeys":["0x0000000000000000000000000000000000000000000000000000000000000000"]}],"index":1}]}}}`,
code: 200,
},
} {
resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", strings.NewReader(tt.body))
if err != nil {
t.Fatalf("could not post: %v", err)
}
bodyBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
t.Fatalf("could not read from response body: %v", err)
}
if have := string(bodyBytes); have != tt.want {
t.Errorf("testcase %d %s,\nhave:\n%v\nwant:\n%v", i, tt.body, have, tt.want)
}
if tt.code != resp.StatusCode {
t.Errorf("testcase %d %s,\nwrong statuscode, have: %v, want: %v", i, tt.body, resp.StatusCode, tt.code)
}
}
}
// Tests that a graphQL request is not handled successfully when graphql is not enabled on the specified endpoint
func TestGraphQLHTTPOnSamePort_GQLRequest_Unsuccessful(t *testing.T) {
stack := createNode(t, false, false)
defer stack.Close()
if err := stack.Start(); err != nil {
t.Fatalf("could not start node: %v", err)
}
body := strings.NewReader(`{"query": "{block{number}}","variables": null}`)
resp, err := http.Post(fmt.Sprintf("%s/graphql", stack.HTTPEndpoint()), "application/json", body)
if err != nil {
t.Fatalf("could not post: %v", err)
}
// make sure the request is not handled successfully
assert.Equal(t, http.StatusNotFound, resp.StatusCode)
}
func createNode(t *testing.T, gqlEnabled bool, txEnabled bool) *node.Node {
stack, err := node.New(&node.Config{
HTTPHost: "127.0.0.1",
HTTPPort: 0,
WSHost: "127.0.0.1",
WSPort: 0,
})
if err != nil {
t.Fatalf("could not create node: %v", err)
}
if !gqlEnabled {
return stack
}
if !txEnabled {
createGQLService(t, stack)
} else {
createGQLServiceWithTransactions(t, stack)
}
return stack
}
func createGQLService(t *testing.T, stack *node.Node) {
// create backend
ethConf := &ethconfig.Config{
Genesis: &core.Genesis{
Config: params.AllEthashProtocolChanges,
GasLimit: 11500000,
Difficulty: big.NewInt(1048576),
},
Ethash: ethash.Config{
PowMode: ethash.ModeFake,
},
NetworkId: 1337,
TrieCleanCache: 5,
TrieCleanCacheJournal: "triecache",
TrieCleanCacheRejournal: 60 * time.Minute,
TrieDirtyCache: 5,
TrieTimeout: 60 * time.Minute,
SnapshotCache: 5,
}
ethBackend, err := eth.New(stack, ethConf, nil)
if err != nil {
t.Fatalf("could not create eth backend: %v", err)
}
// Create some blocks and import them
chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(),
ethash.NewFaker(), ethBackend.ChainDb(), 10, func(i int, gen *core.BlockGen) {})
_, err = ethBackend.BlockChain().InsertChain(chain)
if err != nil {
t.Fatalf("could not create import blocks: %v", err)
}
// create gql service
err = New(stack, ethBackend.APIBackend, []string{}, []string{})
if err != nil {
t.Fatalf("could not create graphql service: %v", err)
}
}
func createGQLServiceWithTransactions(t *testing.T, stack *node.Node) {
// create backend
key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address := crypto.PubkeyToAddress(key.PublicKey)
funds := big.NewInt(1000000000000000)
dad := common.HexToAddress("0x0000000000000000000000000000000000000dad")
ethConf := &ethconfig.Config{
Genesis: &core.Genesis{
Config: params.AllEthashProtocolChanges,
GasLimit: 11500000,
Difficulty: big.NewInt(1048576),
Alloc: core.GenesisAlloc{
address: {Balance: funds},
// The address 0xdad sloads 0x00 and 0x01
dad: {
Code: []byte{
byte(vm.PC),
byte(vm.PC),
byte(vm.SLOAD),
byte(vm.SLOAD),
},
Nonce: 0,
Balance: big.NewInt(0),
},
},
BaseFee: big.NewInt(params.InitialBaseFee),
},
Ethash: ethash.Config{
PowMode: ethash.ModeFake,
},
NetworkId: 1337,
TrieCleanCache: 5,
TrieCleanCacheJournal: "triecache",
TrieCleanCacheRejournal: 60 * time.Minute,
TrieDirtyCache: 5,
TrieTimeout: 60 * time.Minute,
SnapshotCache: 5,
}
ethBackend, err := eth.New(stack, ethConf, nil)
if err != nil {
t.Fatalf("could not create eth backend: %v", err)
}
signer := types.LatestSigner(ethConf.Genesis.Config)
legacyTx, _ := types.SignNewTx(key, signer, &types.LegacyTx{
Nonce: uint64(0),
To: &dad,
Value: big.NewInt(100),
Gas: 50000,
GasPrice: big.NewInt(params.InitialBaseFee),
})
envelopTx, _ := types.SignNewTx(key, signer, &types.AccessListTx{
ChainID: ethConf.Genesis.Config.ChainID,
Nonce: uint64(1),
To: &dad,
Gas: 30000,
GasPrice: big.NewInt(params.InitialBaseFee),
Value: big.NewInt(50),
AccessList: types.AccessList{{
Address: dad,
StorageKeys: []common.Hash{{0}},
}},
})
// Create some blocks and import them
chain, _ := core.GenerateChain(params.AllEthashProtocolChanges, ethBackend.BlockChain().Genesis(),
ethash.NewFaker(), ethBackend.ChainDb(), 1, func(i int, b *core.BlockGen) {
b.SetCoinbase(common.Address{1})
b.AddTx(legacyTx)
b.AddTx(envelopTx)
})
_, err = ethBackend.BlockChain().InsertChain(chain)
if err != nil {
t.Fatalf("could not create import blocks: %v", err)
}
// create gql service
err = New(stack, ethBackend.APIBackend, []string{}, []string{})
if err != nil {
t.Fatalf("could not create graphql service: %v", err)
}
}