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:
+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"`
|
||||
|
||||
Reference in New Issue
Block a user