use test utils from plugeth-statediff
This commit is contained in:
parent
bb6dcea25e
commit
e91f2df0f9
@ -6,12 +6,12 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
helpers "github.com/cerc-io/plugeth-statediff/test_helpers"
|
||||||
"github.com/onsi/gomega"
|
"github.com/onsi/gomega"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/cmd" // this registers env vars with viper
|
"github.com/cerc-io/ipld-eth-db-validator/v5/cmd" // this registers env vars with viper
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/integration"
|
"github.com/cerc-io/ipld-eth-db-validator/v5/integration"
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/helpers"
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package chaingen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/accounts/abi"
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ContractSpec struct {
|
|
||||||
DeploymentCode []byte
|
|
||||||
ABI abi.ABI
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseContract(abiStr, binStr string) (*ContractSpec, error) {
|
|
||||||
parsedABI, err := abi.JSON(strings.NewReader(abiStr))
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
data := common.Hex2Bytes(binStr)
|
|
||||||
return &ContractSpec{data, parsedABI}, nil
|
|
||||||
}
|
|
@ -1,162 +0,0 @@
|
|||||||
package chaingen
|
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/ecdsa"
|
|
||||||
"errors"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
|
||||||
"github.com/ethereum/go-ethereum/consensus/ethash"
|
|
||||||
"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/ethdb"
|
|
||||||
"github.com/ethereum/go-ethereum/params"
|
|
||||||
)
|
|
||||||
|
|
||||||
type GenContext struct {
|
|
||||||
ChainConfig *params.ChainConfig
|
|
||||||
GenFuncs []func(int, *core.BlockGen)
|
|
||||||
DB ethdb.Database
|
|
||||||
|
|
||||||
Keys map[common.Address]*ecdsa.PrivateKey
|
|
||||||
Contracts map[string]*ContractSpec
|
|
||||||
Genesis *types.Block
|
|
||||||
|
|
||||||
block *core.BlockGen // cache the current block for my methods' use
|
|
||||||
deployed map[common.Address]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewGenContext(chainConfig *params.ChainConfig, db ethdb.Database) *GenContext {
|
|
||||||
return &GenContext{
|
|
||||||
ChainConfig: chainConfig,
|
|
||||||
DB: db,
|
|
||||||
Keys: make(map[common.Address]*ecdsa.PrivateKey),
|
|
||||||
Contracts: make(map[string]*ContractSpec),
|
|
||||||
|
|
||||||
deployed: make(map[common.Address]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) AddFunction(fn func(int, *core.BlockGen)) {
|
|
||||||
gen.GenFuncs = append(gen.GenFuncs, fn)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) AddOwnedAccount(key *ecdsa.PrivateKey) common.Address {
|
|
||||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
|
||||||
gen.Keys[addr] = key
|
|
||||||
return addr
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) AddContract(name string, spec *ContractSpec) {
|
|
||||||
gen.Contracts[name] = spec
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) generate(i int, block *core.BlockGen) {
|
|
||||||
gen.block = block
|
|
||||||
for _, fn := range gen.GenFuncs {
|
|
||||||
fn(i, block)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// MakeChain creates a chain of n blocks starting at and including the genesis block.
|
|
||||||
// the returned hash chain is ordered head->parent.
|
|
||||||
func (gen *GenContext) MakeChain(n int) ([]*types.Block, []types.Receipts, *core.BlockChain) {
|
|
||||||
blocks, receipts := core.GenerateChain(
|
|
||||||
gen.ChainConfig, gen.Genesis, ethash.NewFaker(), gen.DB, n, gen.generate,
|
|
||||||
)
|
|
||||||
chain, err := core.NewBlockChain(gen.DB, nil, nil, nil, ethash.NewFaker(), vm.Config{}, nil, nil)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return append([]*types.Block{gen.Genesis}, blocks...), receipts, chain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) CreateSendTx(from common.Address, to common.Address, amount *big.Int) (*types.Transaction, error) {
|
|
||||||
return gen.createTx(from, &to, amount, params.TxGas, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) CreateContractTx(from common.Address, contractName string) (*types.Transaction, error) {
|
|
||||||
contract := gen.Contracts[contractName]
|
|
||||||
if contract == nil {
|
|
||||||
return nil, errors.New("No contract with name " + contractName)
|
|
||||||
}
|
|
||||||
return gen.createTx(from, nil, big.NewInt(0), 1000000, contract.DeploymentCode)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) CreateCallTx(from common.Address, to common.Address, methodName string, args ...interface{}) (*types.Transaction, error) {
|
|
||||||
contractName, ok := gen.deployed[to]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("No contract deployed at address " + to.String())
|
|
||||||
}
|
|
||||||
contract := gen.Contracts[contractName]
|
|
||||||
if contract == nil {
|
|
||||||
return nil, errors.New("No contract with name " + contractName)
|
|
||||||
}
|
|
||||||
|
|
||||||
packed, err := contract.ABI.Pack(methodName, args...)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return gen.createTx(from, &to, big.NewInt(0), 100000, packed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) DeployContract(from common.Address, contractName string) (common.Address, error) {
|
|
||||||
tx, err := gen.CreateContractTx(from, contractName)
|
|
||||||
if err != nil {
|
|
||||||
return common.Address{}, err
|
|
||||||
}
|
|
||||||
addr := crypto.CreateAddress(from, gen.block.TxNonce(from))
|
|
||||||
gen.deployed[addr] = contractName
|
|
||||||
gen.block.AddTx(tx)
|
|
||||||
return addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) createTx(from common.Address, to *common.Address, amount *big.Int, gasLimit uint64, data []byte) (*types.Transaction, error) {
|
|
||||||
signer := types.MakeSigner(gen.ChainConfig, gen.block.Number())
|
|
||||||
nonce := gen.block.TxNonce(from)
|
|
||||||
priv, ok := gen.Keys[from]
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("No private key for sender address" + from.String())
|
|
||||||
}
|
|
||||||
|
|
||||||
var tx *types.Transaction
|
|
||||||
if gen.ChainConfig.IsLondon(gen.block.Number()) {
|
|
||||||
tx = types.NewTx(&types.DynamicFeeTx{
|
|
||||||
ChainID: gen.ChainConfig.ChainID,
|
|
||||||
Nonce: nonce,
|
|
||||||
To: to,
|
|
||||||
Gas: gasLimit,
|
|
||||||
GasTipCap: big.NewInt(50),
|
|
||||||
GasFeeCap: big.NewInt(1000000000),
|
|
||||||
Value: amount,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
tx = types.NewTx(&types.LegacyTx{
|
|
||||||
Nonce: nonce,
|
|
||||||
To: to,
|
|
||||||
Value: amount,
|
|
||||||
Gas: gasLimit,
|
|
||||||
Data: data,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return types.SignTx(tx, signer, priv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (gen *GenContext) CreateLondonTx(block *core.BlockGen, addr *common.Address, key *ecdsa.PrivateKey) (*types.Transaction, error) {
|
|
||||||
londonTrx := types.NewTx(&types.DynamicFeeTx{
|
|
||||||
ChainID: gen.ChainConfig.ChainID,
|
|
||||||
Nonce: block.TxNonce(*addr),
|
|
||||||
GasTipCap: big.NewInt(50),
|
|
||||||
GasFeeCap: big.NewInt(1000000000),
|
|
||||||
Gas: 21000,
|
|
||||||
To: addr,
|
|
||||||
Value: big.NewInt(1000),
|
|
||||||
Data: []byte{},
|
|
||||||
})
|
|
||||||
|
|
||||||
transactionSigner := types.MakeSigner(gen.ChainConfig, block.Number())
|
|
||||||
return types.SignTx(londonTrx, transactionSigner, key)
|
|
||||||
}
|
|
@ -4,6 +4,7 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
|
|
||||||
"github.com/cerc-io/plugeth-statediff/test_helpers"
|
"github.com/cerc-io/plugeth-statediff/test_helpers"
|
||||||
|
"github.com/cerc-io/plugeth-statediff/test_helpers/chaingen"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/ethdb"
|
"github.com/ethereum/go-ethereum/ethdb"
|
||||||
@ -16,20 +17,20 @@ var (
|
|||||||
bank, acct1, acct2 common.Address
|
bank, acct1, acct2 common.Address
|
||||||
contractAddr common.Address
|
contractAddr common.Address
|
||||||
contractDataRoot string
|
contractDataRoot string
|
||||||
defaultContract *ContractSpec
|
defaultContract *chaingen.ContractSpec
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
var err error
|
var err error
|
||||||
defaultContract, err = ParseContract(testdata.TestContractABI, testdata.TestContractCode)
|
defaultContract, err = chaingen.ParseContract(testdata.TestContractABI, testdata.TestContractCode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A GenContext which exactly replicates the chain generator used in existing tests
|
// A GenContext which exactly replicates the chain generator used in existing tests
|
||||||
func DefaultGenContext(chainConfig *params.ChainConfig, db ethdb.Database) *GenContext {
|
func DefaultGenContext(chainConfig *params.ChainConfig, db ethdb.Database) *chaingen.GenContext {
|
||||||
gen := NewGenContext(chainConfig, db)
|
gen := chaingen.NewGenContext(chainConfig, db)
|
||||||
bank = gen.AddOwnedAccount(test_helpers.TestBankKey)
|
bank = gen.AddOwnedAccount(test_helpers.TestBankKey)
|
||||||
acct1 = gen.AddOwnedAccount(test_helpers.Account1Key)
|
acct1 = gen.AddOwnedAccount(test_helpers.Account1Key)
|
||||||
acct2 = gen.AddOwnedAccount(test_helpers.Account2Key)
|
acct2 = gen.AddOwnedAccount(test_helpers.Account2Key)
|
||||||
@ -46,7 +47,7 @@ func DefaultGenContext(chainConfig *params.ChainConfig, db ethdb.Database) *GenC
|
|||||||
return gen
|
return gen
|
||||||
}
|
}
|
||||||
|
|
||||||
func defaultChainGen(gen *GenContext, i int, block *core.BlockGen) error {
|
func defaultChainGen(gen *chaingen.GenContext, i int, block *core.BlockGen) error {
|
||||||
switch i {
|
switch i {
|
||||||
case 0:
|
case 0:
|
||||||
// In block 1, the test bank sends account #1 some ether.
|
// In block 1, the test bank sends account #1 some ether.
|
||||||
|
@ -1,46 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/cerc-io/plugeth-statediff/indexer/database/sql/postgres"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
)
|
|
||||||
|
|
||||||
var TestDBConfig, _ = postgres.TestConfig.WithEnv()
|
|
||||||
|
|
||||||
// SetupDB is use to setup a db for watcher tests
|
|
||||||
func SetupDB() *sqlx.DB {
|
|
||||||
db, err := postgres.ConnectSQLX(context.Background(), TestDBConfig)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return db
|
|
||||||
}
|
|
||||||
|
|
||||||
// TearDownDB is used to tear down the watcher dbs after tests
|
|
||||||
func TearDownDB(db *sqlx.DB) error {
|
|
||||||
tx, err := db.Beginx()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
statements := []string{
|
|
||||||
`TRUNCATE nodes`,
|
|
||||||
`TRUNCATE ipld.blocks`,
|
|
||||||
`TRUNCATE eth.header_cids`,
|
|
||||||
`TRUNCATE eth.uncle_cids`,
|
|
||||||
`TRUNCATE eth.transaction_cids`,
|
|
||||||
`TRUNCATE eth.receipt_cids`,
|
|
||||||
`TRUNCATE eth.state_cids`,
|
|
||||||
`TRUNCATE eth.storage_cids`,
|
|
||||||
`TRUNCATE eth.log_cids`,
|
|
||||||
`TRUNCATE eth_meta.watched_addresses`,
|
|
||||||
}
|
|
||||||
for _, stm := range statements {
|
|
||||||
if _, err = tx.Exec(stm); err != nil {
|
|
||||||
return fmt.Errorf("error executing `%s`: %w", stm, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return tx.Commit()
|
|
||||||
}
|
|
@ -1,100 +0,0 @@
|
|||||||
package helpers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
|
|
||||||
"github.com/cerc-io/plugeth-statediff"
|
|
||||||
"github.com/cerc-io/plugeth-statediff/adapt"
|
|
||||||
"github.com/cerc-io/plugeth-statediff/indexer"
|
|
||||||
"github.com/cerc-io/plugeth-statediff/indexer/interfaces"
|
|
||||||
"github.com/cerc-io/plugeth-statediff/indexer/node"
|
|
||||||
"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"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestStateDiffIndexer(ctx context.Context, chainConfig *params.ChainConfig, genHash common.Hash) (interfaces.StateDiffIndexer, error) {
|
|
||||||
testInfo := node.Info{
|
|
||||||
GenesisBlock: genHash.String(),
|
|
||||||
NetworkID: "1",
|
|
||||||
ID: "1",
|
|
||||||
ClientName: "geth",
|
|
||||||
ChainID: chainConfig.ChainID.Uint64(),
|
|
||||||
}
|
|
||||||
_, indexer, err := indexer.NewStateDiffIndexer(ctx, chainConfig, testInfo, TestDBConfig, true)
|
|
||||||
return indexer, err
|
|
||||||
}
|
|
||||||
|
|
||||||
type IndexChainParams struct {
|
|
||||||
Blocks []*types.Block
|
|
||||||
Receipts []types.Receipts
|
|
||||||
StateCache state.Database
|
|
||||||
|
|
||||||
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(indexer interfaces.StateDiffIndexer, params IndexChainParams) error {
|
|
||||||
builder := statediff.NewBuilder(adapt.GethStateView(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 fmt.Errorf("failed to build diff (block %d): %w", block.Number(), err)
|
|
||||||
}
|
|
||||||
tx, err := indexer.PushBlock(block, rcts, params.TotalDifficulty)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to index block (block %d): %w", block.Number(), err)
|
|
||||||
}
|
|
||||||
defer tx.RollbackOnFailure(err)
|
|
||||||
|
|
||||||
if !params.SkipStateNodes {
|
|
||||||
for _, node := range diff.Nodes {
|
|
||||||
if err = indexer.PushStateNode(tx, node, block.Hash().String()); err != nil {
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to index state node: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !params.SkipIPLDs {
|
|
||||||
for _, ipld := range diff.IPLDs {
|
|
||||||
if err := indexer.PushIPLD(tx, ipld); err != nil {
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to index IPLD: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err = tx.Submit(); err != nil {
|
|
||||||
return fmt.Errorf("failed to commit diff: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -6,6 +6,8 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
helpers "github.com/cerc-io/plugeth-statediff/test_helpers"
|
||||||
|
"github.com/cerc-io/plugeth-statediff/test_helpers/chaingen"
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
@ -14,8 +16,6 @@ import (
|
|||||||
. "github.com/onsi/ginkgo/v2"
|
. "github.com/onsi/ginkgo/v2"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/chaingen"
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/helpers"
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -5,19 +5,17 @@ import (
|
|||||||
"math/big"
|
"math/big"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
|
|
||||||
"github.com/cerc-io/plugeth-statediff/indexer/ipld"
|
"github.com/cerc-io/plugeth-statediff/indexer/ipld"
|
||||||
|
helpers "github.com/cerc-io/plugeth-statediff/test_helpers"
|
||||||
|
"github.com/cerc-io/plugeth-statediff/test_helpers/chaingen"
|
||||||
sdtypes "github.com/cerc-io/plugeth-statediff/types"
|
sdtypes "github.com/cerc-io/plugeth-statediff/types"
|
||||||
"github.com/ethereum/go-ethereum/core/rawdb"
|
"github.com/ethereum/go-ethereum/core/rawdb"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/rpc"
|
"github.com/ethereum/go-ethereum/rpc"
|
||||||
|
"github.com/jmoiron/sqlx"
|
||||||
// import server helpers for non-canonical chain data
|
// import server helpers for non-canonical chain data
|
||||||
server_mocks "github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
server_mocks "github.com/cerc-io/ipld-eth-server/v5/pkg/eth/test_helpers"
|
||||||
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/chaingen"
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/internal/helpers"
|
|
||||||
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
"github.com/cerc-io/ipld-eth-db-validator/v5/pkg/validator"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user