forked from cerc-io/ipld-eth-server
Upgrade to v5 schema
Now uses: * ipld direct_by_leaf StateDB for basic queries * trie_by_cid StateDB for trie slice and proof queries Also: * vulcanize => cerc refactor * Backend method to close dbs * state tests are in multiple packages, to allow separate ginkgo suites * removes gap-filler module * integration tests and github workflows * run stack-orchestrator for testnet * fix various issues with tests, hardhat server, dockerfile * fix cmd flags / env vars * fix flaky tests and clean up code * remove unused code, scripts * remove outdated docs * update version
This commit is contained in:
Vendored
BIN
Binary file not shown.
@@ -25,9 +25,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
)
|
||||
|
||||
var _ tracers.Backend = &Backend{}
|
||||
@@ -43,9 +42,7 @@ type Backend struct {
|
||||
|
||||
// StateAtBlock retrieves the state database associated with a certain block
|
||||
func (b *Backend) StateAtBlock(ctx context.Context, block *types.Block, reexec uint64, base *state.StateDB, checkLive, preferDisk bool) (*state.StateDB, tracers.StateReleaseFunc, error) {
|
||||
rpcBlockNumber := rpc.BlockNumber(block.NumberU64())
|
||||
statedb, _, err := b.StateAndHeaderByNumberOrHash(ctx, rpc.BlockNumberOrHashWithNumber(rpcBlockNumber))
|
||||
return statedb, func() {}, err
|
||||
return nil, func() {}, errMethodNotSupported
|
||||
}
|
||||
|
||||
// StateAtTransaction returns the execution environment of a certain transaction
|
||||
|
||||
+23
-25
@@ -27,9 +27,6 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
ipld_eth_statedb "github.com/cerc-io/ipld-eth-statedb"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
@@ -43,7 +40,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
ipld_direct_state "github.com/cerc-io/ipld-eth-statedb/direct_by_leaf"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -165,10 +164,10 @@ func (pea *PublicEthAPI) BlockNumber() hexutil.Uint64 {
|
||||
}
|
||||
|
||||
// GetBlockByNumber returns the requested canonical block.
|
||||
// * When blockNr is -1 the chain head is returned.
|
||||
// * We cannot support pending block calls since we do not have an active miner
|
||||
// * When fullTx is true all transactions in the block are returned, otherwise
|
||||
// only the transaction hash is returned.
|
||||
// - When blockNr is -1 the chain head is returned.
|
||||
// - We cannot support pending block calls since we do not have an active miner
|
||||
// - When fullTx is true all transactions in the block are returned, otherwise
|
||||
// only the transaction hash is returned.
|
||||
func (pea *PublicEthAPI) GetBlockByNumber(ctx context.Context, number rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
|
||||
block, err := pea.B.BlockByNumber(ctx, number)
|
||||
if block != nil && err == nil {
|
||||
@@ -612,6 +611,7 @@ func (pea *PublicEthAPI) localGetTransactionReceipt(ctx context.Context, hash co
|
||||
from, _ := types.Sender(signer, tx)
|
||||
|
||||
fields := map[string]interface{}{
|
||||
"type": hexutil.Uint64(receipt.Type),
|
||||
"blockHash": blockHash,
|
||||
"blockNumber": hexutil.Uint64(blockNumber),
|
||||
"transactionHash": hash,
|
||||
@@ -780,7 +780,9 @@ State and Storage
|
||||
// block numbers are also allowed.
|
||||
func (pea *PublicEthAPI) GetBalance(ctx context.Context, address common.Address, blockNrOrHash rpc.BlockNumberOrHash) (*hexutil.Big, error) {
|
||||
bal, err := pea.localGetBalance(ctx, address, blockNrOrHash)
|
||||
if bal != nil && err == nil {
|
||||
if err != nil && err != sql.ErrNoRows {
|
||||
return nil, err
|
||||
} else if bal != nil {
|
||||
return bal, nil
|
||||
}
|
||||
if pea.config.ProxyOnError {
|
||||
@@ -878,7 +880,7 @@ func (pea *PublicEthAPI) GetProof(ctx context.Context, address common.Address, s
|
||||
|
||||
// this continues to use ipfs-ethdb based geth StateDB as it requires trie access
|
||||
func (pea *PublicEthAPI) localGetProof(ctx context.Context, address common.Address, storageKeys []string, blockNrOrHash rpc.BlockNumberOrHash) (*AccountResult, error) {
|
||||
state, _, err := pea.B.StateAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
state, _, err := pea.B.IPLDTrieStateDBAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -982,7 +984,7 @@ type OverrideAccount struct {
|
||||
type StateOverride map[common.Address]OverrideAccount
|
||||
|
||||
// Apply overrides the fields of specified accounts into the given state.
|
||||
func (diff *StateOverride) Apply(state *ipld_eth_statedb.StateDB) error {
|
||||
func (diff *StateOverride) Apply(state *ipld_direct_state.StateDB) error {
|
||||
if diff == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -1059,7 +1061,7 @@ func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.Bl
|
||||
log.Debugxf(ctx, "Executing EVM call finished %s runtime %s", time.Now().String(), time.Since(start).String())
|
||||
}(time.Now())
|
||||
|
||||
state, header, err := b.IPLDStateDBAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
state, header, err := b.IPLDDirectStateDBAndHeaderByNumberOrHash(ctx, blockNrOrHash)
|
||||
if state == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -1110,7 +1112,7 @@ func DoCall(ctx context.Context, b *Backend, args CallArgs, blockNrOrHash rpc.Bl
|
||||
return nil, fmt.Errorf("execution aborted (timeout = %v)", timeout)
|
||||
}
|
||||
if err != nil {
|
||||
return result, fmt.Errorf("err: %w (supplied gas %d)", err, args.Gas)
|
||||
return result, fmt.Errorf("err: %w (supplied gas %d)", err, msg.GasLimit)
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
@@ -1149,12 +1151,10 @@ func (pea *PublicEthAPI) writeStateDiffAt(height int64) {
|
||||
defer cancel()
|
||||
var data json.RawMessage
|
||||
params := statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
IncludeBlock: true,
|
||||
IncludeReceipts: true,
|
||||
IncludeTD: true,
|
||||
IncludeCode: true,
|
||||
IncludeBlock: true,
|
||||
IncludeReceipts: true,
|
||||
IncludeTD: true,
|
||||
IncludeCode: true,
|
||||
}
|
||||
log.Debugf("Calling statediff_writeStateDiffAt(%d)", height)
|
||||
if err := pea.rpc.CallContext(ctx, &data, "statediff_writeStateDiffAt", uint64(height), params); err != nil {
|
||||
@@ -1172,12 +1172,10 @@ func (pea *PublicEthAPI) writeStateDiffFor(blockHash common.Hash) {
|
||||
defer cancel()
|
||||
var data json.RawMessage
|
||||
params := statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
IncludeBlock: true,
|
||||
IncludeReceipts: true,
|
||||
IncludeTD: true,
|
||||
IncludeCode: true,
|
||||
IncludeBlock: true,
|
||||
IncludeReceipts: true,
|
||||
IncludeTD: true,
|
||||
IncludeCode: true,
|
||||
}
|
||||
log.Debugf("Calling statediff_writeStateDiffFor(%s)", blockHash.Hex())
|
||||
if err := pea.rpc.CallContext(ctx, &data, "statediff_writeStateDiffFor", blockHash, params); err != nil {
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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 eth_test
|
||||
package eth_api_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -31,14 +31,15 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/jmoiron/sqlx"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -50,7 +51,8 @@ var (
|
||||
blockHash = test_helpers.MockBlock.Header().Hash()
|
||||
baseFee = test_helpers.MockLondonBlock.BaseFee()
|
||||
ctx = context.Background()
|
||||
expectedBlock = map[string]interface{}{
|
||||
|
||||
expectedBlock = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(test_helpers.MockBlock.Number()),
|
||||
"hash": test_helpers.MockBlock.Hash(),
|
||||
"parentHash": test_helpers.MockBlock.ParentHash(),
|
||||
@@ -151,6 +153,7 @@ var (
|
||||
"logsBloom": test_helpers.MockReceipts[0].Bloom,
|
||||
"status": hexutil.Uint(test_helpers.MockReceipts[0].Status),
|
||||
"effectiveGasPrice": (*hexutil.Big)(big.NewInt(100)),
|
||||
"type": hexutil.Uint64(types.LegacyTxType),
|
||||
}
|
||||
expectedReceipt2 = map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
@@ -166,6 +169,7 @@ var (
|
||||
"logsBloom": test_helpers.MockReceipts[1].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[1].PostState),
|
||||
"effectiveGasPrice": (*hexutil.Big)(big.NewInt(200)),
|
||||
"type": hexutil.Uint64(types.LegacyTxType),
|
||||
}
|
||||
expectedReceipt3 = map[string]interface{}{
|
||||
"blockHash": blockHash,
|
||||
@@ -181,80 +185,78 @@ var (
|
||||
"logsBloom": test_helpers.MockReceipts[2].Bloom,
|
||||
"root": hexutil.Bytes(test_helpers.MockReceipts[2].PostState),
|
||||
"effectiveGasPrice": (*hexutil.Big)(big.NewInt(150)),
|
||||
"type": hexutil.Uint64(types.LegacyTxType),
|
||||
}
|
||||
)
|
||||
var (
|
||||
db *sqlx.DB
|
||||
api *eth.PublicEthAPI
|
||||
chainConfig = params.TestChainConfig
|
||||
)
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
var (
|
||||
err error
|
||||
tx interfaces.Batch
|
||||
)
|
||||
|
||||
db = shared.SetupDB()
|
||||
indexAndPublisher := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
|
||||
backend, err := eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
GroupCacheConfig: &shared.GroupCacheConfig{
|
||||
StateDB: shared.GroupConfig{
|
||||
Name: "api_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api, _ = eth.NewPublicEthAPI(backend, nil, eth.APIConfig{StateDiffTimeout: shared.DefaultStateDiffTimeout})
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipld := sdtypes.IPLD{
|
||||
CID: ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHash.Bytes()).String(),
|
||||
Content: test_helpers.ContractCode,
|
||||
}
|
||||
err = indexAndPublisher.PushIPLD(tx, ipld)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, node := range test_helpers.MockStateNodes {
|
||||
err = indexAndPublisher.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
uncles := test_helpers.MockBlock.Uncles()
|
||||
uncleHashes := make([]common.Hash, len(uncles))
|
||||
for i, uncle := range uncles {
|
||||
uncleHashes[i] = uncle.Hash()
|
||||
}
|
||||
expectedBlock["uncles"] = uncleHashes
|
||||
|
||||
// setting chain config to for london block
|
||||
chainConfig.LondonBlock = big.NewInt(2)
|
||||
indexAndPublisher = shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockLondonBlock, test_helpers.MockLondonReceipts, test_helpers.MockLondonBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() { shared.TearDownDB(db) })
|
||||
|
||||
var _ = Describe("API", func() {
|
||||
var (
|
||||
db *sqlx.DB
|
||||
api *eth.PublicEthAPI
|
||||
chainConfig = params.TestChainConfig
|
||||
)
|
||||
// Test db setup, rather than using BeforeEach we only need to setup once since the tests do not mutate the database
|
||||
// Note: if you focus one of the tests be sure to focus this and the defered It()
|
||||
It("test init", func() {
|
||||
var (
|
||||
err error
|
||||
tx interfaces.Batch
|
||||
)
|
||||
|
||||
db = shared.SetupDB()
|
||||
indexAndPublisher := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
|
||||
backend, err := eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
GroupCacheConfig: &shared.GroupCacheConfig{
|
||||
StateDB: shared.GroupConfig{
|
||||
Name: "api_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api, _ = eth.NewPublicEthAPI(backend, nil, eth.APIConfig{false, false, false, false, shared.DefaultStateDiffTimeout})
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ccHash := sdtypes.CodeAndCodeHash{
|
||||
Hash: test_helpers.ContractCodeHash,
|
||||
Code: test_helpers.ContractCode,
|
||||
}
|
||||
|
||||
err = indexAndPublisher.PushCodeAndCodeHash(tx, ccHash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, node := range test_helpers.MockStateNodes {
|
||||
err = indexAndPublisher.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
uncles := test_helpers.MockBlock.Uncles()
|
||||
uncleHashes := make([]common.Hash, len(uncles))
|
||||
for i, uncle := range uncles {
|
||||
uncleHashes[i] = uncle.Hash()
|
||||
}
|
||||
expectedBlock["uncles"] = uncleHashes
|
||||
|
||||
// setting chain config to for london block
|
||||
chainConfig.LondonBlock = big.NewInt(2)
|
||||
indexAndPublisher = shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockLondonBlock, test_helpers.MockLondonReceipts, test_helpers.MockLondonBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
// Single test db tear down at end of all tests
|
||||
defer It("test teardown", func() { shared.TearDownDB(db) })
|
||||
/*
|
||||
|
||||
Headers and blocks
|
||||
@@ -329,12 +331,12 @@ var _ = Describe("API", func() {
|
||||
It("Fetch BaseFee from london block by block number, returns `nil` for legacy block", func() {
|
||||
block, err := api.GetBlockByNumber(ctx, number, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, ok := block["baseFee"]
|
||||
_, ok := block["baseFeePerGas"]
|
||||
Expect(ok).To(Equal(false))
|
||||
|
||||
block, err = api.GetBlockByNumber(ctx, londonBlockNum, false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block["baseFee"].(*big.Int)).To(Equal(baseFee))
|
||||
Expect(block["baseFeePerGas"]).To(Equal((*hexutil.Big)(baseFee)))
|
||||
})
|
||||
It("Retrieves a block by number with uncles in correct order", func() {
|
||||
block, err := api.GetBlockByNumber(ctx, londonBlockNum, false)
|
||||
@@ -383,11 +385,11 @@ var _ = Describe("API", func() {
|
||||
It("Fetch BaseFee from london block by block hash, returns `nil` for legacy block", func() {
|
||||
block, err := api.GetBlockByHash(ctx, test_helpers.MockBlock.Hash(), true)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
_, ok := block["baseFee"]
|
||||
_, ok := block["baseFeePerGas"]
|
||||
Expect(ok).To(Equal(false))
|
||||
block, err = api.GetBlockByHash(ctx, test_helpers.MockLondonBlock.Hash(), false)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(block["baseFee"].(*big.Int)).To(Equal(baseFee))
|
||||
Expect(block["baseFeePerGas"]).To(Equal((*hexutil.Big)(baseFee)))
|
||||
})
|
||||
It("Retrieves a block by hash with uncles in correct order", func() {
|
||||
block, err := api.GetBlockByHash(ctx, test_helpers.MockLondonBlock.Hash(), false)
|
||||
@@ -0,0 +1,29 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 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 eth_api_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestETHSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "ipld-eth-server/pkg/eth/api_test")
|
||||
}
|
||||
+205
-187
@@ -25,18 +25,12 @@ import (
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
validator "github.com/cerc-io/eth-ipfs-state-validator/v4/pkg"
|
||||
ipfsethdb "github.com/cerc-io/ipfs-ethdb/v4/postgres"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
ipld_eth_statedb "github.com/cerc-io/ipld-eth-statedb"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/consensus"
|
||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/bloombits"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
@@ -45,9 +39,17 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
ethServerShared "github.com/ethereum/go-ethereum/statediff/indexer/shared"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
validator "github.com/cerc-io/eth-ipfs-state-validator/v5/pkg"
|
||||
ipfsethdb "github.com/cerc-io/ipfs-ethdb/v5/postgres/v0"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
ipld_direct_state "github.com/cerc-io/ipld-eth-statedb/direct_by_leaf"
|
||||
ipld_sql "github.com/cerc-io/ipld-eth-statedb/sql"
|
||||
ipld_trie_state "github.com/cerc-io/ipld-eth-statedb/trie_by_cid/state"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -64,6 +66,9 @@ const (
|
||||
StateDBGroupCacheName = "statedb"
|
||||
)
|
||||
|
||||
// Backend handles all interactions with IPLD/SQL-backed Ethereum state.
|
||||
// Note that this does not contain a geth state.StateDatabase, as it is not compatible with the
|
||||
// IPFS v0 blockstore nor the leaf-key indexed SQL schema.
|
||||
type Backend struct {
|
||||
// underlying postgres db
|
||||
DB *sqlx.DB
|
||||
@@ -72,10 +77,11 @@ type Backend struct {
|
||||
Retriever *Retriever
|
||||
|
||||
// ethereum interfaces
|
||||
EthDB ethdb.Database
|
||||
StateDatabase state.Database
|
||||
// We'll use this state.Database for eth_call and any place we don't need trie access
|
||||
IpldStateDatabase ipld_eth_statedb.StateDatabase
|
||||
EthDB ethdb.Database
|
||||
// We use this state.Database for eth_call and any place we don't need trie access
|
||||
IpldDirectStateDatabase ipld_direct_state.StateDatabase
|
||||
// We use this where state must be accessed by trie
|
||||
IpldTrieStateDatabase ipld_trie_state.Database
|
||||
|
||||
Config *Config
|
||||
}
|
||||
@@ -83,7 +89,6 @@ type Backend struct {
|
||||
type Config struct {
|
||||
ChainConfig *params.ChainConfig
|
||||
VMConfig vm.Config
|
||||
DefaultSender *common.Address
|
||||
RPCGasCap *big.Int
|
||||
GroupCacheConfig *shared.GroupCacheConfig
|
||||
}
|
||||
@@ -104,17 +109,14 @@ func NewEthBackend(db *sqlx.DB, c *Config) (*Backend, error) {
|
||||
})
|
||||
|
||||
logStateDBStatsOnTimer(ethDB.(*ipfsethdb.Database), gcc)
|
||||
ipldStateDB, err := ipld_eth_statedb.NewStateDatabaseWithSqlxPool(db)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
driver := ipld_sql.NewSQLXDriverFromPool(context.Background(), db)
|
||||
return &Backend{
|
||||
DB: db,
|
||||
Retriever: r,
|
||||
EthDB: ethDB,
|
||||
StateDatabase: state.NewDatabase(ethDB),
|
||||
IpldStateDatabase: ipldStateDB,
|
||||
Config: c,
|
||||
DB: db,
|
||||
Retriever: r,
|
||||
EthDB: ethDB,
|
||||
IpldDirectStateDatabase: ipld_direct_state.NewStateDatabase(driver),
|
||||
IpldTrieStateDatabase: ipld_trie_state.NewDatabase(ethDB),
|
||||
Config: c,
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -123,27 +125,35 @@ func (b *Backend) ChainDb() ethdb.Database {
|
||||
return b.EthDB
|
||||
}
|
||||
|
||||
// HeaderByNumber gets the canonical header for the provided block number
|
||||
func (b *Backend) HeaderByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (*types.Header, error) {
|
||||
func (b *Backend) normalizeBlockNumber(blockNumber rpc.BlockNumber) (int64, error) {
|
||||
var err error
|
||||
number := blockNumber.Int64()
|
||||
if blockNumber == rpc.LatestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.EarliestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.PendingBlockNumber {
|
||||
return nil, errPendingBlockNumber
|
||||
return 0, errPendingBlockNumber
|
||||
}
|
||||
if number < 0 {
|
||||
return nil, errNegativeBlockNumber
|
||||
return 0, errNegativeBlockNumber
|
||||
}
|
||||
return number, nil
|
||||
}
|
||||
|
||||
// HeaderByNumber gets the canonical header for the provided block number
|
||||
func (b *Backend) HeaderByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (*types.Header, error) {
|
||||
number, err := b.normalizeBlockNumber(blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, canonicalHeaderRLP, err := b.GetCanonicalHeader(uint64(number))
|
||||
if err != nil {
|
||||
@@ -156,23 +166,7 @@ func (b *Backend) HeaderByNumber(ctx context.Context, blockNumber rpc.BlockNumbe
|
||||
|
||||
// HeaderByHash gets the header for the provided block hash
|
||||
func (b *Backend) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
// Begin tx
|
||||
tx, err := b.DB.Beginx()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if p := recover(); p != nil {
|
||||
shared.Rollback(tx)
|
||||
panic(p)
|
||||
} else if err != nil {
|
||||
shared.Rollback(tx)
|
||||
} else {
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
|
||||
_, headerRLP, err := b.Retriever.RetrieveHeaderByHash(tx, hash)
|
||||
_, headerRLP, err := b.Retriever.RetrieveHeaderByHash(hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -191,14 +185,16 @@ func (b *Backend) HeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Bl
|
||||
return nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, errors.New("header for hash not found")
|
||||
return nil, errHeaderHashNotFound
|
||||
}
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
if blockNrOrHash.RequireCanonical {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
}
|
||||
return header, nil
|
||||
}
|
||||
@@ -245,14 +241,16 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo
|
||||
return nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, errors.New("header for hash not found")
|
||||
return nil, errHeaderHashNotFound
|
||||
}
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
if blockNrOrHash.RequireCanonical {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if canonicalHash != hash {
|
||||
return nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
}
|
||||
block, err := b.BlockByHash(ctx, hash)
|
||||
if err != nil {
|
||||
@@ -268,28 +266,10 @@ func (b *Backend) BlockByNumberOrHash(ctx context.Context, blockNrOrHash rpc.Blo
|
||||
|
||||
// BlockByNumber returns the requested canonical block
|
||||
func (b *Backend) BlockByNumber(ctx context.Context, blockNumber rpc.BlockNumber) (*types.Block, error) {
|
||||
var err error
|
||||
number := blockNumber.Int64()
|
||||
if blockNumber == rpc.LatestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveLastBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
number, err := b.normalizeBlockNumber(blockNumber)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if blockNumber == rpc.EarliestBlockNumber {
|
||||
number, err = b.Retriever.RetrieveFirstBlockNumber()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if blockNumber == rpc.PendingBlockNumber {
|
||||
return nil, errPendingBlockNumber
|
||||
}
|
||||
if number < 0 {
|
||||
return nil, errNegativeBlockNumber
|
||||
}
|
||||
|
||||
// Get the canonical hash
|
||||
canonicalHash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
@@ -360,12 +340,12 @@ func (b *Backend) BlockByHash(ctx context.Context, hash common.Hash) (*types.Blo
|
||||
}
|
||||
|
||||
// Compose everything together into a complete block
|
||||
return types.NewBlock(header, transactions, uncles, receipts, new(trie.Trie)), err
|
||||
return types.NewBlock(header, transactions, uncles, receipts, trie.NewEmpty(nil)), err
|
||||
}
|
||||
|
||||
// GetHeaderByBlockHash retrieves header for a provided block hash
|
||||
func (b *Backend) GetHeaderByBlockHash(tx *sqlx.Tx, hash common.Hash) (*types.Header, error) {
|
||||
_, headerRLP, err := b.Retriever.RetrieveHeaderByHash(tx, hash)
|
||||
_, headerRLP, err := b.Retriever.RetrieveHeaderByHash2(tx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -376,11 +356,11 @@ func (b *Backend) GetHeaderByBlockHash(tx *sqlx.Tx, hash common.Hash) (*types.He
|
||||
|
||||
// CurrentHeader returns the current block's header
|
||||
func (b *Backend) CurrentHeader() *types.Header {
|
||||
block, err := b.BlockByNumber(context.Background(), rpc.LatestBlockNumber)
|
||||
header, err := b.HeaderByNumber(context.Background(), rpc.LatestBlockNumber)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return block.Header()
|
||||
return header
|
||||
}
|
||||
|
||||
// GetBody returns the the body for the provided block hash and number
|
||||
@@ -388,8 +368,11 @@ func (b *Backend) GetBody(ctx context.Context, hash common.Hash, number rpc.Bloc
|
||||
if number < 0 || hash == (common.Hash{}) {
|
||||
return nil, errors.New("invalid arguments; expect hash and no special block numbers")
|
||||
}
|
||||
block, bErr := b.BlockByHash(ctx, hash)
|
||||
if block != nil && bErr == nil {
|
||||
block, err := b.BlockByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if block != nil {
|
||||
return block.Body(), nil
|
||||
}
|
||||
return nil, errors.New("block body not found")
|
||||
@@ -403,7 +386,7 @@ func (b *Backend) GetUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) ([]*types.
|
||||
}
|
||||
|
||||
uncles := make([]*types.Header, 0)
|
||||
if err := rlp.DecodeBytes(uncleBytes, uncles); err != nil {
|
||||
if err := rlp.DecodeBytes(uncleBytes, &uncles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -418,7 +401,7 @@ func (b *Backend) GetUnclesByBlockHashAndNumber(tx *sqlx.Tx, hash common.Hash, n
|
||||
}
|
||||
|
||||
uncles := make([]*types.Header, 0)
|
||||
if err := rlp.DecodeBytes(uncleBytes, uncles); err != nil {
|
||||
if err := rlp.DecodeBytes(uncleBytes, &uncles); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -594,10 +577,24 @@ func (b *Backend) GetLogs(ctx context.Context, hash common.Hash, number uint64)
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// StateAndHeaderByNumberOrHash returns the statedb and header for the provided block number or hash
|
||||
func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*state.StateDB, *types.Header, error) {
|
||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||
return b.StateAndHeaderByNumber(ctx, blockNr)
|
||||
// IPLDStateDBAndHeaderByNumberOrHash returns the statedb and header for the provided block number or hash
|
||||
func (b *Backend) IPLDDirectStateDBAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*ipld_direct_state.StateDB, *types.Header, error) {
|
||||
if number, ok := blockNrOrHash.Number(); ok {
|
||||
// Pending state is only known by the miner
|
||||
if number == rpc.PendingBlockNumber {
|
||||
return nil, nil, errPendingBlockNumber
|
||||
}
|
||||
// Otherwise resolve the block number and return its state
|
||||
header, err := b.HeaderByNumber(ctx, number)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, nil, errHeaderNotFound
|
||||
}
|
||||
// TODO use GetCanonicalHeaderAndHash to avoid rehashing
|
||||
statedb, err := ipld_direct_state.New(header.Hash(), b.IpldDirectStateDatabase)
|
||||
return statedb, header, err
|
||||
}
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
header, err := b.HeaderByHash(ctx, hash)
|
||||
@@ -605,81 +602,66 @@ func (b *Backend) StateAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHas
|
||||
return nil, nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, nil, errors.New("header for hash not found")
|
||||
return nil, nil, errHeaderHashNotFound
|
||||
}
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
if blockNrOrHash.RequireCanonical {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if canonicalHash != hash {
|
||||
return nil, nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
stateDb, err := state.New(header.Root, b.StateDatabase, nil)
|
||||
return stateDb, header, err
|
||||
statedb, err := ipld_direct_state.New(header.Hash(), b.IpldDirectStateDatabase)
|
||||
return statedb, header, err
|
||||
}
|
||||
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
|
||||
// IPLDStateDBAndHeaderByNumberOrHash returns the statedb and header for the provided block number or hash
|
||||
func (b *Backend) IPLDStateDBAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*ipld_eth_statedb.StateDB, *types.Header, error) {
|
||||
if blockNr, ok := blockNrOrHash.Number(); ok {
|
||||
return b.IPLDStateDBAndHeaderByNumber(ctx, blockNr)
|
||||
}
|
||||
if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
header, err := b.HeaderByHash(ctx, hash)
|
||||
func (b *Backend) IPLDTrieStateDBAndHeaderByNumberOrHash(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash) (*ipld_trie_state.StateDB, *types.Header, error) {
|
||||
var header *types.Header
|
||||
if number, ok := blockNrOrHash.Number(); ok {
|
||||
// Pending state is only known by the miner
|
||||
if number == rpc.PendingBlockNumber {
|
||||
return nil, nil, errPendingBlockNumber
|
||||
}
|
||||
// Otherwise resolve the block number and return its state
|
||||
blockHash, err := b.GetCanonicalHash(uint64(number))
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
header, err = b.HeaderByHash(ctx, blockHash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, nil, errors.New("header for hash not found")
|
||||
return nil, nil, errHeaderNotFound
|
||||
}
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
} else if hash, ok := blockNrOrHash.Hash(); ok {
|
||||
var err error
|
||||
header, err = b.HeaderByHash(ctx, hash)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if blockNrOrHash.RequireCanonical && canonicalHash != hash {
|
||||
return nil, nil, errors.New("hash is not currently canonical")
|
||||
if header == nil {
|
||||
return nil, nil, errHeaderHashNotFound
|
||||
}
|
||||
stateDB, err := ipld_eth_statedb.New(header.Root, b.IpldStateDatabase)
|
||||
return stateDB, header, err
|
||||
if blockNrOrHash.RequireCanonical {
|
||||
canonicalHash, err := b.GetCanonicalHash(header.Number.Uint64())
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if canonicalHash != hash {
|
||||
return nil, nil, errors.New("hash is not currently canonical")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
return nil, nil, errors.New("invalid arguments; neither block nor hash specified")
|
||||
}
|
||||
|
||||
// StateAndHeaderByNumber returns the statedb and header for a provided block number
|
||||
func (b *Backend) StateAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*state.StateDB, *types.Header, error) {
|
||||
// Pending state is only known by the miner
|
||||
if number == rpc.PendingBlockNumber {
|
||||
return nil, nil, errPendingBlockNumber
|
||||
}
|
||||
// Otherwise resolve the block number and return its state
|
||||
header, err := b.HeaderByNumber(ctx, number)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, nil, errors.New("header not found")
|
||||
}
|
||||
stateDb, err := state.New(header.Root, b.StateDatabase, nil)
|
||||
return stateDb, header, err
|
||||
}
|
||||
|
||||
// IPLDStateDBAndHeaderByNumber returns the statedb and header for a provided block number
|
||||
func (b *Backend) IPLDStateDBAndHeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*ipld_eth_statedb.StateDB, *types.Header, error) {
|
||||
// Pending state is only known by the miner
|
||||
if number == rpc.PendingBlockNumber {
|
||||
return nil, nil, errPendingBlockNumber
|
||||
}
|
||||
// Otherwise resolve the block number and return its state
|
||||
header, err := b.HeaderByNumber(ctx, number)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if header == nil {
|
||||
return nil, nil, errors.New("header not found")
|
||||
}
|
||||
stateDb, err := ipld_eth_statedb.New(header.Root, b.IpldStateDatabase)
|
||||
return stateDb, header, err
|
||||
statedb, err := ipld_trie_state.New(header.Root, b.IpldTrieStateDatabase, nil)
|
||||
return statedb, header, err
|
||||
}
|
||||
|
||||
// GetCanonicalHash gets the canonical hash for the provided number, if there is one
|
||||
@@ -691,19 +673,30 @@ func (b *Backend) GetCanonicalHash(number uint64) (common.Hash, error) {
|
||||
return common.HexToHash(hashResult), nil
|
||||
}
|
||||
|
||||
type rowResult struct {
|
||||
CID string
|
||||
Data []byte
|
||||
}
|
||||
|
||||
// GetCanonicalHeader gets the canonical header for the provided number, if there is one
|
||||
func (b *Backend) GetCanonicalHeader(number uint64) (string, []byte, error) {
|
||||
type rowResult struct {
|
||||
CID string
|
||||
Data []byte
|
||||
}
|
||||
headerResult := new(rowResult)
|
||||
return headerResult.CID, headerResult.Data, b.DB.QueryRowx(RetrieveCanonicalHeaderByNumber, number).StructScan(headerResult)
|
||||
return headerResult.CID, headerResult.Data,
|
||||
b.DB.QueryRowx(RetrieveCanonicalHeaderByNumber, number).StructScan(headerResult)
|
||||
}
|
||||
|
||||
// GetCanonicalHeader gets the canonical hash and header for the provided number, if they exist
|
||||
func (b *Backend) GetCanonicalHeaderAndHash(number uint64) (string, []byte, error) {
|
||||
type rowResult struct {
|
||||
Data []byte
|
||||
BlockHash string
|
||||
}
|
||||
headerResult := new(rowResult)
|
||||
return headerResult.BlockHash, headerResult.Data,
|
||||
b.DB.QueryRowx(RetrieveCanonicalHeaderAndHashByNumber, number).StructScan(headerResult)
|
||||
}
|
||||
|
||||
// GetEVM constructs and returns a vm.EVM
|
||||
func (b *Backend) GetEVM(ctx context.Context, msg *core.Message, state *state.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
func (b *Backend) GetEVM(ctx context.Context, msg *core.Message, state vm.StateDB, header *types.Header) (*vm.EVM, func() error, error) {
|
||||
vmError := func() error { return nil }
|
||||
txContext := core.NewEVMTxContext(msg)
|
||||
evmCtx := core.NewEVMBlockContext(header, b, nil)
|
||||
@@ -759,13 +752,21 @@ func (b *Backend) GetAccountByHash(ctx context.Context, address common.Address,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, accountRlp, err := b.Retriever.RetrieveAccountByAddressAndBlockHash(address, hash)
|
||||
acctRecord, err := b.Retriever.RetrieveAccountByAddressAndBlockHash(address, hash)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
acct := new(types.StateAccount)
|
||||
return acct, rlp.DecodeBytes(accountRlp, acct)
|
||||
balance, ok := new(big.Int).SetString(acctRecord.Balance, 10)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to parse balance %s", acctRecord.Balance)
|
||||
}
|
||||
return &types.StateAccount{
|
||||
Nonce: acctRecord.Nonce,
|
||||
Balance: balance,
|
||||
Root: common.HexToHash(acctRecord.Root),
|
||||
CodeHash: acctRecord.CodeHash,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetCodeByNumberOrHash returns the byte code for the contract deployed at the provided address at the block with the provided hash or block number
|
||||
@@ -803,14 +804,13 @@ func (b *Backend) GetCodeByNumber(ctx context.Context, address common.Address, b
|
||||
return nil, err
|
||||
}
|
||||
if hash == (common.Hash{}) {
|
||||
return nil, fmt.Errorf("no canoncial block hash found for provided height (%d)", number)
|
||||
return nil, fmt.Errorf("no canonical block hash found for provided height (%d)", number)
|
||||
}
|
||||
return b.GetCodeByHash(ctx, address, hash)
|
||||
}
|
||||
|
||||
// GetCodeByHash returns the byte code for the contract deployed at the provided address at the block with the provided hash
|
||||
func (b *Backend) GetCodeByHash(ctx context.Context, address common.Address, hash common.Hash) ([]byte, error) {
|
||||
codeHash := make([]byte, 0)
|
||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
// Begin tx
|
||||
tx, err := b.DB.Beginx()
|
||||
@@ -827,17 +827,14 @@ func (b *Backend) GetCodeByHash(ctx context.Context, address common.Address, has
|
||||
err = tx.Commit()
|
||||
}
|
||||
}()
|
||||
var codeHash string
|
||||
err = tx.Get(&codeHash, RetrieveCodeHashByLeafKeyAndBlockHash, leafKey.Hex(), hash.Hex())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var mhKey string
|
||||
mhKey, err = ethServerShared.MultihashKeyFromKeccak256(common.BytesToHash(codeHash))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
code := make([]byte, 0)
|
||||
err = tx.Get(&code, RetrieveCodeByMhKey, mhKey)
|
||||
cid := ipld.Keccak256ToCid(ipld.RawBinary, common.HexToHash(codeHash).Bytes())
|
||||
var code []byte
|
||||
err = tx.Get(&code, RetrieveCodeByKey, cid.String())
|
||||
return code, err
|
||||
}
|
||||
|
||||
@@ -890,8 +887,7 @@ func (b *Backend) GetStorageByHash(ctx context.Context, address common.Address,
|
||||
return nil, err
|
||||
}
|
||||
|
||||
_, _, storageRlp, err := b.Retriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
|
||||
return storageRlp, err
|
||||
return b.Retriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address, key, hash)
|
||||
}
|
||||
|
||||
func (b *Backend) GetSlice(path string, depth int, root common.Hash, storage bool) (*GetSliceResponse, error) {
|
||||
@@ -902,14 +898,23 @@ func (b *Backend) GetSlice(path string, depth int, root common.Hash, storage boo
|
||||
metaData := metaDataFields{}
|
||||
|
||||
startTime := makeTimestamp()
|
||||
t, _ := b.StateDatabase.OpenTrie(root)
|
||||
var t ipld_trie_state.Trie
|
||||
var err error
|
||||
if storage {
|
||||
t, err = b.IpldTrieStateDatabase.OpenStorageTrie(common.Hash{}, common.Hash{}, root)
|
||||
} else {
|
||||
t, err = b.IpldTrieStateDatabase.OpenTrie(root)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
metaData.trieLoadingTime = makeTimestamp() - startTime
|
||||
|
||||
// Convert the head hex path to a decoded byte path
|
||||
headPath := common.FromHex(path)
|
||||
|
||||
// Get Stem nodes
|
||||
err := b.getSliceStem(headPath, t, response, &metaData, storage)
|
||||
err = b.getSliceStem(headPath, t, response, &metaData, storage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -933,16 +938,16 @@ func (b *Backend) GetSlice(path string, depth int, root common.Hash, storage boo
|
||||
return response, nil
|
||||
}
|
||||
|
||||
func (b *Backend) getSliceStem(headPath []byte, t state.Trie, response *GetSliceResponse, metaData *metaDataFields, storage bool) error {
|
||||
func (b *Backend) getSliceStem(headPath []byte, t ipld_trie_state.Trie, response *GetSliceResponse, metaData *metaDataFields, storage bool) error {
|
||||
leavesFetchTime := int64(0)
|
||||
totalStemStartTime := makeTimestamp()
|
||||
|
||||
for i := 0; i < len(headPath); i++ {
|
||||
// Create path for each node along the stem
|
||||
nodePath := make([]byte, len(headPath[:i]))
|
||||
copy(nodePath, headPath[:i])
|
||||
// nodePath := make([]byte, len(headPath[:i]))
|
||||
nodePath := headPath[:i]
|
||||
|
||||
rawNode, _, err := t.(*trie.StateTrie).TryGetNode(trie.HexToCompact(nodePath))
|
||||
rawNode, _, err := t.TryGetNode(trie.HexToCompact(nodePath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -952,12 +957,12 @@ func (b *Backend) getSliceStem(headPath []byte, t state.Trie, response *GetSlice
|
||||
continue
|
||||
}
|
||||
|
||||
node, nodeElements, err := ResolveNode(nodePath, rawNode, b.StateDatabase.TrieDB())
|
||||
node, nodeElements, err := ResolveNode(nodePath, rawNode, b.IpldTrieStateDatabase.TrieDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leafFetchTime, err := fillSliceNodeData(b.EthDB, response.TrieNodes.Stem, response.Leaves, node, nodeElements, storage)
|
||||
leafFetchTime, err := fillSliceNodeData(b.IpldTrieStateDatabase, response.TrieNodes.Stem, response.Leaves, node, nodeElements, storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -981,10 +986,10 @@ func (b *Backend) getSliceStem(headPath []byte, t state.Trie, response *GetSlice
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) getSliceHead(headPath []byte, t state.Trie, response *GetSliceResponse, metaData *metaDataFields, storage bool) error {
|
||||
func (b *Backend) getSliceHead(headPath []byte, t ipld_trie_state.Trie, response *GetSliceResponse, metaData *metaDataFields, storage bool) error {
|
||||
totalHeadStartTime := makeTimestamp()
|
||||
|
||||
rawNode, _, err := t.(*trie.StateTrie).TryGetNode(trie.HexToCompact(headPath))
|
||||
rawNode, _, err := t.TryGetNode(trie.HexToCompact(headPath))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -994,12 +999,12 @@ func (b *Backend) getSliceHead(headPath []byte, t state.Trie, response *GetSlice
|
||||
return nil
|
||||
}
|
||||
|
||||
node, nodeElements, err := ResolveNode(headPath, rawNode, b.StateDatabase.TrieDB())
|
||||
node, nodeElements, err := ResolveNode(headPath, rawNode, b.IpldTrieStateDatabase.TrieDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leafFetchTime, err := fillSliceNodeData(b.EthDB, response.TrieNodes.Head, response.Leaves, node, nodeElements, storage)
|
||||
leafFetchTime, err := fillSliceNodeData(b.IpldTrieStateDatabase, response.TrieNodes.Head, response.Leaves, node, nodeElements, storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1021,7 +1026,7 @@ func (b *Backend) getSliceHead(headPath []byte, t state.Trie, response *GetSlice
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *Backend) getSliceTrie(headPath []byte, t state.Trie, response *GetSliceResponse, metaData *metaDataFields, depth int, storage bool) error {
|
||||
func (b *Backend) getSliceTrie(headPath []byte, t ipld_trie_state.Trie, response *GetSliceResponse, metaData *metaDataFields, depth int, storage bool) error {
|
||||
it, timeTaken := getIteratorAtPath(t, headPath)
|
||||
metaData.trieLoadingTime += timeTaken
|
||||
|
||||
@@ -1051,12 +1056,16 @@ func (b *Backend) getSliceTrie(headPath []byte, t state.Trie, response *GetSlice
|
||||
continue
|
||||
}
|
||||
|
||||
node, nodeElements, err := ResolveNodeIt(it, b.StateDatabase.TrieDB())
|
||||
blob, err := it.NodeBlob(), it.Error()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
node, nodeElements, err := ResolveNode(it.Path(), blob, b.IpldTrieStateDatabase.TrieDB())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
leafFetchTime, err := fillSliceNodeData(b.EthDB, response.TrieNodes.Slice, response.Leaves, node, nodeElements, storage)
|
||||
leafFetchTime, err := fillSliceNodeData(b.IpldTrieStateDatabase, response.TrieNodes.Slice, response.Leaves, node, nodeElements, storage)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1133,6 +1142,15 @@ func (b *Backend) ServiceFilter(ctx context.Context, session *bloombits.MatcherS
|
||||
panic("implement me")
|
||||
}
|
||||
|
||||
// Close closes the backing DB and EthDB instances
|
||||
func (b *Backend) Close() error {
|
||||
err := b.EthDB.Close()
|
||||
if err != nil {
|
||||
log.Errorf("error closing EthDB: %s", err)
|
||||
}
|
||||
return b.DB.Close()
|
||||
}
|
||||
|
||||
func logStateDBStatsOnTimer(ethDB *ipfsethdb.Database, gcc *shared.GroupCacheConfig) {
|
||||
// No stats logging if interval isn't a positive integer.
|
||||
if gcc.StateDB.LogStatsIntervalInSecs <= 0 {
|
||||
@@ -1143,7 +1161,7 @@ func logStateDBStatsOnTimer(ethDB *ipfsethdb.Database, gcc *shared.GroupCacheCon
|
||||
|
||||
go func() {
|
||||
for range ticker.C {
|
||||
log.Infof("%s groupcache stats: %+v", StateDBGroupCacheName, ethDB.GetCacheStats())
|
||||
log.Debugf("%s groupcache stats: %+v", StateDBGroupCacheName, ethDB.GetCacheStats())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
@@ -27,15 +27,14 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
nodeiter "github.com/ethereum/go-ethereum/trie/concurrent_iterator"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-statedb/trie_by_cid/state"
|
||||
)
|
||||
|
||||
var nullHashBytes = common.Hex2Bytes("0000000000000000000000000000000000000000000000000000000000000000")
|
||||
@@ -65,7 +64,7 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
|
||||
}
|
||||
|
||||
if head.BaseFee != nil {
|
||||
headerMap["baseFee"] = head.BaseFee
|
||||
headerMap["baseFeePerGas"] = (*hexutil.Big)(head.BaseFee)
|
||||
}
|
||||
return headerMap
|
||||
}
|
||||
@@ -329,7 +328,7 @@ func getIteratorAtPath(t state.Trie, startKey []byte) (trie.NodeIterator, int64)
|
||||
}
|
||||
|
||||
func fillSliceNodeData(
|
||||
ethDB ethdb.KeyValueReader,
|
||||
sdb state.Database,
|
||||
nodesMap map[string]string,
|
||||
leavesMap map[string]GetSliceResponseAccount,
|
||||
node StateNode,
|
||||
@@ -343,7 +342,7 @@ func fillSliceNodeData(
|
||||
// Extract account data if it's a Leaf node
|
||||
leafStartTime := makeTimestamp()
|
||||
if node.NodeType == Leaf && !storage {
|
||||
stateLeafKey, storageRoot, code, err := extractContractAccountInfo(ethDB, node, nodeElements)
|
||||
stateLeafKey, storageRoot, code, err := extractContractAccountInfo(sdb, node, nodeElements)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("GetSlice account lookup error: %s", err.Error())
|
||||
}
|
||||
@@ -360,7 +359,7 @@ func fillSliceNodeData(
|
||||
return makeTimestamp() - leafStartTime, nil
|
||||
}
|
||||
|
||||
func extractContractAccountInfo(ethDB ethdb.KeyValueReader, node StateNode, nodeElements []interface{}) (string, string, []byte, error) {
|
||||
func extractContractAccountInfo(sdb state.Database, node StateNode, nodeElements []interface{}) (string, string, []byte, error) {
|
||||
var account types.StateAccount
|
||||
if err := rlp.DecodeBytes(nodeElements[1].([]byte), &account); err != nil {
|
||||
return "", "", nil, fmt.Errorf("error decoding account for leaf node at path %x nerror: %v", node.Path, err)
|
||||
@@ -381,7 +380,32 @@ func extractContractAccountInfo(ethDB ethdb.KeyValueReader, node StateNode, node
|
||||
|
||||
// Extract codeHash and get code
|
||||
codeHash := common.BytesToHash(account.CodeHash)
|
||||
codeBytes := rawdb.ReadCode(ethDB, codeHash)
|
||||
codeBytes, err := sdb.ContractCode(codeHash)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
|
||||
return stateLeafKeyString, storageRootString, codeBytes, nil
|
||||
}
|
||||
|
||||
// IsLeaf checks if the node we are at is a leaf
|
||||
func IsLeaf(elements []interface{}) (bool, error) {
|
||||
if len(elements) > 2 {
|
||||
return false, nil
|
||||
}
|
||||
if len(elements) < 2 {
|
||||
return false, fmt.Errorf("node cannot be less than two elements in length")
|
||||
}
|
||||
switch elements[0].([]byte)[0] / 16 {
|
||||
case '\x00':
|
||||
return false, nil
|
||||
case '\x01':
|
||||
return false, nil
|
||||
case '\x02':
|
||||
return true, nil
|
||||
case '\x03':
|
||||
return true, nil
|
||||
default:
|
||||
return false, fmt.Errorf("unknown hex prefix")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,19 +17,13 @@
|
||||
package eth_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestETHSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "eth ipld server eth suite test")
|
||||
RunSpecs(t, "ipld-eth-server/pkg/eth")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
})
|
||||
|
||||
+7
-21
@@ -4,7 +4,8 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-statedb/trie_by_cid/trie"
|
||||
)
|
||||
|
||||
// NodeType for explicitly setting type of node
|
||||
@@ -73,18 +74,17 @@ type StorageNode struct {
|
||||
}
|
||||
|
||||
func ResolveNode(path []byte, node []byte, trieDB *trie.Database) (StateNode, []interface{}, error) {
|
||||
nodePath := make([]byte, len(path))
|
||||
copy(nodePath, path)
|
||||
|
||||
var nodeElements []interface{}
|
||||
if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
|
||||
return StateNode{}, nil, err
|
||||
}
|
||||
|
||||
ty, err := CheckKeyType(nodeElements)
|
||||
if err != nil {
|
||||
return StateNode{}, nil, err
|
||||
}
|
||||
|
||||
nodePath := make([]byte, len(path))
|
||||
copy(nodePath, path)
|
||||
return StateNode{
|
||||
NodeType: ty,
|
||||
Path: nodePath,
|
||||
@@ -94,23 +94,9 @@ func ResolveNode(path []byte, node []byte, trieDB *trie.Database) (StateNode, []
|
||||
|
||||
// ResolveNodeIt return the state diff node pointed by the iterator.
|
||||
func ResolveNodeIt(it trie.NodeIterator, trieDB *trie.Database) (StateNode, []interface{}, error) {
|
||||
nodePath := make([]byte, len(it.Path()))
|
||||
copy(nodePath, it.Path())
|
||||
node, err := trieDB.Node(it.Hash())
|
||||
node, err := it.NodeBlob(), it.Error()
|
||||
if err != nil {
|
||||
return StateNode{}, nil, err
|
||||
}
|
||||
var nodeElements []interface{}
|
||||
if err = rlp.DecodeBytes(node, &nodeElements); err != nil {
|
||||
return StateNode{}, nil, err
|
||||
}
|
||||
ty, err := CheckKeyType(nodeElements)
|
||||
if err != nil {
|
||||
return StateNode{}, nil, err
|
||||
}
|
||||
return StateNode{
|
||||
NodeType: ty,
|
||||
Path: nodePath,
|
||||
NodeValue: node,
|
||||
}, nodeElements, nil
|
||||
return ResolveNode(it.Path(), node, trieDB)
|
||||
}
|
||||
|
||||
+71
-71
@@ -17,22 +17,21 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
"github.com/ethereum/go-ethereum/statediff/trie_helpers"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/lib/pq"
|
||||
"gorm.io/driver/postgres"
|
||||
"gorm.io/gorm"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
)
|
||||
|
||||
// Retriever is used for fetching
|
||||
@@ -60,14 +59,13 @@ type HeaderCIDRecord struct {
|
||||
TotalDifficulty string `gorm:"column:td"`
|
||||
TxRoot string
|
||||
RctRoot string `gorm:"column:receipt_root"`
|
||||
UncleRoot string
|
||||
UnclesHash string
|
||||
Bloom []byte
|
||||
MhKey string
|
||||
|
||||
// gorm doesn't check if foreign key exists in database.
|
||||
// It is required to eager load relations using preload.
|
||||
TransactionCIDs []TransactionCIDRecord `gorm:"foreignKey:HeaderID,BlockNumber;references:BlockHash,BlockNumber"`
|
||||
IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
|
||||
IPLD IPLDModelRecord `gorm:"foreignKey:CID,BlockNumber;references:Key,BlockNumber"`
|
||||
}
|
||||
|
||||
// TableName overrides the table name used by HeaderCIDRecord
|
||||
@@ -83,8 +81,15 @@ type TransactionCIDRecord struct {
|
||||
Index int64
|
||||
Src string
|
||||
Dst string
|
||||
MhKey string
|
||||
IPLD IPLDModelRecord `gorm:"foreignKey:MhKey,BlockNumber;references:Key,BlockNumber"`
|
||||
IPLD IPLDModelRecord `gorm:"foreignKey:CID,BlockNumber;references:Key,BlockNumber"`
|
||||
}
|
||||
|
||||
type StateAccountRecord struct {
|
||||
Nonce uint64 `db:"nonce"`
|
||||
Balance string `db:"balance"`
|
||||
Root string `db:"storage_root"`
|
||||
CodeHash []byte `db:"code_hash"`
|
||||
Removed bool `db:"removed"`
|
||||
}
|
||||
|
||||
// TableName overrides the table name used by TransactionCIDRecord
|
||||
@@ -220,13 +225,13 @@ func (r *Retriever) RetrieveFilteredGQLLogs(tx *sqlx.Tx, rctFilter ReceiptFilter
|
||||
pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
|
||||
pgStr += ` ORDER BY log_cids.index`
|
||||
|
||||
logCIDs := make([]LogResult, 0)
|
||||
err := tx.Select(&logCIDs, pgStr, args...)
|
||||
logs := make([]LogResult, 0)
|
||||
err := tx.Select(&logs, pgStr, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return logCIDs, nil
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
// RetrieveFilteredLogs retrieves and returns all the log CIDs provided blockHeight or blockHash that conform to the provided
|
||||
@@ -250,13 +255,22 @@ func (r *Retriever) RetrieveFilteredLogs(tx *sqlx.Tx, rctFilter ReceiptFilter, b
|
||||
pgStr, args = logFilterCondition(&id, pgStr, args, rctFilter)
|
||||
pgStr += ` ORDER BY log_cids.index`
|
||||
|
||||
logCIDs := make([]LogResult, 0)
|
||||
err := tx.Select(&logCIDs, pgStr, args...)
|
||||
logs := make([]LogResult, 0)
|
||||
err := tx.Select(&logs, pgStr, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// decode logs and extract original contract Data
|
||||
for i, log := range logs {
|
||||
var buf []interface{}
|
||||
r := bytes.NewReader(log.LogLeafData)
|
||||
if err := rlp.Decode(r, &buf); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logs[i].Data = buf[2].([]byte)
|
||||
}
|
||||
|
||||
return logCIDs, nil
|
||||
return logs, nil
|
||||
}
|
||||
|
||||
func hasTopics(topics [][]string) bool {
|
||||
@@ -360,7 +374,14 @@ func (r *Retriever) RetrieveTxCIDByHash(txHash string, blockNumber *big.Int) (Tr
|
||||
var EmptyNodeValue = make([]byte, common.HashLength)
|
||||
|
||||
// RetrieveHeaderByHash returns the cid and rlp bytes for the header corresponding to the provided block hash
|
||||
func (r *Retriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
|
||||
func (r *Retriever) RetrieveHeaderByHash(hash common.Hash) (string, []byte, error) {
|
||||
headerResult := new(ipldResult)
|
||||
return headerResult.CID, headerResult.Data, r.db.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex())
|
||||
}
|
||||
|
||||
// RetrieveHeaderByHash2 returns the cid and rlp bytes for the header corresponding to the provided block hash
|
||||
// using a sqlx.Tx
|
||||
func (r *Retriever) RetrieveHeaderByHash2(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
|
||||
headerResult := new(ipldResult)
|
||||
return headerResult.CID, headerResult.Data, tx.Get(headerResult, RetrieveHeaderByHashPgStr, hash.Hex())
|
||||
}
|
||||
@@ -368,7 +389,7 @@ func (r *Retriever) RetrieveHeaderByHash(tx *sqlx.Tx, hash common.Hash) (string,
|
||||
// RetrieveUncles returns the cid and rlp bytes for the uncle list corresponding to the provided block hash, number (of non-omner root block)
|
||||
func (r *Retriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64) (string, []byte, error) {
|
||||
uncleResult := new(ipldResult)
|
||||
if err := tx.Select(uncleResult, RetrieveUnclesPgStr, hash.Hex(), number); err != nil {
|
||||
if err := tx.Get(uncleResult, RetrieveUnclesPgStr, hash.Hex(), number); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return uncleResult.CID, uncleResult.Data, nil
|
||||
@@ -377,7 +398,7 @@ func (r *Retriever) RetrieveUncles(tx *sqlx.Tx, hash common.Hash, number uint64)
|
||||
// RetrieveUnclesByBlockHash returns the cid and rlp bytes for the uncle list corresponding to the provided block hash (of non-omner root block)
|
||||
func (r *Retriever) RetrieveUnclesByBlockHash(tx *sqlx.Tx, hash common.Hash) (string, []byte, error) {
|
||||
uncleResult := new(ipldResult)
|
||||
if err := tx.Select(uncleResult, RetrieveUnclesByBlockHashPgStr, hash.Hex()); err != nil {
|
||||
if err := tx.Get(uncleResult, RetrieveUnclesByBlockHashPgStr, hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return uncleResult.CID, uncleResult.Data, nil
|
||||
@@ -419,13 +440,12 @@ func DecodeLeafNode(node []byte) ([]byte, error) {
|
||||
if err := rlp.DecodeBytes(node, &nodeElements); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ty, err := trie_helpers.CheckKeyType(nodeElements)
|
||||
ok, err := IsLeaf(nodeElements)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ty != sdtypes.Leaf {
|
||||
return nil, fmt.Errorf("expected leaf node but found %s", ty)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected leaf node but found %v", nodeElements)
|
||||
}
|
||||
return nodeElements[1].([]byte), nil
|
||||
}
|
||||
@@ -443,11 +463,7 @@ func (r *Retriever) RetrieveReceipts(tx *sqlx.Tx, hash common.Hash, number uint6
|
||||
|
||||
for i, res := range rctResults {
|
||||
cids[i] = res.CID
|
||||
nodeVal, err := DecodeLeafNode(res.Data)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
rcts[i] = nodeVal
|
||||
rcts[i] = res.Data
|
||||
txs[i] = common.HexToHash(res.TxHash)
|
||||
}
|
||||
|
||||
@@ -480,64 +496,48 @@ func (r *Retriever) RetrieveReceiptsByBlockHash(tx *sqlx.Tx, hash common.Hash) (
|
||||
|
||||
// RetrieveAccountByAddressAndBlockHash returns the cid and rlp bytes for the account corresponding to the provided address and block hash
|
||||
// TODO: ensure this handles deleted accounts appropriately
|
||||
func (r *Retriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (string, []byte, error) {
|
||||
accountResult := new(nodeInfo)
|
||||
func (r *Retriever) RetrieveAccountByAddressAndBlockHash(address common.Address, hash common.Hash) (StateAccountRecord, error) {
|
||||
var accountResult StateAccountRecord
|
||||
leafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
if err := r.db.Get(accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
if err := r.db.Get(&accountResult, RetrieveAccountByLeafKeyAndBlockHashPgStr, leafKey.Hex(), hash.Hex()); err != nil {
|
||||
return StateAccountRecord{}, err
|
||||
}
|
||||
|
||||
if accountResult.Removed {
|
||||
return "", EmptyNodeValue, nil
|
||||
return StateAccountRecord{}, nil
|
||||
}
|
||||
|
||||
blockNumber, err := strconv.ParseUint(accountResult.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
accountResult.Data, err = shared.FetchIPLD(r.db, accountResult.CID, blockNumber)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
|
||||
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())
|
||||
}
|
||||
if len(i) != 2 {
|
||||
return "", nil, fmt.Errorf("eth Retriever expected state leaf node rlp to decode into two elements")
|
||||
}
|
||||
return accountResult.CID, i[1].([]byte), nil
|
||||
return accountResult, nil
|
||||
}
|
||||
|
||||
// RetrieveStorageAtByAddressAndStorageSlotAndBlockHash returns the cid and rlp bytes for the storage value corresponding to the provided address, storage slot, and block hash
|
||||
func (r *Retriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) (string, []byte, []byte, error) {
|
||||
storageResult := new(nodeInfo)
|
||||
func (r *Retriever) RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(address common.Address, key, hash common.Hash) ([]byte, error) {
|
||||
var storageResult nodeInfo
|
||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
storageHash := crypto.Keccak256Hash(key.Bytes())
|
||||
if err := r.db.Get(storageResult, RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr, stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, nil, err
|
||||
if err := r.db.Get(&storageResult,
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr,
|
||||
stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if storageResult.StateLeafRemoved || storageResult.Removed {
|
||||
return "", EmptyNodeValue, EmptyNodeValue, nil
|
||||
return EmptyNodeValue, nil
|
||||
}
|
||||
return storageResult.Value, nil
|
||||
}
|
||||
|
||||
blockNumber, err := strconv.ParseUint(storageResult.BlockNumber, 10, 64)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
// RetrieveStorageAndRLP returns the cid and rlp bytes for the storage value corresponding to the
|
||||
// provided address, storage slot, and block hash
|
||||
func (r *Retriever) RetrieveStorageAndRLP(address common.Address, key, hash common.Hash) (string, []byte, error) {
|
||||
var storageResult nodeInfo
|
||||
stateLeafKey := crypto.Keccak256Hash(address.Bytes())
|
||||
storageHash := crypto.Keccak256Hash(key.Bytes())
|
||||
if err := r.db.Get(&storageResult,
|
||||
RetrieveStorageAndRLPByAddressHashAndLeafKeyAndBlockHashPgStr,
|
||||
stateLeafKey.Hex(), storageHash.Hex(), hash.Hex()); err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
storageResult.Data, err = shared.FetchIPLD(r.db, storageResult.CID, blockNumber)
|
||||
if err != nil {
|
||||
return "", nil, nil, err
|
||||
if storageResult.StateLeafRemoved || storageResult.Removed {
|
||||
return "", EmptyNodeValue, nil
|
||||
}
|
||||
|
||||
var i []interface{}
|
||||
if err := rlp.DecodeBytes(storageResult.Data, &i); err != nil {
|
||||
err = fmt.Errorf("error decoding storage leaf node rlp: %s", err.Error())
|
||||
return "", nil, nil, err
|
||||
}
|
||||
if len(i) != 2 {
|
||||
return "", nil, nil, fmt.Errorf("eth Retriever expected storage leaf node rlp to decode into two elements")
|
||||
}
|
||||
return storageResult.CID, storageResult.Data, i[1].([]byte), nil
|
||||
return storageResult.CID, storageResult.Data, nil
|
||||
}
|
||||
|
||||
+18
-15
@@ -17,16 +17,19 @@
|
||||
package eth_test
|
||||
|
||||
import (
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/interfaces"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/jmoiron/sqlx"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
var _ = Describe("Retriever", func() {
|
||||
@@ -34,6 +37,7 @@ var _ = Describe("Retriever", func() {
|
||||
db *sqlx.DB
|
||||
diffIndexer interfaces.StateDiffIndexer
|
||||
retriever *eth.Retriever
|
||||
ctx = context.Background()
|
||||
)
|
||||
BeforeEach(func() {
|
||||
db = shared.SetupDB()
|
||||
@@ -43,20 +47,19 @@ var _ = Describe("Retriever", func() {
|
||||
})
|
||||
AfterEach(func() {
|
||||
shared.TearDownDB(db)
|
||||
db.Close()
|
||||
})
|
||||
|
||||
Describe("Retrieve", func() {
|
||||
BeforeEach(func() {
|
||||
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
It("Retrieve", func() {
|
||||
tx, err := diffIndexer.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
for _, node := range test_helpers.MockStateNodes {
|
||||
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
for _, node := range test_helpers.MockStateNodes {
|
||||
err = diffIndexer.PushStateNode(tx, node, test_helpers.MockBlock.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
}
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
Describe("RetrieveFirstBlockNumber", func() {
|
||||
@@ -166,5 +169,5 @@ var _ = Describe("Retriever", func() {
|
||||
func newMockBlock(blockNumber uint64) *types.Block {
|
||||
header := test_helpers.MockHeader
|
||||
header.Number.SetUint64(blockNumber)
|
||||
return types.NewBlock(&test_helpers.MockHeader, test_helpers.MockTransactions, nil, test_helpers.MockReceipts, new(trie.Trie))
|
||||
return types.NewBlock(&test_helpers.MockHeader, test_helpers.MockTransactions, nil, test_helpers.MockReceipts, trie.NewEmpty(nil))
|
||||
}
|
||||
|
||||
+38
-27
@@ -95,24 +95,23 @@ const (
|
||||
)
|
||||
WHERE block_hash = $1
|
||||
ORDER BY eth.transaction_cids.index ASC`
|
||||
// TODO: join on ipld.blocks and return IPLD object in this query instead of round tripping back to ipld.blocks
|
||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.cid, state_cids.block_number, state_cids.removed
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (
|
||||
state_cids.header_id = header_cids.block_hash
|
||||
AND state_cids.block_number = header_cids.block_number
|
||||
)
|
||||
WHERE state_leaf_key = $1
|
||||
AND header_cids.block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = $2)
|
||||
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
||||
ORDER BY header_cids.block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveAccountByLeafKeyAndBlockHashPgStr = `SELECT state_cids.nonce, state_cids.balance, state_cids.storage_root, state_cids.code_hash, state_cids.removed
|
||||
FROM eth.state_cids
|
||||
INNER JOIN eth.header_cids ON (
|
||||
state_cids.header_id = header_cids.block_hash
|
||||
AND state_cids.block_number = header_cids.block_number
|
||||
)
|
||||
WHERE state_leaf_key = $1
|
||||
AND header_cids.block_number <= (SELECT block_number
|
||||
FROM eth.header_cids
|
||||
WHERE block_hash = $2)
|
||||
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
||||
ORDER BY header_cids.block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveFilteredGQLLogs = `SELECT CAST(eth.log_cids.block_number as TEXT), eth.log_cids.header_id as block_hash,
|
||||
eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id, eth.log_cids.address,
|
||||
eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3, eth.log_cids.log_data,
|
||||
data, eth.receipt_cids.cid, eth.receipt_cids.post_status, eth.receipt_cids.tx_id AS tx_hash
|
||||
eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3,
|
||||
data, eth.receipt_cids.cid AS rct_cid, eth.receipt_cids.post_status, eth.receipt_cids.tx_id AS tx_hash
|
||||
FROM eth.log_cids, eth.receipt_cids, ipld.blocks
|
||||
WHERE eth.log_cids.rct_id = receipt_cids.tx_id
|
||||
AND eth.log_cids.header_id = receipt_cids.header_id
|
||||
@@ -122,19 +121,28 @@ const (
|
||||
AND receipt_cids.header_id = $1`
|
||||
RetrieveFilteredLogs = `SELECT CAST(eth.log_cids.block_number as TEXT), eth.log_cids.cid, eth.log_cids.index, eth.log_cids.rct_id,
|
||||
eth.log_cids.address, eth.log_cids.topic0, eth.log_cids.topic1, eth.log_cids.topic2, eth.log_cids.topic3,
|
||||
eth.log_cids.log_data, eth.transaction_cids.tx_hash, eth.transaction_cids.index as txn_index,
|
||||
eth.receipt_cids.cid as cid, eth.receipt_cids.post_status, header_cids.block_hash
|
||||
FROM eth.log_cids, eth.receipt_cids, eth.transaction_cids, eth.header_cids
|
||||
eth.transaction_cids.tx_hash, eth.transaction_cids.index as txn_index,
|
||||
blocks.data, eth.receipt_cids.cid AS rct_cid, eth.receipt_cids.post_status, header_cids.block_hash
|
||||
FROM eth.log_cids, eth.receipt_cids, eth.transaction_cids, eth.header_cids, ipld.blocks
|
||||
WHERE eth.log_cids.rct_id = receipt_cids.tx_id
|
||||
AND eth.log_cids.header_id = eth.receipt_cids.header_id
|
||||
AND eth.log_cids.block_number = eth.receipt_cids.block_number
|
||||
AND log_cids.cid = blocks.key
|
||||
AND log_cids.block_number = blocks.block_number
|
||||
AND receipt_cids.tx_id = transaction_cids.tx_hash
|
||||
AND receipt_cids.header_id = transaction_cids.header_id
|
||||
AND receipt_cids.block_number = transaction_cids.block_number
|
||||
AND transaction_cids.header_id = header_cids.block_hash
|
||||
AND transaction_cids.block_number = header_cids.block_number`
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, val, block_number, removed, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)`
|
||||
RetrieveCanonicalBlockHashByNumber = `SELECT block_hash
|
||||
RetrieveStorageLeafByAddressHashAndLeafKeyAndBlockHashPgStr = `SELECT cid, val, block_number, removed, state_leaf_removed FROM get_storage_at_by_hash($1, $2, $3)`
|
||||
RetrieveStorageAndRLPByAddressHashAndLeafKeyAndBlockHashPgStr = `
|
||||
SELECT cid, val, storage.block_number, removed, state_leaf_removed, data
|
||||
FROM get_storage_at_by_hash($1, $2, $3) AS storage
|
||||
INNER JOIN ipld.blocks ON (
|
||||
storage.cid = blocks.key
|
||||
AND storage.block_number = blocks.block_number
|
||||
)`
|
||||
RetrieveCanonicalBlockHashByNumber = `SELECT block_hash
|
||||
FROM canonical_header_hash($1) AS block_hash
|
||||
WHERE block_hash IS NOT NULL`
|
||||
RetrieveCanonicalHeaderByNumber = `SELECT cid, data FROM eth.header_cids
|
||||
@@ -143,6 +151,12 @@ const (
|
||||
AND header_cids.block_number = blocks.block_number
|
||||
)
|
||||
WHERE block_hash = (SELECT canonical_header_hash($1))`
|
||||
RetrieveCanonicalHeaderAndHashByNumber = `SELECT data, block_hash FROM eth.header_cids
|
||||
INNER JOIN ipld.blocks ON (
|
||||
header_cids.cid = blocks.key
|
||||
AND header_cids.block_number = blocks.block_number
|
||||
)
|
||||
WHERE block_hash = (SELECT canonical_header_hash($1))`
|
||||
RetrieveTD = `SELECT CAST(td as TEXT) FROM eth.header_cids
|
||||
WHERE header_cids.block_hash = $1`
|
||||
RetrieveRPCTransaction = `SELECT blocks.data, header_id, transaction_cids.block_number, index
|
||||
@@ -151,11 +165,8 @@ const (
|
||||
AND blocks.block_number = transaction_cids.block_number
|
||||
AND transaction_cids.tx_hash = $1
|
||||
AND transaction_cids.header_id = (SELECT canonical_header_hash(transaction_cids.block_number))`
|
||||
RetrieveCodeHashByLeafKeyAndBlockHash = `SELECT code_hash FROM eth.state_accounts, eth.state_cids, eth.header_cids
|
||||
WHERE state_accounts.header_id = state_cids.header_id
|
||||
AND state_accounts.state_path = state_cids.state_path
|
||||
AND state_accounts.block_number = state_cids.block_number
|
||||
AND state_cids.header_id = header_cids.block_hash
|
||||
RetrieveCodeHashByLeafKeyAndBlockHash = `SELECT code_hash FROM eth.state_cids, eth.header_cids
|
||||
WHERE state_cids.header_id = header_cids.block_hash
|
||||
AND state_cids.block_number = header_cids.block_number
|
||||
AND state_leaf_key = $1
|
||||
AND header_cids.block_number <= (SELECT block_number
|
||||
@@ -164,7 +175,7 @@ const (
|
||||
AND header_cids.block_hash = (SELECT canonical_header_hash(header_cids.block_number))
|
||||
ORDER BY header_cids.block_number DESC
|
||||
LIMIT 1`
|
||||
RetrieveCodeByMhKey = `SELECT data FROM ipld.blocks WHERE key = $1`
|
||||
RetrieveCodeByKey = `SELECT data FROM ipld.blocks WHERE key = $1`
|
||||
)
|
||||
|
||||
type ipldResult struct {
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 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 eth_state_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func TestETHSuite(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "ipld-eth-server/pkg/eth/state_test")
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package eth_state_test
|
||||
|
||||
import (
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func CheckGetSliceResponse(sliceResponse eth.GetSliceResponse, expectedResponse eth.GetSliceResponse) {
|
||||
Expect(sliceResponse.SliceID).To(Equal(expectedResponse.SliceID))
|
||||
Expect(sliceResponse.TrieNodes).To(Equal(expectedResponse.TrieNodes))
|
||||
Expect(sliceResponse.Leaves).To(Equal(expectedResponse.Leaves))
|
||||
Expect(sliceResponse.MetaData.NodeStats).To(Equal(expectedResponse.MetaData.NodeStats))
|
||||
}
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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 eth_test
|
||||
package eth_state_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
@@ -22,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -30,21 +29,26 @@ import (
|
||||
"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"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/jmoiron/sqlx"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
// "github.com/ethereum/go-ethereum/statediff/test_helpers"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
var (
|
||||
parsedABI abi.ABI
|
||||
parsedABI abi.ABI
|
||||
randomAddress = common.HexToAddress("0x9F4203bd7a11aCB94882050E6f1C3ab14BBaD3D9")
|
||||
randomHash = crypto.Keccak256Hash(randomAddress.Bytes())
|
||||
number = rpc.BlockNumber(test_helpers.BlockNumber.Int64())
|
||||
|
||||
block1StateRoot = common.HexToHash("0xa1f614839ebdd58677df2c9d66a3e0acc9462acc49fad6006d0b6e5d2b98ed21")
|
||||
rootDataHashBlock1 = "a1f614839ebdd58677df2c9d66a3e0acc9462acc49fad6006d0b6e5d2b98ed21"
|
||||
@@ -83,7 +87,7 @@ var (
|
||||
|
||||
func init() {
|
||||
// load abi
|
||||
abiBytes, err := ioutil.ReadFile("./test_helpers/abi.json")
|
||||
abiBytes, err := ioutil.ReadFile("../test_helpers/abi.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -93,132 +97,133 @@ func init() {
|
||||
}
|
||||
}
|
||||
|
||||
var _ = Describe("eth state reading tests", func() {
|
||||
const chainLength = 5
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *sqlx.DB
|
||||
api *eth.PublicEthAPI
|
||||
backend *eth.Backend
|
||||
chainConfig = params.TestChainConfig
|
||||
mockTD = big.NewInt(1337)
|
||||
expectedCanonicalHeader map[string]interface{}
|
||||
)
|
||||
It("test init", func() {
|
||||
// db and type initializations
|
||||
var err error
|
||||
db = shared.SetupDB()
|
||||
transformer := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
const chainLength = 5
|
||||
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
GroupCacheConfig: &shared.GroupCacheConfig{
|
||||
StateDB: shared.GroupConfig{
|
||||
Name: "eth_state_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
var (
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *sqlx.DB
|
||||
api *eth.PublicEthAPI
|
||||
backend *eth.Backend
|
||||
chainConfig = &*params.TestChainConfig
|
||||
mockTD = big.NewInt(1337)
|
||||
expectedCanonicalHeader map[string]interface{}
|
||||
ctx = context.Background()
|
||||
)
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
chainConfig.LondonBlock = big.NewInt(100)
|
||||
|
||||
// db and type initializations
|
||||
var err error
|
||||
db = shared.SetupDB()
|
||||
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000), // Max gas capacity for a rpc call.
|
||||
GroupCacheConfig: &shared.GroupCacheConfig{
|
||||
StateDB: shared.GroupConfig{
|
||||
Name: "eth_state_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api, _ = eth.NewPublicEthAPI(backend, nil, eth.APIConfig{false, false, false, false, shared.DefaultStateDiffTimeout})
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
api, _ = eth.NewPublicEthAPI(backend, nil, eth.APIConfig{StateDiffTimeout: shared.DefaultStateDiffTimeout})
|
||||
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(chainLength, test_helpers.Genesis, test_helpers.TestChainGen)
|
||||
params := statediff.Params{
|
||||
IntermediateStateNodes: true,
|
||||
IntermediateStorageNodes: true,
|
||||
}
|
||||
canonicalHeader := blocks[1].Header()
|
||||
expectedCanonicalHeader = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(canonicalHeader.Number),
|
||||
"hash": canonicalHeader.Hash(),
|
||||
"parentHash": canonicalHeader.ParentHash,
|
||||
"nonce": canonicalHeader.Nonce,
|
||||
"mixHash": canonicalHeader.MixDigest,
|
||||
"sha3Uncles": canonicalHeader.UncleHash,
|
||||
"logsBloom": canonicalHeader.Bloom,
|
||||
"stateRoot": canonicalHeader.Root,
|
||||
"miner": canonicalHeader.Coinbase,
|
||||
"difficulty": (*hexutil.Big)(canonicalHeader.Difficulty),
|
||||
"extraData": hexutil.Bytes([]byte{}),
|
||||
"size": hexutil.Uint64(canonicalHeader.Size()),
|
||||
"gasLimit": hexutil.Uint64(canonicalHeader.GasLimit),
|
||||
"gasUsed": hexutil.Uint64(canonicalHeader.GasUsed),
|
||||
"timestamp": hexutil.Uint64(canonicalHeader.Time),
|
||||
"transactionsRoot": canonicalHeader.TxHash,
|
||||
"receiptsRoot": canonicalHeader.ReceiptHash,
|
||||
"totalDifficulty": (*hexutil.Big)(mockTD),
|
||||
}
|
||||
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
|
||||
builder := statediff.NewBuilder(chain.StateCache())
|
||||
for i, block := range blocks {
|
||||
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]
|
||||
// make the test blockchain (and state)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(chainLength, test_helpers.Genesis, test_helpers.TestChainGen, chainConfig)
|
||||
|
||||
transformer := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
params := statediff.Params{}
|
||||
canonicalHeader := blocks[1].Header()
|
||||
expectedCanonicalHeader = map[string]interface{}{
|
||||
"number": (*hexutil.Big)(canonicalHeader.Number),
|
||||
"hash": canonicalHeader.Hash(),
|
||||
"parentHash": canonicalHeader.ParentHash,
|
||||
"nonce": canonicalHeader.Nonce,
|
||||
"mixHash": canonicalHeader.MixDigest,
|
||||
"sha3Uncles": canonicalHeader.UncleHash,
|
||||
"logsBloom": canonicalHeader.Bloom,
|
||||
"stateRoot": canonicalHeader.Root,
|
||||
"miner": canonicalHeader.Coinbase,
|
||||
"difficulty": (*hexutil.Big)(canonicalHeader.Difficulty),
|
||||
"extraData": hexutil.Bytes([]byte{}),
|
||||
"size": hexutil.Uint64(canonicalHeader.Size()),
|
||||
"gasLimit": hexutil.Uint64(canonicalHeader.GasLimit),
|
||||
"gasUsed": hexutil.Uint64(canonicalHeader.GasUsed),
|
||||
"timestamp": hexutil.Uint64(canonicalHeader.Time),
|
||||
"transactionsRoot": canonicalHeader.TxHash,
|
||||
"receiptsRoot": canonicalHeader.ReceiptHash,
|
||||
"totalDifficulty": (*hexutil.Big)(mockTD),
|
||||
}
|
||||
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
|
||||
builder := statediff.NewBuilder(chain.StateCache())
|
||||
for i, block := range blocks {
|
||||
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(),
|
||||
}
|
||||
diff, err := builder.BuildStateDiffObject(args, params)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx, err := transformer.PushBlock(block, rcts, mockTD)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, node := range diff.Nodes {
|
||||
err = transformer.PushStateNode(tx, node, block.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
} else {
|
||||
args = statediff.Args{
|
||||
OldStateRoot: blocks[i-1].Root(),
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
err = tx.Submit(err)
|
||||
rcts = receipts[i-1]
|
||||
}
|
||||
diff, err := builder.BuildStateDiffObject(args, params)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
tx, err := transformer.PushBlock(block, rcts, mockTD)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, node := range diff.Nodes {
|
||||
err = transformer.PushStateNode(tx, node, block.Hash().String())
|
||||
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())
|
||||
|
||||
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
for _, ipld := range diff.IPLDs {
|
||||
err = transformer.PushIPLD(tx, ipld)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
// The non-canonical header has a child
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
|
||||
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())
|
||||
|
||||
hash := sdtypes.CodeAndCodeHash{
|
||||
Hash: test_helpers.CodeHash,
|
||||
Code: test_helpers.ContractCode,
|
||||
}
|
||||
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = indexAndPublisher.PushCodeAndCodeHash(tx, hash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// wait for tx batch process to complete.
|
||||
time.Sleep(10000 * time.Millisecond)
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
defer It("test teardown", func() {
|
||||
shared.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
// The non-canonical header has a child
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
shared.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
|
||||
var _ = Describe("eth state reading tests", func() {
|
||||
|
||||
Describe("eth_call", func() {
|
||||
It("Applies call args (tx data) on top of state, returning the result (e.g. a Getter method call)", func() {
|
||||
@@ -229,6 +234,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
To: &test_helpers.ContractAddr,
|
||||
Data: &bdata,
|
||||
}
|
||||
|
||||
// Before contract deployment, returns nil
|
||||
res, err := api.Call(context.Background(), callArgs, rpc.BlockNumberOrHashWithNumber(0), nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
@@ -261,25 +267,25 @@ var _ = Describe("eth state reading tests", func() {
|
||||
})
|
||||
})
|
||||
|
||||
var (
|
||||
expectedContractBalance = (*hexutil.Big)(common.Big0)
|
||||
expectedBankBalanceBlock0 = (*hexutil.Big)(test_helpers.TestBankFunds)
|
||||
|
||||
expectedAcct1BalanceBlock1 = (*hexutil.Big)(big.NewInt(10000))
|
||||
expectedBankBalanceBlock1 = (*hexutil.Big)(new(big.Int).Sub(test_helpers.TestBankFunds, big.NewInt(10000)))
|
||||
|
||||
expectedAcct2BalanceBlock2 = (*hexutil.Big)(big.NewInt(1000))
|
||||
expectedBankBalanceBlock2 = (*hexutil.Big)(new(big.Int).Sub(expectedBankBalanceBlock1.ToInt(), big.NewInt(1000)))
|
||||
|
||||
expectedAcct2BalanceBlock3 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock2.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct2BalanceBlock4 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock3.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct1BalanceBlock5 = (*hexutil.Big)(new(big.Int).Add(expectedAcct1BalanceBlock1.ToInt(), test_helpers.MiningReward))
|
||||
)
|
||||
|
||||
Describe("eth_getBalance", func() {
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided number", func() {
|
||||
var (
|
||||
expectedContractBalance = (*hexutil.Big)(common.Big0)
|
||||
expectedBankBalanceBlock0 = (*hexutil.Big)(test_helpers.TestBankFunds)
|
||||
|
||||
expectedAcct1BalanceBlock1 = (*hexutil.Big)(big.NewInt(10000))
|
||||
expectedBankBalanceBlock1 = (*hexutil.Big)(new(big.Int).Sub(test_helpers.TestBankFunds, big.NewInt(10000)))
|
||||
|
||||
expectedAcct2BalanceBlock2 = (*hexutil.Big)(big.NewInt(1000))
|
||||
expectedBankBalanceBlock2 = (*hexutil.Big)(new(big.Int).Sub(expectedBankBalanceBlock1.ToInt(), big.NewInt(1000)))
|
||||
|
||||
expectedAcct2BalanceBlock3 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock2.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct2BalanceBlock4 = (*hexutil.Big)(new(big.Int).Add(expectedAcct2BalanceBlock3.ToInt(), test_helpers.MiningReward))
|
||||
|
||||
expectedAcct1BalanceBlock5 = (*hexutil.Big)(new(big.Int).Add(expectedAcct1BalanceBlock1.ToInt(), test_helpers.MiningReward))
|
||||
)
|
||||
|
||||
It("Retrieves account balance by block number", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock0))
|
||||
@@ -364,7 +370,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
})
|
||||
It("Retrieves the eth balance for the provided account address at the block with the provided hash", func() {
|
||||
It("Retrieves account balance by block hash", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.TestBankAddress, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock0))
|
||||
@@ -449,7 +455,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal(expectedBankBalanceBlock2))
|
||||
})
|
||||
It("Returns `0` for an account it cannot find the balance for an account at the provided block number", func() {
|
||||
It("Returns 0 if account balance not found by block number", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithNumber(0))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
@@ -462,7 +468,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
})
|
||||
It("Returns `0` for an error for an account it cannot find the balance for an account at the provided block hash", func() {
|
||||
It("Returns 0 if account balance not found by block hash", func() {
|
||||
bal, err := api.GetBalance(ctx, test_helpers.Account1Addr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
@@ -474,7 +480,6 @@ var _ = Describe("eth state reading tests", func() {
|
||||
bal, err = api.GetBalance(ctx, test_helpers.ContractAddr, rpc.BlockNumberOrHashWithHash(blocks[0].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(bal).To(Equal((*hexutil.Big)(common.Big0)))
|
||||
|
||||
})
|
||||
})
|
||||
|
||||
@@ -498,7 +503,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Expect(code).To(Equal((hexutil.Bytes)(test_helpers.ContractCode)))
|
||||
})
|
||||
It("Returns `nil` for an account it cannot find the code for", func() {
|
||||
code, err := api.GetCode(ctx, randomAddr, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
code, err := api.GetCode(ctx, randomAddress, rpc.BlockNumberOrHashWithHash(blocks[3].Hash(), true))
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(code).To(BeEmpty())
|
||||
})
|
||||
@@ -604,7 +609,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the state slice for root path with 0 depth", func() {
|
||||
path := "0x"
|
||||
@@ -633,7 +638,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the state slice for a path to an account", func() {
|
||||
path := "0x06"
|
||||
@@ -669,7 +674,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the state slice for a path to a non-existing account", func() {
|
||||
path := "0x06"
|
||||
@@ -698,7 +703,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
|
||||
It("Retrieves the storage slice for root path", func() {
|
||||
@@ -731,7 +736,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the storage slice for root path with 0 depth", func() {
|
||||
path := "0x"
|
||||
@@ -760,7 +765,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the storage slice for root path with deleted nodes", func() {
|
||||
path := "0x"
|
||||
@@ -789,7 +794,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
It("Retrieves the storage slice for a path to a storage node", func() {
|
||||
path := "0x0b"
|
||||
@@ -820,7 +825,7 @@ var _ = Describe("eth state reading tests", func() {
|
||||
Leaves: map[string]eth.GetSliceResponseAccount{},
|
||||
}
|
||||
|
||||
eth.CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
CheckGetSliceResponse(*sliceResponse, expectedResponse)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,49 +0,0 @@
|
||||
// VulcanizeDB
|
||||
// Copyright © 2019 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 eth
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
// TxModelsContainsCID used to check if a list of TxModels contains a specific cid string
|
||||
func TxModelsContainsCID(txs []models.TxModel, cid string) bool {
|
||||
for _, tx := range txs {
|
||||
if tx.CID == cid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// ReceiptModelsContainsCID used to check if a list of ReceiptModel contains a specific cid string
|
||||
func ReceiptModelsContainsCID(rcts []models.ReceiptModel, cid string) bool {
|
||||
for _, rct := range rcts {
|
||||
if rct.LeafCID == cid {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func CheckGetSliceResponse(sliceResponse GetSliceResponse, expectedResponse GetSliceResponse) {
|
||||
Expect(sliceResponse.SliceID).To(Equal(expectedResponse.SliceID))
|
||||
Expect(sliceResponse.MetaData.NodeStats).To(Equal(expectedResponse.MetaData.NodeStats))
|
||||
Expect(sliceResponse.TrieNodes).To(Equal(expectedResponse.TrieNodes))
|
||||
Expect(sliceResponse.Leaves).To(Equal(expectedResponse.Leaves))
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
package test_helpers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
type IndexChainParams struct {
|
||||
Blocks []*types.Block
|
||||
Receipts []types.Receipts
|
||||
StateCache state.Database
|
||||
ChainConfig *params.ChainConfig
|
||||
|
||||
StateDiffParams statediff.Params
|
||||
TotalDifficulty *big.Int
|
||||
// Whether to skip indexing state nodes (state_cids, storage_cids)
|
||||
SkipStateNodes bool
|
||||
// Whether to skip indexing IPLD blocks
|
||||
SkipIPLDs bool
|
||||
}
|
||||
|
||||
func IndexChain(params IndexChainParams) error {
|
||||
indexer := shared.SetupTestStateDiffIndexer(context.Background(), params.ChainConfig, Genesis.Hash())
|
||||
builder := statediff.NewBuilder(params.StateCache)
|
||||
// iterate over the blocks, generating statediff payloads, and transforming the data into Postgres
|
||||
for i, block := range params.Blocks {
|
||||
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: params.Blocks[i-1].Root(),
|
||||
NewStateRoot: block.Root(),
|
||||
BlockNumber: block.Number(),
|
||||
BlockHash: block.Hash(),
|
||||
}
|
||||
rcts = params.Receipts[i-1]
|
||||
}
|
||||
|
||||
diff, err := builder.BuildStateDiffObject(args, params.StateDiffParams)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tx, err := indexer.PushBlock(block, rcts, params.TotalDifficulty)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !params.SkipStateNodes {
|
||||
for _, node := range diff.Nodes {
|
||||
if err = indexer.PushStateNode(tx, node, block.Hash().String()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if !params.SkipIPLDs {
|
||||
for _, ipld := range diff.IPLDs {
|
||||
if err := indexer.PushIPLD(tx, ipld); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if err = tx.Submit(err); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -17,7 +17,6 @@
|
||||
package test_helpers
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -28,10 +27,7 @@ import (
|
||||
"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/statediff/indexer/ipld"
|
||||
"github.com/ethereum/go-ethereum/statediff/test_helpers"
|
||||
"github.com/ipfs/go-cid"
|
||||
)
|
||||
|
||||
// Test variables
|
||||
@@ -40,7 +36,8 @@ var (
|
||||
TestBankKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
TestBankAddress = crypto.PubkeyToAddress(TestBankKey.PublicKey) //0x71562b71999873DB5b286dF957af199Ec94617F7
|
||||
TestBankFunds = big.NewInt(100000000)
|
||||
Genesis = test_helpers.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds)
|
||||
|
||||
Genesis = test_helpers.GenesisBlockForTesting(Testdb, TestBankAddress, TestBankFunds, big.NewInt(params.InitialBaseFee), params.MaxGasLimit)
|
||||
|
||||
Account1Key, _ = crypto.HexToECDSA("8a1f9a8f95be41cd7ccb6168179afb4504aefe388d1e14474d32c45c72ce7b7a")
|
||||
Account2Key, _ = crypto.HexToECDSA("49a7b37aa6f6645917e7b807e9d1c00d4fa71f18343b0d4122a4d2df64dd6fee")
|
||||
@@ -65,11 +62,12 @@ data function sig: 73d4a13a
|
||||
|
||||
// MakeChain creates a chain of n blocks starting at and including parent.
|
||||
// the returned hash chain is ordered head->parent.
|
||||
func MakeChain(n int, parent *types.Block, chainGen func(int, *core.BlockGen)) ([]*types.Block, []types.Receipts, *core.BlockChain) {
|
||||
config := params.TestChainConfig
|
||||
config.LondonBlock = big.NewInt(100)
|
||||
func MakeChain(n int, parent *types.Block, chainGen func(int, *core.BlockGen), config *params.ChainConfig) ([]*types.Block, []types.Receipts, *core.BlockChain) {
|
||||
blocks, receipts := core.GenerateChain(config, parent, ethash.NewFaker(), Testdb, n, chainGen)
|
||||
chain, _ := core.NewBlockChain(Testdb, nil, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
chain, err := core.NewBlockChain(Testdb, nil, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return append([]*types.Block{parent}, blocks...), receipts, chain
|
||||
}
|
||||
|
||||
@@ -110,40 +108,3 @@ func TestChainGen(i int, block *core.BlockGen) {
|
||||
block.AddTx(tx)
|
||||
}
|
||||
}
|
||||
|
||||
// GetRctLeafNodeData converts the receipts to receipt trie and returns the receipt leaf node IPLD data and
|
||||
// corresponding CIDs
|
||||
func GetRctLeafNodeData(rcts types.Receipts) ([]cid.Cid, [][]byte, error) {
|
||||
receiptTrie := ipld.NewRctTrie()
|
||||
for idx, rct := range rcts {
|
||||
ethRct, err := ipld.NewReceipt(rct)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if err = receiptTrie.Add(idx, ethRct.RawData()); err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
}
|
||||
|
||||
rctLeafNodes, keys, err := receiptTrie.GetLeafNodes()
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ethRctleafNodeCids := make([]cid.Cid, len(rctLeafNodes))
|
||||
ethRctleafNodeData := make([][]byte, len(rctLeafNodes))
|
||||
for i, rln := range rctLeafNodes {
|
||||
var idx uint
|
||||
|
||||
r := bytes.NewReader(keys[i].TrieKey)
|
||||
err = rlp.Decode(r, &idx)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
ethRctleafNodeCids[idx] = rln.Cid()
|
||||
ethRctleafNodeData[idx] = rln.RawData()
|
||||
}
|
||||
|
||||
return ethRctleafNodeCids, ethRctleafNodeData, nil
|
||||
}
|
||||
|
||||
@@ -22,17 +22,19 @@ import (
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
testhelpers "github.com/ethereum/go-ethereum/statediff/test_helpers"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/ipfs/go-cid"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
)
|
||||
|
||||
// Test variables
|
||||
@@ -58,6 +60,7 @@ var (
|
||||
ReceiptHash: common.HexToHash("0x1"),
|
||||
Difficulty: big.NewInt(500001),
|
||||
Extra: []byte{},
|
||||
ParentHash: Genesis.Hash(),
|
||||
},
|
||||
{
|
||||
Time: 2,
|
||||
@@ -67,9 +70,10 @@ var (
|
||||
ReceiptHash: common.HexToHash("0x2"),
|
||||
Difficulty: big.NewInt(500002),
|
||||
Extra: []byte{},
|
||||
ParentHash: Genesis.Hash(),
|
||||
},
|
||||
}
|
||||
MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
|
||||
MockBlock = createNewBlock(&MockHeader, MockTransactions, MockUncles, MockReceipts, trie.NewEmpty(nil))
|
||||
MockChildHeader = types.Header{
|
||||
Time: 0,
|
||||
Number: new(big.Int).Add(BlockNumber, common.Big1),
|
||||
@@ -80,25 +84,23 @@ var (
|
||||
Extra: []byte{},
|
||||
ParentHash: MockBlock.Header().Hash(),
|
||||
}
|
||||
MockChild = types.NewBlock(&MockChildHeader, MockTransactions, MockUncles, MockReceipts, new(trie.Trie))
|
||||
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
|
||||
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
|
||||
AnotherAddress1 = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476594")
|
||||
AnotherAddress2 = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476596")
|
||||
ContractAddress = crypto.CreateAddress(SenderAddr, MockTransactions[2].Nonce())
|
||||
ContractHash = crypto.Keccak256Hash(ContractAddress.Bytes()).String()
|
||||
MockContractByteCode = []byte{0, 1, 2, 3, 4, 5}
|
||||
mockTopic11 = common.HexToHash("0x04")
|
||||
mockTopic12 = common.HexToHash("0x06")
|
||||
mockTopic21 = common.HexToHash("0x05")
|
||||
mockTopic22 = common.HexToHash("0x07")
|
||||
mockTopic31 = common.HexToHash("0x08")
|
||||
mockTopic41 = common.HexToHash("0x09")
|
||||
mockTopic42 = common.HexToHash("0x0a")
|
||||
mockTopic43 = common.HexToHash("0x0b")
|
||||
mockTopic51 = common.HexToHash("0x0c")
|
||||
mockTopic61 = common.HexToHash("0x0d")
|
||||
MockLog1 = &types.Log{
|
||||
MockChild = types.NewBlock(&MockChildHeader, MockTransactions, MockUncles, MockReceipts, trie.NewEmpty(nil))
|
||||
Address = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476592")
|
||||
AnotherAddress = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476593")
|
||||
AnotherAddress1 = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476594")
|
||||
AnotherAddress2 = common.HexToAddress("0xaE9BEa628c4Ce503DcFD7E305CaB4e29E7476596")
|
||||
ContractAddress = crypto.CreateAddress(SenderAddr, MockTransactions[2].Nonce())
|
||||
mockTopic11 = common.HexToHash("0x04")
|
||||
mockTopic12 = common.HexToHash("0x06")
|
||||
mockTopic21 = common.HexToHash("0x05")
|
||||
mockTopic22 = common.HexToHash("0x07")
|
||||
mockTopic31 = common.HexToHash("0x08")
|
||||
mockTopic41 = common.HexToHash("0x09")
|
||||
mockTopic42 = common.HexToHash("0x0a")
|
||||
mockTopic43 = common.HexToHash("0x0b")
|
||||
mockTopic51 = common.HexToHash("0x0c")
|
||||
mockTopic61 = common.HexToHash("0x0d")
|
||||
MockLog1 = &types.Log{
|
||||
Address: Address,
|
||||
Topics: []common.Hash{mockTopic11, mockTopic12},
|
||||
Data: []byte{},
|
||||
@@ -148,71 +150,55 @@ var (
|
||||
Index: 5,
|
||||
}
|
||||
|
||||
rctCIDs, _, _ = GetRctLeafNodeData(MockReceipts)
|
||||
Rct1CID = rctCIDs[0]
|
||||
Rct4CID = rctCIDs[3]
|
||||
MockTrxMeta = []models.TxModel{
|
||||
rctCIDs, _ = getReceiptCIDs(MockReceipts)
|
||||
Rct1CID = rctCIDs[0]
|
||||
Rct4CID = rctCIDs[3]
|
||||
MockTrxMeta = []models.TxModel{
|
||||
{
|
||||
CID: "", // This is empty until we go to publish to ipfs
|
||||
MhKey: "",
|
||||
Src: SenderAddr.Hex(),
|
||||
Dst: Address.String(),
|
||||
Index: 0,
|
||||
TxHash: MockTransactions[0].Hash().String(),
|
||||
Data: []byte{},
|
||||
},
|
||||
{
|
||||
CID: "",
|
||||
MhKey: "",
|
||||
Src: SenderAddr.Hex(),
|
||||
Dst: AnotherAddress.String(),
|
||||
Index: 1,
|
||||
TxHash: MockTransactions[1].Hash().String(),
|
||||
Data: []byte{},
|
||||
},
|
||||
{
|
||||
CID: "",
|
||||
MhKey: "",
|
||||
Src: SenderAddr.Hex(),
|
||||
Dst: "",
|
||||
Index: 2,
|
||||
TxHash: MockTransactions[2].Hash().String(),
|
||||
Data: MockContractByteCode,
|
||||
},
|
||||
{
|
||||
CID: "",
|
||||
MhKey: "",
|
||||
Src: SenderAddr.Hex(),
|
||||
Dst: "",
|
||||
Index: 3,
|
||||
TxHash: MockTransactions[3].Hash().String(),
|
||||
Data: []byte{},
|
||||
},
|
||||
}
|
||||
MockRctMeta = []models.ReceiptModel{
|
||||
{
|
||||
LeafCID: "",
|
||||
LeafMhKey: "",
|
||||
Contract: "",
|
||||
ContractHash: "",
|
||||
CID: "",
|
||||
Contract: "",
|
||||
},
|
||||
{
|
||||
LeafCID: "",
|
||||
LeafMhKey: "",
|
||||
Contract: "",
|
||||
ContractHash: "",
|
||||
CID: "",
|
||||
Contract: "",
|
||||
},
|
||||
{
|
||||
LeafCID: "",
|
||||
LeafMhKey: "",
|
||||
Contract: ContractAddress.String(),
|
||||
ContractHash: ContractHash,
|
||||
CID: "",
|
||||
Contract: ContractAddress.String(),
|
||||
},
|
||||
{
|
||||
LeafCID: "",
|
||||
LeafMhKey: "",
|
||||
Contract: "",
|
||||
ContractHash: "",
|
||||
CID: "",
|
||||
Contract: "",
|
||||
},
|
||||
}
|
||||
|
||||
@@ -226,21 +212,20 @@ var (
|
||||
StorageValue,
|
||||
})
|
||||
|
||||
nonce1 = uint64(1)
|
||||
ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0"
|
||||
ContractCodeHash = crypto.Keccak256Hash(MockContractByteCode)
|
||||
contractPath = common.Bytes2Hex([]byte{'\x06'})
|
||||
ContractLeafKey = testhelpers.AddressToLeafKey(ContractAddress)
|
||||
ContractAccount, _ = rlp.EncodeToBytes(&types.StateAccount{
|
||||
Nonce: nonce1,
|
||||
ContractRoot = "0x821e2556a290c86405f8160a2d662042a431ba456b9db265c79bb837c04be5f0"
|
||||
contractPath = common.Bytes2Hex([]byte{'\x06'})
|
||||
ContractLeafKey = crypto.Keccak256(ContractAddress[:])
|
||||
ContractAccount = types.StateAccount{
|
||||
Nonce: uint64(1),
|
||||
Balance: big.NewInt(0),
|
||||
CodeHash: ContractCodeHash.Bytes(),
|
||||
CodeHash: CodeHash.Bytes(),
|
||||
Root: common.HexToHash(ContractRoot),
|
||||
})
|
||||
ContractPartialPath = common.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45")
|
||||
ContractLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
|
||||
}
|
||||
ContractAccountRLP, _ = rlp.EncodeToBytes(&ContractAccount)
|
||||
ContractPartialPath = common.Hex2Bytes("3114658a74d9cc9f7acf2c5cd696c3494d7c344d78bfec3add0d91ec4e8d1c45")
|
||||
ContractLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
|
||||
ContractPartialPath,
|
||||
ContractAccount,
|
||||
ContractAccountRLP,
|
||||
})
|
||||
|
||||
nonce0 = uint64(0)
|
||||
@@ -248,49 +233,49 @@ var (
|
||||
AccountRoot = "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"
|
||||
AccountCodeHash = common.HexToHash("0xc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a470")
|
||||
AccountAddresss = common.HexToAddress("0x0D3ab14BBaD3D99F4203bd7a11aCB94882050E7e")
|
||||
AccountLeafKey = testhelpers.Account2LeafKey
|
||||
Account, _ = rlp.EncodeToBytes(&types.StateAccount{
|
||||
AccountLeafKey = crypto.Keccak256(AccountAddresss[:])
|
||||
Account = types.StateAccount{
|
||||
Nonce: nonce0,
|
||||
Balance: AccountBalance,
|
||||
CodeHash: AccountCodeHash.Bytes(),
|
||||
Root: common.HexToHash(AccountRoot),
|
||||
})
|
||||
}
|
||||
AccountRLP, _ = rlp.EncodeToBytes(&Account)
|
||||
AccountPartialPath = common.Hex2Bytes("3957f3e2f04a0764c3a0491b175f69926da61efbcc8f61fa1455fd2d2b4cdd45")
|
||||
AccountLeafNode, _ = rlp.EncodeToBytes(&[]interface{}{
|
||||
AccountPartialPath,
|
||||
Account,
|
||||
AccountRLP,
|
||||
})
|
||||
|
||||
MockStateNodes = []sdtypes.StateNode{
|
||||
MockStateNodes = []sdtypes.StateLeafNode{
|
||||
{
|
||||
LeafKey: ContractLeafKey,
|
||||
Path: []byte{'\x06'},
|
||||
NodeValue: ContractLeafNode,
|
||||
NodeType: sdtypes.Leaf,
|
||||
StorageNodes: []sdtypes.StorageNode{
|
||||
AccountWrapper: sdtypes.AccountWrapper{
|
||||
Account: &ContractAccount,
|
||||
LeafKey: ContractLeafKey,
|
||||
CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(ContractLeafNode)).String(),
|
||||
},
|
||||
StorageDiff: []sdtypes.StorageLeafNode{
|
||||
{
|
||||
Path: []byte{},
|
||||
NodeType: sdtypes.Leaf,
|
||||
LeafKey: StorageLeafKey,
|
||||
NodeValue: StorageLeafNode,
|
||||
LeafKey: StorageLeafKey,
|
||||
Value: StorageValue,
|
||||
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(StorageLeafNode)).String(),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
LeafKey: AccountLeafKey,
|
||||
Path: []byte{'\x0c'},
|
||||
NodeValue: AccountLeafNode,
|
||||
NodeType: sdtypes.Leaf,
|
||||
StorageNodes: []sdtypes.StorageNode{},
|
||||
AccountWrapper: sdtypes.AccountWrapper{
|
||||
Account: &Account,
|
||||
LeafKey: AccountLeafKey,
|
||||
CID: ipld.Keccak256ToCid(ipld.MEthStateTrie, crypto.Keccak256(AccountLeafNode)).String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
MockStorageNodes = map[string][]sdtypes.StorageNode{
|
||||
MockStorageNodes = map[string][]sdtypes.StorageLeafNode{
|
||||
contractPath: {
|
||||
{
|
||||
LeafKey: StorageLeafKey,
|
||||
NodeValue: StorageLeafNode,
|
||||
NodeType: sdtypes.Leaf,
|
||||
Path: []byte{},
|
||||
LeafKey: StorageLeafKey,
|
||||
Value: StorageValue,
|
||||
CID: ipld.Keccak256ToCid(ipld.MEthStorageTrie, crypto.Keccak256(StorageLeafNode)).String(),
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -338,7 +323,7 @@ var (
|
||||
Extra: []byte{},
|
||||
},
|
||||
}
|
||||
MockLondonBlock = createNewBlock(&MockLondonHeader, MockLondonTransactions, MockLondonUncles, MockLondonReceipts, new(trie.Trie))
|
||||
MockLondonBlock = createNewBlock(&MockLondonHeader, MockLondonTransactions, MockLondonUncles, MockLondonReceipts, trie.NewEmpty(nil))
|
||||
)
|
||||
|
||||
func createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*types.Header, receipts []*types.Receipt, hasher types.TrieHasher) *types.Block {
|
||||
@@ -355,7 +340,7 @@ func createNewBlock(header *types.Header, txs []*types.Transaction, uncles []*ty
|
||||
// createDynamicTransactionsAndReceipts is a helper function to generate signed mock transactions and mock receipts with mock logs
|
||||
func createDynamicTransactionsAndReceipts(blockNumber *big.Int) (types.Transactions, types.Receipts, common.Address) {
|
||||
// make transactions
|
||||
config := params.TestChainConfig
|
||||
config := *params.TestChainConfig
|
||||
config.LondonBlock = blockNumber
|
||||
trx1 := types.NewTx(&types.DynamicFeeTx{
|
||||
ChainID: config.ChainID,
|
||||
@@ -368,7 +353,7 @@ func createDynamicTransactionsAndReceipts(blockNumber *big.Int) (types.Transacti
|
||||
Data: []byte{},
|
||||
})
|
||||
|
||||
transactionSigner := types.MakeSigner(config, blockNumber)
|
||||
transactionSigner := types.MakeSigner(&config, blockNumber)
|
||||
mockCurve := elliptic.P256()
|
||||
mockPrvKey, err := ecdsa.GenerateKey(mockCurve, rand.Reader)
|
||||
if err != nil {
|
||||
@@ -404,7 +389,7 @@ func createLegacyTransactionsAndReceipts() (types.Transactions, types.Receipts,
|
||||
// make transactions
|
||||
trx1 := types.NewTransaction(0, Address, big.NewInt(1000), 50, big.NewInt(100), []byte{})
|
||||
trx2 := types.NewTransaction(1, AnotherAddress, big.NewInt(2000), 100, big.NewInt(200), []byte{})
|
||||
trx3 := types.NewContractCreation(2, big.NewInt(1500), 75, big.NewInt(150), MockContractByteCode)
|
||||
trx3 := types.NewContractCreation(2, big.NewInt(1500), 75, big.NewInt(150), ContractCode)
|
||||
trx4 := types.NewTransaction(3, AnotherAddress1, big.NewInt(2000), 100, big.NewInt(200), []byte{})
|
||||
transactionSigner := types.MakeSigner(params.MainnetChainConfig, new(big.Int).Set(BlockNumber))
|
||||
mockCurve := elliptic.P256()
|
||||
@@ -463,3 +448,15 @@ func createLegacyTransactionsAndReceipts() (types.Transactions, types.Receipts,
|
||||
|
||||
return types.Transactions{signedTrx1, signedTrx2, signedTrx3, signedTrx4}, types.Receipts{mockReceipt1, mockReceipt2, mockReceipt3, mockReceipt4}, SenderAddr
|
||||
}
|
||||
|
||||
func getReceiptCIDs(rcts []*types.Receipt) ([]cid.Cid, error) {
|
||||
cids := make([]cid.Cid, len(rcts))
|
||||
for i, rct := range rcts {
|
||||
ethRct, err := ipld.NewReceipt(rct)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cids[i] = ethRct.Cid()
|
||||
}
|
||||
return cids, nil
|
||||
}
|
||||
|
||||
+8
-9
@@ -22,12 +22,11 @@ import (
|
||||
"math/big"
|
||||
"strconv"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/common/math"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/models"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
@@ -194,9 +193,9 @@ func (arg *CallArgs) ToMessage(globalGasCap uint64, baseFee *big.Int) (*core.Mes
|
||||
msg := &core.Message{
|
||||
Nonce: 0,
|
||||
GasLimit: gas,
|
||||
GasPrice: new(big.Int).Set(gasPrice),
|
||||
GasFeeCap: new(big.Int).Set(gasFeeCap),
|
||||
GasTipCap: new(big.Int).Set(gasTipCap),
|
||||
GasPrice: gasPrice,
|
||||
GasFeeCap: gasFeeCap,
|
||||
GasTipCap: gasTipCap,
|
||||
To: arg.To,
|
||||
Value: value,
|
||||
Data: data,
|
||||
@@ -216,8 +215,8 @@ type ConvertedPayload struct {
|
||||
TxMetaData []models.TxModel
|
||||
Receipts types.Receipts
|
||||
ReceiptMetaData []models.ReceiptModel
|
||||
StateNodes []sdtypes.StateNode
|
||||
StorageNodes map[string][]sdtypes.StorageNode
|
||||
StateNodes []sdtypes.StateLeafNode
|
||||
StorageNodes map[string][]sdtypes.StorageLeafNode
|
||||
}
|
||||
|
||||
// LogResult represent a log.
|
||||
@@ -232,7 +231,7 @@ type LogResult struct {
|
||||
Topic2 string `db:"topic2"`
|
||||
Topic3 string `db:"topic3"`
|
||||
LogLeafData []byte `db:"data"`
|
||||
RctCID string `db:"cid"`
|
||||
RctCID string `db:"rct_cid"`
|
||||
RctStatus uint64 `db:"post_status"`
|
||||
BlockNumber string `db:"block_number"`
|
||||
BlockHash string `db:"block_hash"`
|
||||
|
||||
@@ -43,12 +43,12 @@ type IPFSBlockResponse struct {
|
||||
}
|
||||
|
||||
type EthTransactionCIDResponse struct {
|
||||
CID string `json:"cid"`
|
||||
TxHash string `json:"txHash"`
|
||||
Index int32 `json:"index"`
|
||||
Src string `json:"src"`
|
||||
Dst string `json:"dst"`
|
||||
BlockByMhKey IPFSBlockResponse `json:"blockByMhKey"`
|
||||
CID string `json:"cid"`
|
||||
TxHash string `json:"txHash"`
|
||||
Index int32 `json:"index"`
|
||||
Src string `json:"src"`
|
||||
Dst string `json:"dst"`
|
||||
BlockByCid IPFSBlockResponse `json:"blockByCid"`
|
||||
}
|
||||
|
||||
type EthTransactionCIDByTxHash struct {
|
||||
@@ -72,7 +72,7 @@ type EthHeaderCIDResponse struct {
|
||||
UncleRoot string `json:"uncleRoot"`
|
||||
Bloom string `json:"bloom"`
|
||||
EthTransactionCIDsByHeaderId EthTransactionCIDsByHeaderIdResponse `json:"ethTransactionCidsByHeaderId"`
|
||||
BlockByMhKey IPFSBlockResponse `json:"blockByMhKey"`
|
||||
BlockByCid IPFSBlockResponse `json:"blockByCid"`
|
||||
}
|
||||
|
||||
type AllEthHeaderCIDsResponse struct {
|
||||
@@ -195,7 +195,7 @@ func (c *Client) AllEthHeaderCIDs(ctx context.Context, condition EthHeaderCIDCon
|
||||
receiptRoot
|
||||
uncleRoot
|
||||
bloom
|
||||
blockByMhKey {
|
||||
blockByCid {
|
||||
key
|
||||
data
|
||||
}
|
||||
@@ -244,7 +244,7 @@ func (c *Client) EthTransactionCIDByTxHash(ctx context.Context, txHash string) (
|
||||
index
|
||||
src
|
||||
dst
|
||||
blockByMhKey {
|
||||
blockByCid {
|
||||
data
|
||||
}
|
||||
}
|
||||
|
||||
+18
-21
@@ -34,9 +34,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
ipld_eth_statedb "github.com/cerc-io/ipld-eth-statedb"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
"github.com/cerc-io/ipld-eth-statedb/direct_by_leaf"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -51,8 +51,8 @@ type Account struct {
|
||||
}
|
||||
|
||||
// getState fetches the StateDB object for an account.
|
||||
func (a *Account) getState(ctx context.Context) (*ipld_eth_statedb.StateDB, error) {
|
||||
state, _, err := a.backend.IPLDStateDBAndHeaderByNumberOrHash(ctx, a.blockNrOrHash)
|
||||
func (a *Account) getState(ctx context.Context) (*state.StateDB, error) {
|
||||
state, _, err := a.backend.IPLDDirectStateDBAndHeaderByNumberOrHash(ctx, a.blockNrOrHash)
|
||||
return state, err
|
||||
}
|
||||
|
||||
@@ -1009,30 +1009,27 @@ func (r *Resolver) GetStorageAt(ctx context.Context, args struct {
|
||||
Contract common.Address
|
||||
Slot common.Hash
|
||||
}) (*StorageResult, error) {
|
||||
cid, ipldBlock, rlpValue, err := r.backend.Retriever.RetrieveStorageAtByAddressAndStorageSlotAndBlockHash(args.Contract, args.Slot, args.BlockHash)
|
||||
|
||||
cid, nodeRLP, err := r.backend.Retriever.RetrieveStorageAndRLP(args.Contract, args.Slot, args.BlockHash)
|
||||
if err != nil {
|
||||
if err == sql.ErrNoRows {
|
||||
ret := StorageResult{value: []byte{}, cid: "", ipldBlock: []byte{}}
|
||||
|
||||
return &ret, nil
|
||||
return &StorageResult{value: []byte{}, cid: "", ipldBlock: []byte{}}, nil
|
||||
}
|
||||
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if bytes.Equal(rlpValue, eth.EmptyNodeValue) {
|
||||
return &StorageResult{value: eth.EmptyNodeValue, cid: cid, ipldBlock: ipldBlock}, nil
|
||||
valueRLP, err := eth.DecodeLeafNode(nodeRLP)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if bytes.Equal(valueRLP, eth.EmptyNodeValue) {
|
||||
return &StorageResult{value: eth.EmptyNodeValue, cid: cid, ipldBlock: nodeRLP}, nil
|
||||
}
|
||||
|
||||
var value interface{}
|
||||
err = rlp.DecodeBytes(rlpValue, &value)
|
||||
err = rlp.DecodeBytes(valueRLP, &value)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ret := StorageResult{value: value.([]byte), cid: cid, ipldBlock: ipldBlock}
|
||||
return &ret, nil
|
||||
return &StorageResult{value: value.([]byte), cid: cid, ipldBlock: nodeRLP}, nil
|
||||
}
|
||||
|
||||
func (r *Resolver) GetLogs(ctx context.Context, args struct {
|
||||
@@ -1160,7 +1157,7 @@ func (t EthTransactionCID) Dst(ctx context.Context) string {
|
||||
return t.dst
|
||||
}
|
||||
|
||||
func (t EthTransactionCID) BlockByMhKey(ctx context.Context) IPFSBlock {
|
||||
func (t EthTransactionCID) BlockByCid(ctx context.Context) IPFSBlock {
|
||||
return t.ipfsBlock
|
||||
}
|
||||
|
||||
@@ -1249,7 +1246,7 @@ func (h EthHeaderCID) EthTransactionCidsByHeaderId(ctx context.Context) EthTrans
|
||||
return EthTransactionCIDsConnection{nodes: h.transactions}
|
||||
}
|
||||
|
||||
func (h EthHeaderCID) BlockByMhKey(ctx context.Context) IPFSBlock {
|
||||
func (h EthHeaderCID) BlockByCid(ctx context.Context) IPFSBlock {
|
||||
return h.ipfsBlock
|
||||
}
|
||||
|
||||
@@ -1326,7 +1323,7 @@ func (r *Resolver) AllEthHeaderCids(ctx context.Context, args struct {
|
||||
td: td,
|
||||
txRoot: headerCID.TxRoot,
|
||||
receiptRoot: headerCID.RctRoot,
|
||||
uncleRoot: headerCID.UncleRoot,
|
||||
uncleRoot: headerCID.UnclesHash,
|
||||
bloom: Bytes(headerCID.Bloom).String(),
|
||||
ipfsBlock: IPFSBlock{
|
||||
key: headerCID.IPLD.Key,
|
||||
|
||||
@@ -17,11 +17,9 @@
|
||||
package graphql_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
@@ -29,7 +27,3 @@ func TestGraphQL(t *testing.T) {
|
||||
RegisterFailHandler(Fail)
|
||||
RunSpecs(t, "graphql test suite")
|
||||
}
|
||||
|
||||
var _ = BeforeSuite(func() {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
})
|
||||
|
||||
+106
-144
@@ -30,151 +30,113 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/ipld"
|
||||
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||
"github.com/jmoiron/sqlx"
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/graphql"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
ethServerShared "github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/graphql"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
var _ = Describe("GraphQL", func() {
|
||||
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 *sqlx.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
|
||||
)
|
||||
const (
|
||||
gqlEndPoint = "127.0.0.1:8083"
|
||||
)
|
||||
|
||||
It("test init", func() {
|
||||
var err error
|
||||
db = shared.SetupDB()
|
||||
transformer := shared.SetupTestStateDiffIndexer(ctx, chainConfig, test_helpers.Genesis.Hash())
|
||||
var (
|
||||
randomAddr = common.HexToAddress("0x1C3ab14BBaD3D99F4203bd7a11aCB94882050E6f")
|
||||
randomHash = crypto.Keccak256Hash(randomAddr.Bytes())
|
||||
blocks []*types.Block
|
||||
receipts []types.Receipts
|
||||
chain *core.BlockChain
|
||||
db *sqlx.DB
|
||||
backend *eth.Backend
|
||||
graphQLServer *graphql.Service
|
||||
chainConfig = &*params.TestChainConfig
|
||||
client = graphql.NewClient(fmt.Sprintf("http://%s/graphql", gqlEndPoint))
|
||||
mockTD = big.NewInt(1337)
|
||||
ctx = context.Background()
|
||||
nonCanonBlockHash common.Hash
|
||||
nonCanonContractAddress common.Address
|
||||
)
|
||||
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
GroupCacheConfig: ðServerShared.GroupCacheConfig{
|
||||
StateDB: ethServerShared.GroupConfig{
|
||||
Name: "graphql_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
var _ = BeforeSuite(func() {
|
||||
var err error
|
||||
db = shared.SetupDB()
|
||||
|
||||
backend, err = eth.NewEthBackend(db, ð.Config{
|
||||
ChainConfig: chainConfig,
|
||||
VMConfig: vm.Config{},
|
||||
RPCGasCap: big.NewInt(10000000000),
|
||||
GroupCacheConfig: &shared.GroupCacheConfig{
|
||||
StateDB: shared.GroupConfig{
|
||||
Name: "graphql_test",
|
||||
CacheSizeInMB: 8,
|
||||
CacheExpiryInMins: 60,
|
||||
LogStatsIntervalInSecs: 0,
|
||||
},
|
||||
})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
},
|
||||
})
|
||||
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 sdtypes.StateObject
|
||||
diff, err = builder.BuildStateDiffObject(args, params)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
tx, err := transformer.PushBlock(block, rcts, mockTD)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
for _, node := range diff.Nodes {
|
||||
err = transformer.PushStateNode(tx, node, block.Hash().String())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
}
|
||||
|
||||
err = tx.Submit(err)
|
||||
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())
|
||||
|
||||
blockHash = test_helpers.MockBlock.Hash()
|
||||
contractAddress = test_helpers.ContractAddr
|
||||
|
||||
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// The non-canonical header has a child
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ccHash := sdtypes.CodeAndCodeHash{
|
||||
Hash: test_helpers.CodeHash,
|
||||
Code: test_helpers.ContractCode,
|
||||
}
|
||||
|
||||
err = indexAndPublisher.PushCodeAndCodeHash(tx, ccHash)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
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())
|
||||
// make the test blockchain (and state)
|
||||
chainConfig.LondonBlock = big.NewInt(100)
|
||||
blocks, receipts, chain = test_helpers.MakeChain(5, test_helpers.Genesis, test_helpers.TestChainGen, chainConfig)
|
||||
test_helpers.IndexChain(test_helpers.IndexChainParams{
|
||||
StateCache: chain.StateCache(),
|
||||
ChainConfig: chainConfig,
|
||||
Blocks: blocks,
|
||||
Receipts: receipts,
|
||||
TotalDifficulty: mockTD,
|
||||
})
|
||||
|
||||
defer It("test teardown", func() {
|
||||
err := graphQLServer.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
shared.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
// 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())
|
||||
|
||||
nonCanonBlockHash = test_helpers.MockBlock.Hash()
|
||||
nonCanonContractAddress = test_helpers.ContractAddr
|
||||
|
||||
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
// The non-canonical header has a child
|
||||
tx, err = indexAndPublisher.PushBlock(test_helpers.MockChild, test_helpers.MockReceipts, test_helpers.MockChild.Difficulty())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
ipld := sdtypes.IPLD{
|
||||
CID: ipld.Keccak256ToCid(ipld.RawBinary, test_helpers.CodeHash.Bytes()).String(),
|
||||
Content: test_helpers.ContractCode,
|
||||
}
|
||||
err = indexAndPublisher.PushIPLD(tx, ipld)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
err = tx.Submit(err)
|
||||
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())
|
||||
})
|
||||
|
||||
var _ = AfterSuite(func() {
|
||||
err := graphQLServer.Stop()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
shared.TearDownDB(db)
|
||||
chain.Stop()
|
||||
})
|
||||
|
||||
var _ = Describe("GraphQL", func() {
|
||||
Describe("eth_getLogs", func() {
|
||||
It("Retrieves logs that matches the provided blockHash and contract address", func() {
|
||||
logs, err := client.GetLogs(ctx, blockHash, []common.Address{contractAddress})
|
||||
logs, err := client.GetLogs(ctx, nonCanonBlockHash, []common.Address{nonCanonContractAddress})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expectedLogs := []graphql.LogResponse{
|
||||
@@ -191,7 +153,7 @@ var _ = Describe("GraphQL", func() {
|
||||
})
|
||||
|
||||
It("Retrieves logs for the failed receipt status that matches the provided blockHash and another contract address", func() {
|
||||
logs, err := client.GetLogs(ctx, blockHash, []common.Address{test_helpers.AnotherAddress2})
|
||||
logs, err := client.GetLogs(ctx, nonCanonBlockHash, []common.Address{test_helpers.AnotherAddress2})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expectedLogs := []graphql.LogResponse{
|
||||
@@ -208,7 +170,7 @@ var _ = Describe("GraphQL", func() {
|
||||
})
|
||||
|
||||
It("Retrieves logs that matches the provided blockHash and multiple contract addresses", func() {
|
||||
logs, err := client.GetLogs(ctx, blockHash, []common.Address{contractAddress, test_helpers.AnotherAddress2})
|
||||
logs, err := client.GetLogs(ctx, nonCanonBlockHash, []common.Address{nonCanonContractAddress, test_helpers.AnotherAddress2})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
expectedLogs := []graphql.LogResponse{
|
||||
@@ -232,13 +194,13 @@ var _ = Describe("GraphQL", func() {
|
||||
})
|
||||
|
||||
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)
|
||||
logs, err := client.GetLogs(ctx, nonCanonBlockHash, nil)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(6))
|
||||
})
|
||||
|
||||
It("Retrieves logs with random hash", func() {
|
||||
logs, err := client.GetLogs(ctx, randomHash, []common.Address{contractAddress})
|
||||
logs, err := client.GetLogs(ctx, randomHash, []common.Address{nonCanonContractAddress})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(len(logs)).To(Equal(0))
|
||||
})
|
||||
@@ -246,31 +208,31 @@ var _ = Describe("GraphQL", func() {
|
||||
|
||||
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)
|
||||
storageRes, err := client.GetStorageAt(ctx, blocks[2].Hash(), nonCanonContractAddress, 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)
|
||||
storageRes, err = client.GetStorageAt(ctx, blocks[3].Hash(), nonCanonContractAddress, 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)
|
||||
storageRes, err = client.GetStorageAt(ctx, blocks[4].Hash(), nonCanonContractAddress, 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)
|
||||
storageRes, err := client.GetStorageAt(ctx, blocks[0].Hash(), nonCanonContractAddress, 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)
|
||||
storageRes, err = client.GetStorageAt(ctx, blocks[1].Hash(), nonCanonContractAddress, 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())
|
||||
storageRes, err := client.GetStorageAt(ctx, blocks[3].Hash(), nonCanonContractAddress, randomHash.Hex())
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(storageRes.Value).To(Equal(common.Hash{}))
|
||||
})
|
||||
@@ -316,7 +278,7 @@ var _ = Describe("GraphQL", func() {
|
||||
|
||||
compareEthTxCID(*ethTransactionCIDResp, txCID)
|
||||
|
||||
Expect(ethTransactionCIDResp.BlockByMhKey.Data).To(Equal(graphql.Bytes(txCID.IPLD.Data).String()))
|
||||
Expect(ethTransactionCIDResp.BlockByCid.Data).To(Equal(graphql.Bytes(txCID.IPLD.Data).String()))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -337,7 +299,7 @@ func compareEthHeaderCID(ethHeaderCID graphql.EthHeaderCIDResponse, headerCID et
|
||||
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.UncleRoot).To(Equal(headerCID.UnclesHash))
|
||||
Expect(ethHeaderCID.Bloom).To(Equal(graphql.Bytes(headerCID.Bloom).String()))
|
||||
|
||||
for tIdx, txCID := range headerCID.TransactionCIDs {
|
||||
@@ -345,8 +307,8 @@ func compareEthHeaderCID(ethHeaderCID graphql.EthHeaderCIDResponse, headerCID et
|
||||
compareEthTxCID(ethTxCID, txCID)
|
||||
}
|
||||
|
||||
Expect(ethHeaderCID.BlockByMhKey.Data).To(Equal(graphql.Bytes(headerCID.IPLD.Data).String()))
|
||||
Expect(ethHeaderCID.BlockByMhKey.Key).To(Equal(headerCID.IPLD.Key))
|
||||
Expect(ethHeaderCID.BlockByCid.Data).To(Equal(graphql.Bytes(headerCID.IPLD.Data).String()))
|
||||
Expect(ethHeaderCID.BlockByCid.Key).To(Equal(headerCID.IPLD.Key))
|
||||
}
|
||||
|
||||
func compareEthTxCID(ethTxCID graphql.EthTransactionCIDResponse, txCID eth.TransactionCIDRecord) {
|
||||
|
||||
@@ -292,7 +292,7 @@ const schema string = `
|
||||
index: Int!
|
||||
src: String!
|
||||
dst: String!
|
||||
blockByMhKey: IPFSBlock!
|
||||
blockByCid: IPFSBlock!
|
||||
}
|
||||
|
||||
type EthTransactionCidsConnection {
|
||||
@@ -317,7 +317,7 @@ const schema string = `
|
||||
uncleRoot: String!
|
||||
bloom: String!
|
||||
ethTransactionCidsByHeaderId: EthTransactionCidsConnection!
|
||||
blockByMhKey: IPFSBlock!
|
||||
blockByCid: IPFSBlock!
|
||||
}
|
||||
|
||||
type EthHeaderCidsConnection {
|
||||
|
||||
@@ -28,8 +28,8 @@ import (
|
||||
"github.com/graph-gophers/graphql-go"
|
||||
"github.com/graph-gophers/graphql-go/relay"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
)
|
||||
|
||||
// Service encapsulates a GraphQL service.
|
||||
@@ -76,8 +76,8 @@ func (s *Service) Start(server *p2p.Server) error {
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
}
|
||||
extapiURL := fmt.Sprintf("http://%v/", addr)
|
||||
log.Infof("graphQL endpoint opened for url %s", extapiURL)
|
||||
extapiURL := fmt.Sprintf("http://%v", addr)
|
||||
log.Infof("GraphQL endpoint opened at %s", extapiURL)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -188,8 +188,7 @@ func WithField(field string, value interface{}) *Entry {
|
||||
|
||||
func Init() error {
|
||||
// Set the output.
|
||||
viper.BindEnv("logrus.file", "LOGRUS_FILE")
|
||||
logFile := viper.GetString("logrus.file")
|
||||
logFile := viper.GetString("log.file")
|
||||
if logFile != "" {
|
||||
file, err := os.OpenFile(logFile,
|
||||
os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0640)
|
||||
@@ -205,8 +204,7 @@ func Init() error {
|
||||
}
|
||||
|
||||
// Set the level.
|
||||
viper.BindEnv("logrus.level", "LOGRUS_LEVEL")
|
||||
lvl, err := logrus.ParseLevel(viper.GetString("logrus.level"))
|
||||
lvl, err := logrus.ParseLevel(viper.GetString("log.level"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -230,7 +228,7 @@ func Init() error {
|
||||
for next, again := frames.Next(); again; next, again = frames.Next() {
|
||||
if !strings.Contains(next.File, "sirupsen/logrus.us") &&
|
||||
!strings.HasPrefix(next.Function, "runtime.") &&
|
||||
!strings.Contains(next.File, "ipld-eth-server/pkg/logrus") {
|
||||
!strings.Contains(next.File, "ipld-eth-server/pkg/log") {
|
||||
return next.Function, fmt.Sprintf("%s:%d", next.File, next.Line)
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -17,10 +17,10 @@
|
||||
package net_test
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/net"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/net"
|
||||
)
|
||||
|
||||
var _ = Describe("API", func() {
|
||||
|
||||
@@ -20,8 +20,8 @@ import (
|
||||
"io/ioutil"
|
||||
"testing"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
. "github.com/onsi/ginkgo"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import (
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
+1
-1
@@ -20,7 +20,7 @@ import (
|
||||
"errors"
|
||||
"net/http"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/prometheus/client_golang/prometheus/promhttp"
|
||||
)
|
||||
|
||||
|
||||
+4
-4
@@ -19,12 +19,12 @@ package rpc
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/prom"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/prom"
|
||||
)
|
||||
|
||||
// StartHTTPEndpoint starts the HTTP RPC endpoint, configured with cors/vhosts/modules.
|
||||
@@ -42,8 +42,8 @@ func StartHTTPEndpoint(endpoint string, apis []rpc.API, modules []string, cors [
|
||||
if err != nil {
|
||||
utils.Fatalf("Could not start RPC api: %v", err)
|
||||
}
|
||||
extapiURL := fmt.Sprintf("http://%v/", addr)
|
||||
log.Infof("HTTP endpoint opened %s", extapiURL)
|
||||
extapiURL := fmt.Sprintf("http://%s", addr)
|
||||
log.Infof("HTTP endpoint opened at %s", extapiURL)
|
||||
|
||||
return srv, err
|
||||
}
|
||||
|
||||
+2
-2
@@ -22,11 +22,11 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/prom"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/prom"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
+3
-1
@@ -24,7 +24,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/prom"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/prom"
|
||||
)
|
||||
|
||||
// StartWSEndpoint starts a websocket endpoint.
|
||||
@@ -49,6 +50,7 @@ func StartWSEndpoint(endpoint string, apis []rpc.API, modules []string, wsOrigin
|
||||
wsServer := NewWSServer(wsOrigins, handler)
|
||||
wsServer.Handler = prom.WSMiddleware(wsServer.Handler)
|
||||
go wsServer.Serve(listener)
|
||||
log.Infof("WS endpoint opened at ws://%s", endpoint)
|
||||
|
||||
return listener, handler, err
|
||||
|
||||
|
||||
+62
-53
@@ -29,18 +29,20 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/database/sql/postgres"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/jmoiron/sqlx"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/prom"
|
||||
ethServerShared "github.com/cerc-io/ipld-eth-server/v4/pkg/shared"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/prom"
|
||||
ethServerShared "github.com/cerc-io/ipld-eth-server/v5/pkg/shared"
|
||||
)
|
||||
|
||||
// Env variables
|
||||
const (
|
||||
SERVER_WS_PATH = "SERVER_WS_PATH"
|
||||
SERVER_IPC_PATH = "SERVER_IPC_PATH"
|
||||
SERVER_HTTP_PATH = "SERVER_HTTP_PATH"
|
||||
SERVER_WS_PATH = "SERVER_WS_PATH"
|
||||
SERVER_IPC_PATH = "SERVER_IPC_PATH"
|
||||
SERVER_HTTP_PATH = "SERVER_HTTP_PATH"
|
||||
SERVER_GRAPHQL_PATH = "SERVER_GRAPHQL_PATH"
|
||||
|
||||
SERVER_MAX_IDLE_CONNECTIONS = "SERVER_MAX_IDLE_CONNECTIONS"
|
||||
SERVER_MAX_OPEN_CONNECTIONS = "SERVER_MAX_OPEN_CONNECTIONS"
|
||||
@@ -57,6 +59,25 @@ const (
|
||||
|
||||
VALIDATOR_ENABLED = "VALIDATOR_ENABLED"
|
||||
VALIDATOR_EVERY_NTH_BLOCK = "VALIDATOR_EVERY_NTH_BLOCK"
|
||||
|
||||
HTTP_TIMEOUT = "HTTP_TIMEOUT"
|
||||
|
||||
ETH_WS_PATH = "ETH_WS_PATH"
|
||||
ETH_HTTP_PATH = "ETH_HTTP_PATH"
|
||||
ETH_NODE_ID = "ETH_NODE_ID"
|
||||
ETH_CLIENT_NAME = "ETH_CLIENT_NAME"
|
||||
ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK"
|
||||
ETH_NETWORK_ID = "ETH_NETWORK_ID"
|
||||
ETH_CHAIN_ID = "ETH_CHAIN_ID"
|
||||
|
||||
DATABASE_NAME = "DATABASE_NAME"
|
||||
DATABASE_HOSTNAME = "DATABASE_HOSTNAME"
|
||||
DATABASE_PORT = "DATABASE_PORT"
|
||||
DATABASE_USER = "DATABASE_USER"
|
||||
DATABASE_PASSWORD = "DATABASE_PASSWORD"
|
||||
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS"
|
||||
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS"
|
||||
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME"
|
||||
)
|
||||
|
||||
// Config struct
|
||||
@@ -76,12 +97,6 @@ type Config struct {
|
||||
EthGraphqlEnabled bool
|
||||
EthGraphqlEndpoint string
|
||||
|
||||
IpldGraphqlEnabled bool
|
||||
IpldGraphqlEndpoint string
|
||||
IpldPostgraphileEndpoint string
|
||||
TracingHttpEndpoint string
|
||||
TracingPostgraphileEndpoint string
|
||||
|
||||
ChainConfig *params.ChainConfig
|
||||
DefaultSender *common.Address
|
||||
RPCGasCap *big.Int
|
||||
@@ -106,8 +121,12 @@ type Config struct {
|
||||
func NewConfig() (*Config, error) {
|
||||
c := new(Config)
|
||||
|
||||
viper.BindEnv("server.httpPath", SERVER_HTTP_PATH)
|
||||
viper.BindEnv("server.wsPath", SERVER_WS_PATH)
|
||||
viper.BindEnv("server.ipcPath", SERVER_IPC_PATH)
|
||||
viper.BindEnv("server.graphqlPath", SERVER_GRAPHQL_PATH)
|
||||
|
||||
viper.BindEnv("ethereum.httpPath", ETH_HTTP_PATH)
|
||||
viper.BindEnv("ethereum.defaultSender", ETH_DEFAULT_SENDER_ADDR)
|
||||
viper.BindEnv("ethereum.rpcGasCap", ETH_RPC_GAS_CAP)
|
||||
viper.BindEnv("ethereum.chainConfig", ETH_CHAIN_CONFIG)
|
||||
viper.BindEnv("ethereum.supportsStateDiff", ETH_SUPPORTS_STATEDIFF)
|
||||
@@ -115,6 +134,8 @@ func NewConfig() (*Config, error) {
|
||||
viper.BindEnv("ethereum.forwardEthCalls", ETH_FORWARD_ETH_CALLS)
|
||||
viper.BindEnv("ethereum.forwardGetStorageAt", ETH_FORWARD_GET_STORAGE_AT)
|
||||
viper.BindEnv("ethereum.proxyOnError", ETH_PROXY_ON_ERROR)
|
||||
viper.BindEnv("log.file", "LOG_FILE")
|
||||
viper.BindEnv("log.level", "LOG_LEVEL")
|
||||
|
||||
c.dbInit()
|
||||
ethHTTP := viper.GetString("ethereum.httpPath")
|
||||
@@ -132,9 +153,9 @@ func NewConfig() (*Config, error) {
|
||||
c.EthHttpEndpoint = ethHTTPEndpoint
|
||||
|
||||
// websocket server
|
||||
wsEnabled := viper.GetBool("eth.server.ws")
|
||||
wsEnabled := viper.GetBool("server.ws")
|
||||
if wsEnabled {
|
||||
wsPath := viper.GetString("eth.server.wsPath")
|
||||
wsPath := viper.GetString("server.wsPath")
|
||||
if wsPath == "" {
|
||||
wsPath = "127.0.0.1:8080"
|
||||
}
|
||||
@@ -143,9 +164,9 @@ func NewConfig() (*Config, error) {
|
||||
c.WSEnabled = wsEnabled
|
||||
|
||||
// ipc server
|
||||
ipcEnabled := viper.GetBool("eth.server.ipc")
|
||||
ipcEnabled := viper.GetBool("server.ipc")
|
||||
if ipcEnabled {
|
||||
ipcPath := viper.GetString("eth.server.ipcPath")
|
||||
ipcPath := viper.GetString("server.ipcPath")
|
||||
if ipcPath == "" {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
@@ -158,9 +179,9 @@ func NewConfig() (*Config, error) {
|
||||
c.IPCEnabled = ipcEnabled
|
||||
|
||||
// http server
|
||||
httpEnabled := viper.GetBool("eth.server.http")
|
||||
httpEnabled := viper.GetBool("server.http")
|
||||
if httpEnabled {
|
||||
httpPath := viper.GetString("eth.server.httpPath")
|
||||
httpPath := viper.GetString("server.httpPath")
|
||||
if httpPath == "" {
|
||||
httpPath = "127.0.0.1:8081"
|
||||
}
|
||||
@@ -169,9 +190,9 @@ func NewConfig() (*Config, error) {
|
||||
c.HTTPEnabled = httpEnabled
|
||||
|
||||
// eth graphql endpoint
|
||||
ethGraphqlEnabled := viper.GetBool("eth.server.graphql")
|
||||
ethGraphqlEnabled := viper.GetBool("server.graphql")
|
||||
if ethGraphqlEnabled {
|
||||
ethGraphqlPath := viper.GetString("eth.server.graphqlPath")
|
||||
ethGraphqlPath := viper.GetString("server.graphqlPath")
|
||||
if ethGraphqlPath == "" {
|
||||
ethGraphqlPath = "127.0.0.1:8082"
|
||||
}
|
||||
@@ -179,34 +200,6 @@ func NewConfig() (*Config, error) {
|
||||
}
|
||||
c.EthGraphqlEnabled = ethGraphqlEnabled
|
||||
|
||||
// ipld graphql endpoint
|
||||
ipldGraphqlEnabled := viper.GetBool("ipld.server.graphql")
|
||||
if ipldGraphqlEnabled {
|
||||
ipldGraphqlPath := viper.GetString("ipld.server.graphqlPath")
|
||||
if ipldGraphqlPath == "" {
|
||||
ipldGraphqlPath = "127.0.0.1:8083"
|
||||
}
|
||||
c.IpldGraphqlEndpoint = ipldGraphqlPath
|
||||
|
||||
ipldPostgraphilePath := viper.GetString("ipld.postgraphilePath")
|
||||
if ipldPostgraphilePath == "" {
|
||||
return nil, errors.New("ipld-postgraphile-path parameter is empty")
|
||||
}
|
||||
c.IpldPostgraphileEndpoint = ipldPostgraphilePath
|
||||
|
||||
tracingHttpEndpoint := viper.GetString("tracing.httpPath")
|
||||
tracingPostgraphilePath := viper.GetString("tracing.postgraphilePath")
|
||||
|
||||
// these two parameters either can be both empty or both set
|
||||
if (tracingHttpEndpoint == "" && tracingPostgraphilePath != "") || (tracingHttpEndpoint != "" && tracingPostgraphilePath == "") {
|
||||
return nil, errors.New("tracing.httpPath and tracing.postgraphilePath parameters either can be both empty or both set")
|
||||
}
|
||||
|
||||
c.TracingHttpEndpoint = tracingHttpEndpoint
|
||||
c.TracingPostgraphileEndpoint = tracingPostgraphilePath
|
||||
}
|
||||
c.IpldGraphqlEnabled = ipldGraphqlEnabled
|
||||
|
||||
overrideDBConnConfig(&c.DBConfig)
|
||||
serveDB, err := ethServerShared.NewDB(c.DBConfig.DbConnectionString(), c.DBConfig)
|
||||
if err != nil {
|
||||
@@ -216,11 +209,6 @@ func NewConfig() (*Config, error) {
|
||||
prom.RegisterDBCollector(c.DBConfig.DatabaseName, serveDB)
|
||||
c.DB = serveDB
|
||||
|
||||
defaultSenderStr := viper.GetString("ethereum.defaultSender")
|
||||
if defaultSenderStr != "" {
|
||||
sender := common.HexToAddress(defaultSenderStr)
|
||||
c.DefaultSender = &sender
|
||||
}
|
||||
rpcGasCapStr := viper.GetString("ethereum.rpcGasCap")
|
||||
if rpcGasCapStr != "" {
|
||||
if rpcGasCap, ok := new(big.Int).SetString(rpcGasCapStr, 10); ok {
|
||||
@@ -313,3 +301,24 @@ func (c *Config) loadValidatorConfig() {
|
||||
c.StateValidationEnabled = viper.GetBool("validator.enabled")
|
||||
c.StateValidationEveryNthBlock = viper.GetUint64("validator.everyNthBlock")
|
||||
}
|
||||
|
||||
// GetEthNodeAndClient returns eth node info and client from path url
|
||||
func getEthNodeAndClient(path string) (node.Info, *rpc.Client, error) {
|
||||
viper.BindEnv("ethereum.nodeID", ETH_NODE_ID)
|
||||
viper.BindEnv("ethereum.clientName", ETH_CLIENT_NAME)
|
||||
viper.BindEnv("ethereum.genesisBlock", ETH_GENESIS_BLOCK)
|
||||
viper.BindEnv("ethereum.networkID", ETH_NETWORK_ID)
|
||||
viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID)
|
||||
|
||||
rpcClient, err := rpc.Dial(path)
|
||||
if err != nil {
|
||||
return node.Info{}, nil, err
|
||||
}
|
||||
return node.Info{
|
||||
ID: viper.GetString("ethereum.nodeID"),
|
||||
ClientName: viper.GetString("ethereum.clientName"),
|
||||
GenesisBlock: viper.GetString("ethereum.genesisBlock"),
|
||||
NetworkID: viper.GetString("ethereum.networkID"),
|
||||
ChainID: viper.GetUint64("ethereum.chainID"),
|
||||
}, rpcClient, nil
|
||||
}
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
package serve
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
// Env variables
|
||||
const (
|
||||
HTTP_TIMEOUT = "HTTP_TIMEOUT"
|
||||
|
||||
ETH_WS_PATH = "ETH_WS_PATH"
|
||||
ETH_HTTP_PATH = "ETH_HTTP_PATH"
|
||||
ETH_NODE_ID = "ETH_NODE_ID"
|
||||
ETH_CLIENT_NAME = "ETH_CLIENT_NAME"
|
||||
ETH_GENESIS_BLOCK = "ETH_GENESIS_BLOCK"
|
||||
ETH_NETWORK_ID = "ETH_NETWORK_ID"
|
||||
ETH_CHAIN_ID = "ETH_CHAIN_ID"
|
||||
|
||||
DATABASE_NAME = "DATABASE_NAME"
|
||||
DATABASE_HOSTNAME = "DATABASE_HOSTNAME"
|
||||
DATABASE_PORT = "DATABASE_PORT"
|
||||
DATABASE_USER = "DATABASE_USER"
|
||||
DATABASE_PASSWORD = "DATABASE_PASSWORD"
|
||||
DATABASE_MAX_IDLE_CONNECTIONS = "DATABASE_MAX_IDLE_CONNECTIONS"
|
||||
DATABASE_MAX_OPEN_CONNECTIONS = "DATABASE_MAX_OPEN_CONNECTIONS"
|
||||
DATABASE_MAX_CONN_LIFETIME = "DATABASE_MAX_CONN_LIFETIME"
|
||||
)
|
||||
|
||||
// GetEthNodeAndClient returns eth node info and client from path url
|
||||
func getEthNodeAndClient(path string) (node.Info, *rpc.Client, error) {
|
||||
viper.BindEnv("ethereum.nodeID", ETH_NODE_ID)
|
||||
viper.BindEnv("ethereum.clientName", ETH_CLIENT_NAME)
|
||||
viper.BindEnv("ethereum.genesisBlock", ETH_GENESIS_BLOCK)
|
||||
viper.BindEnv("ethereum.networkID", ETH_NETWORK_ID)
|
||||
viper.BindEnv("ethereum.chainID", ETH_CHAIN_ID)
|
||||
|
||||
rpcClient, err := rpc.Dial(path)
|
||||
if err != nil {
|
||||
return node.Info{}, nil, err
|
||||
}
|
||||
return node.Info{
|
||||
ID: viper.GetString("ethereum.nodeID"),
|
||||
ClientName: viper.GetString("ethereum.clientName"),
|
||||
GenesisBlock: viper.GetString("ethereum.genesisBlock"),
|
||||
NetworkID: viper.GetString("ethereum.networkID"),
|
||||
ChainID: viper.GetUint64("ethereum.chainID"),
|
||||
}, rpcClient, nil
|
||||
}
|
||||
@@ -21,7 +21,7 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/eth/tracers"
|
||||
ethnode "github.com/ethereum/go-ethereum/node"
|
||||
@@ -29,9 +29,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/jmoiron/sqlx"
|
||||
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/debug"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/net"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/debug"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/eth"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/net"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -94,7 +94,6 @@ func NewServer(settings *Config) (Server, error) {
|
||||
sap.backend, err = eth.NewEthBackend(sap.db, ð.Config{
|
||||
ChainConfig: settings.ChainConfig,
|
||||
VMConfig: vm.Config{NoBaseFee: true},
|
||||
DefaultSender: settings.DefaultSender,
|
||||
RPCGasCap: settings.RPCGasCap,
|
||||
GroupCacheConfig: settings.GroupCache,
|
||||
})
|
||||
@@ -159,7 +158,7 @@ func (sap *Service) Serve(wg *sync.WaitGroup) {
|
||||
<-sap.QuitChan
|
||||
log.Info("quiting eth ipld server process")
|
||||
}()
|
||||
log.Info("eth ipld server process successfully spun up")
|
||||
log.Debug("eth ipld server process successfully spun up")
|
||||
}
|
||||
|
||||
// Start is used to begin the service
|
||||
@@ -174,7 +173,7 @@ func (sap *Service) Start() error {
|
||||
// Stop is used to close down the service
|
||||
// This is mostly just to satisfy the node.Service interface
|
||||
func (sap *Service) Stop() error {
|
||||
log.Infof("stopping eth ipld server")
|
||||
log.Info("stopping eth ipld server")
|
||||
sap.Lock()
|
||||
close(sap.QuitChan)
|
||||
sap.Unlock()
|
||||
|
||||
+1
-34
@@ -17,11 +17,8 @@
|
||||
package shared
|
||||
|
||||
import (
|
||||
"github.com/cerc-io/ipld-eth-server/v4/pkg/log"
|
||||
"github.com/cerc-io/ipld-eth-server/v5/pkg/log"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ipfs/go-cid"
|
||||
blockstore "github.com/ipfs/go-ipfs-blockstore"
|
||||
dshelp "github.com/ipfs/go-ipfs-ds-help"
|
||||
"github.com/jmoiron/sqlx"
|
||||
)
|
||||
|
||||
@@ -47,33 +44,3 @@ func Rollback(tx *sqlx.Tx) {
|
||||
log.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// FetchIPLDByMhKeyAndBlockNumber is used to retrieve an ipld from Postgres blockstore with the provided tx, mhkey string and blockNumber
|
||||
func FetchIPLDByMhKeyAndBlockNumber(tx *sqlx.Tx, mhKey string, blockNumber uint64) ([]byte, error) {
|
||||
pgStr := `SELECT data FROM ipld.blocks WHERE key = $1 AND block_number = $2`
|
||||
var block []byte
|
||||
return block, tx.Get(&block, pgStr, mhKey, blockNumber)
|
||||
}
|
||||
|
||||
// FetchIPLD is used to retrieve an IPLD from Postgres mhkey and blockNumber
|
||||
func FetchIPLD(db *sqlx.DB, mhKey string, blockNumber uint64) ([]byte, error) {
|
||||
pgStr := `SELECT data FROM ipld.blocks WHERE key = $1 AND block_number = $2`
|
||||
var block []byte
|
||||
return block, db.Get(&block, pgStr, mhKey, blockNumber)
|
||||
}
|
||||
|
||||
// MultihashKeyFromCID converts a cid into a blockstore-prefixed multihash db key string
|
||||
func MultihashKeyFromCID(c cid.Cid) string {
|
||||
dbKey := dshelp.MultihashToDsKey(c.Hash())
|
||||
return blockstore.BlockPrefix.String() + dbKey.String()
|
||||
}
|
||||
|
||||
// MultihashKeyFromCIDString converts a cid string into a blockstore-prefixed multihash db key string
|
||||
func MultihashKeyFromCIDString(c string) (string, error) {
|
||||
dc, err := cid.Decode(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
dbKey := dshelp.MultihashToDsKey(dc.Hash())
|
||||
return blockstore.BlockPrefix.String() + dbKey.String(), nil
|
||||
}
|
||||
|
||||
@@ -19,8 +19,6 @@ package shared
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
@@ -46,7 +44,8 @@ func IPLDsContainBytes(iplds []models.IPLDModel, b []byte) bool {
|
||||
|
||||
// SetupDB is use to setup a db for watcher tests
|
||||
func SetupDB() *sqlx.DB {
|
||||
config := getTestDBConfig()
|
||||
config, err := postgres.TestConfig.WithEnv()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
db, err := NewDB(config.DbConnectionString(), config)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
@@ -60,6 +59,8 @@ func TearDownDB(db *sqlx.DB) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM nodes`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM ipld.blocks`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.header_cids`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.uncle_cids`)
|
||||
@@ -72,12 +73,6 @@ func TearDownDB(db *sqlx.DB) {
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.storage_cids`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.state_accounts`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.access_list_elements`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM blocks`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth.log_cids`)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, err = tx.Exec(`DELETE FROM eth_meta.watched_addresses`)
|
||||
@@ -96,20 +91,10 @@ func SetupTestStateDiffIndexer(ctx context.Context, chainConfig *params.ChainCon
|
||||
ChainID: params.TestChainConfig.ChainID.Uint64(),
|
||||
}
|
||||
|
||||
_, stateDiffIndexer, err := indexer.NewStateDiffIndexer(ctx, chainConfig, testInfo, getTestDBConfig())
|
||||
dbconfig, err := postgres.TestConfig.WithEnv()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
_, stateDiffIndexer, err := indexer.NewStateDiffIndexer(ctx, chainConfig, testInfo, dbconfig)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
return stateDiffIndexer
|
||||
}
|
||||
|
||||
func getTestDBConfig() postgres.Config {
|
||||
port, _ := strconv.Atoi(os.Getenv("DATABASE_PORT"))
|
||||
return postgres.Config{
|
||||
Hostname: os.Getenv("DATABASE_HOSTNAME"),
|
||||
DatabaseName: os.Getenv("DATABASE_NAME"),
|
||||
Username: os.Getenv("DATABASE_USER"),
|
||||
Password: os.Getenv("DATABASE_PASSWORD"),
|
||||
Port: port,
|
||||
Driver: postgres.SQLX,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user