Add unit test.
This commit is contained in:
parent
7afb77c18d
commit
530aa2b62f
@ -7,4 +7,4 @@
|
|||||||
|
|
||||||
[validate]
|
[validate]
|
||||||
block-height = 1
|
block-height = 1
|
||||||
trail = 3100
|
trail = 10
|
@ -2,12 +2,17 @@ package validator
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||||
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var TestChainConfig = ¶ms.ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(100), big.NewInt(0), nil, nil, new(params.EthashConfig), nil}
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
dbParams postgres.ConnectionParams
|
dbParams postgres.ConnectionParams
|
||||||
dbConfig postgres.ConnectionConfig
|
dbConfig postgres.ConnectionConfig
|
||||||
|
@ -4,8 +4,13 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"math/big"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/clique"
|
||||||
|
"github.com/ethereum/go-ethereum/consensus/ethash"
|
||||||
"github.com/ethereum/go-ethereum/core"
|
"github.com/ethereum/go-ethereum/core"
|
||||||
"github.com/ethereum/go-ethereum/core/state"
|
"github.com/ethereum/go-ethereum/core/state"
|
||||||
"github.com/ethereum/go-ethereum/core/types"
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
@ -21,6 +26,12 @@ import (
|
|||||||
ethServerShared "github.com/vulcanize/ipld-eth-server/pkg/shared"
|
ethServerShared "github.com/vulcanize/ipld-eth-server/pkg/shared"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
big8 = big.NewInt(8)
|
||||||
|
big32 = big.NewInt(32)
|
||||||
|
testHash = common.HexToHash("0x1283a0bca5cce009bcf3e5a860eccdc202d1345f464024f2ee8ea1e1254349e7")
|
||||||
|
)
|
||||||
|
|
||||||
type service struct {
|
type service struct {
|
||||||
db *postgres.DB
|
db *postgres.DB
|
||||||
blockNum, trail uint64
|
blockNum, trail uint64
|
||||||
@ -85,7 +96,7 @@ func NewDB(connectString string, config postgres.ConnectionConfig, node node.Inf
|
|||||||
|
|
||||||
// Start is used to begin the service
|
// Start is used to begin the service
|
||||||
func (s *service) Start(ctx context.Context) (uint64, error) {
|
func (s *service) Start(ctx context.Context) (uint64, error) {
|
||||||
api, err := ethAPI(s.db)
|
api, err := ethAPI(ctx, s.db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
@ -106,6 +117,7 @@ func (s *service) Start(ctx context.Context) (uint64, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
blockStateRoot := validateBlock.Header().Root.String()
|
blockStateRoot := validateBlock.Header().Root.String()
|
||||||
|
|
||||||
dbStateRoot := stateDB.IntermediateRoot(true).String()
|
dbStateRoot := stateDB.IntermediateRoot(true).String()
|
||||||
if blockStateRoot != dbStateRoot {
|
if blockStateRoot != dbStateRoot {
|
||||||
s.logger.Errorf("failed to verify state root at block %d", idxBlockNum)
|
s.logger.Errorf("failed to verify state root at block %d", idxBlockNum)
|
||||||
@ -129,10 +141,9 @@ func (s *service) Start(ctx context.Context) (uint64, error) {
|
|||||||
return idxBlockNum, nil
|
return idxBlockNum, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ethAPI(db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
func ethAPI(ctx context.Context, db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
||||||
// TODO: decide network for chainConfig.
|
// TODO: decide network for custom chainConfig.
|
||||||
backend, err := NewEthBackend(db, &ipldEth.Config{
|
backend, err := NewEthBackend(db, &ipldEth.Config{
|
||||||
ChainConfig: params.RinkebyChainConfig,
|
|
||||||
GroupCacheConfig: ðServerShared.GroupCacheConfig{
|
GroupCacheConfig: ðServerShared.GroupCacheConfig{
|
||||||
StateDB: ethServerShared.GroupConfig{
|
StateDB: ethServerShared.GroupConfig{
|
||||||
Name: "vulcanize_validator",
|
Name: "vulcanize_validator",
|
||||||
@ -144,6 +155,16 @@ func ethAPI(db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var genesisBlock *types.Block
|
||||||
|
if backend.Config.ChainConfig == nil {
|
||||||
|
genesisBlock, err = backend.BlockByNumber(ctx, rpc.BlockNumber(0))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
backend.Config.ChainConfig = setChainConfig(genesisBlock.Hash())
|
||||||
|
}
|
||||||
|
|
||||||
return ipldEth.NewPublicEthAPI(backend, nil, false, false, false)
|
return ipldEth.NewPublicEthAPI(backend, nil, false, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +198,7 @@ func applyTransaction(block *types.Block, backend *ipldEth.Backend) (*state.Stat
|
|||||||
// Assemble the transaction call message and return if the requested offset
|
// Assemble the transaction call message and return if the requested offset
|
||||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||||
txContext := core.NewEVMTxContext(msg)
|
txContext := core.NewEVMTxContext(msg)
|
||||||
ctx := core.NewEVMBlockContext(block.Header(), backend, nil)
|
ctx := core.NewEVMBlockContext(block.Header(), backend, getAuthor(backend, block.Header()))
|
||||||
|
|
||||||
// Not yet the searched for transaction, execute on top of the current state
|
// Not yet the searched for transaction, execute on top of the current state
|
||||||
newEVM := vm.NewEVM(ctx, txContext, stateDB, backend.Config.ChainConfig, vm.Config{})
|
newEVM := vm.NewEVM(ctx, txContext, stateDB, backend.Config.ChainConfig, vm.Config{})
|
||||||
@ -187,5 +208,79 @@ func applyTransaction(block *types.Block, backend *ipldEth.Backend) (*state.Stat
|
|||||||
return nil, fmt.Errorf("transaction %#x failed: %w", tx.Hash(), err)
|
return nil, fmt.Errorf("transaction %#x failed: %w", tx.Hash(), err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if backend.Config.ChainConfig.Ethash != nil {
|
||||||
|
accumulateRewards(backend.Config.ChainConfig, stateDB, block.Header(), block.Uncles())
|
||||||
|
}
|
||||||
|
|
||||||
return stateDB, nil
|
return stateDB, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// accumulateRewards credits the coinbase of the given block with the mining
|
||||||
|
// reward. The total reward consists of the static block reward and rewards for
|
||||||
|
// included uncles. The coinbase of each uncle block is also rewarded.
|
||||||
|
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
|
||||||
|
// Select the correct block reward based on chain progression
|
||||||
|
blockReward := ethash.FrontierBlockReward
|
||||||
|
if config.IsByzantium(header.Number) {
|
||||||
|
blockReward = ethash.ByzantiumBlockReward
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.IsConstantinople(header.Number) {
|
||||||
|
blockReward = ethash.ConstantinopleBlockReward
|
||||||
|
}
|
||||||
|
|
||||||
|
// Accumulate the rewards for the miner and any included uncles
|
||||||
|
reward := new(big.Int).Set(blockReward)
|
||||||
|
r := new(big.Int)
|
||||||
|
for _, uncle := range uncles {
|
||||||
|
r.Add(uncle.Number, big8)
|
||||||
|
r.Sub(r, header.Number)
|
||||||
|
r.Mul(r, blockReward)
|
||||||
|
r.Div(r, big8)
|
||||||
|
state.AddBalance(uncle.Coinbase, r)
|
||||||
|
|
||||||
|
r.Div(blockReward, big32)
|
||||||
|
reward.Add(reward, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
state.AddBalance(header.Coinbase, reward)
|
||||||
|
}
|
||||||
|
|
||||||
|
func setChainConfig(ghash common.Hash) *params.ChainConfig {
|
||||||
|
switch {
|
||||||
|
case ghash == params.MainnetGenesisHash:
|
||||||
|
return params.MainnetChainConfig
|
||||||
|
case ghash == params.RopstenGenesisHash:
|
||||||
|
return params.RopstenChainConfig
|
||||||
|
case ghash == params.SepoliaGenesisHash:
|
||||||
|
return params.SepoliaChainConfig
|
||||||
|
case ghash == params.RinkebyGenesisHash:
|
||||||
|
return params.RinkebyChainConfig
|
||||||
|
case ghash == params.GoerliGenesisHash:
|
||||||
|
return params.GoerliChainConfig
|
||||||
|
case ghash == testHash:
|
||||||
|
return TestChainConfig
|
||||||
|
default:
|
||||||
|
return params.AllEthashProtocolChanges
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAuthor(b *ipldEth.Backend, header *types.Header) *common.Address {
|
||||||
|
author, err := getEngine(b).Author(header)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &author
|
||||||
|
}
|
||||||
|
|
||||||
|
func getEngine(b *ipldEth.Backend) consensus.Engine {
|
||||||
|
// TODO: add logic for other engines
|
||||||
|
if b.Config.ChainConfig.Clique != nil {
|
||||||
|
engine := clique.New(b.Config.ChainConfig.Clique, nil)
|
||||||
|
return engine
|
||||||
|
}
|
||||||
|
|
||||||
|
return ethash.NewFaker()
|
||||||
|
}
|
||||||
|
19
test/validator_suite_test.go
Normal file
19
test/validator_suite_test.go
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io/ioutil"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestETHSuite(t *testing.T) {
|
||||||
|
RegisterFailHandler(Fail)
|
||||||
|
RunSpecs(t, "eth ipld validator eth suite test")
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = BeforeSuite(func() {
|
||||||
|
logrus.SetOutput(ioutil.Discard)
|
||||||
|
})
|
143
test/validator_test.go
Normal file
143
test/validator_test.go
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"math/big"
|
||||||
|
|
||||||
|
"github.com/ethereum/go-ethereum/common"
|
||||||
|
"github.com/ethereum/go-ethereum/core"
|
||||||
|
"github.com/ethereum/go-ethereum/core/types"
|
||||||
|
"github.com/ethereum/go-ethereum/params"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/node"
|
||||||
|
"github.com/ethereum/go-ethereum/statediff/indexer/postgres"
|
||||||
|
sdtypes "github.com/ethereum/go-ethereum/statediff/types"
|
||||||
|
. "github.com/onsi/ginkgo"
|
||||||
|
. "github.com/onsi/gomega"
|
||||||
|
"github.com/vulcanize/ipld-eth-server/pkg/eth"
|
||||||
|
|
||||||
|
"github.com/vulcanize/ipld-eth-server/pkg/eth/test_helpers"
|
||||||
|
|
||||||
|
"github.com/Vulcanize/ipld-eth-db-validator/pkg/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
chainLength = 20
|
||||||
|
blockHeight = 1
|
||||||
|
trail = 15
|
||||||
|
)
|
||||||
|
|
||||||
|
// SetupDB is use to setup a db for watcher tests
|
||||||
|
func setupDB() (*postgres.DB, error) {
|
||||||
|
uri := postgres.DbConnectionString(postgres.ConnectionParams{
|
||||||
|
User: "vdbm",
|
||||||
|
Password: "password",
|
||||||
|
Hostname: "localhost",
|
||||||
|
Name: "vulcanize_public",
|
||||||
|
Port: 5432,
|
||||||
|
})
|
||||||
|
return postgres.NewDB(uri, postgres.ConnectionConfig{}, node.Info{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ = Describe("eth state reading tests", func() {
|
||||||
|
var (
|
||||||
|
blocks []*types.Block
|
||||||
|
receipts []types.Receipts
|
||||||
|
chain *core.BlockChain
|
||||||
|
db *postgres.DB
|
||||||
|
chainConfig = params.TestChainConfig
|
||||||
|
mockTD = big.NewInt(1337)
|
||||||
|
err error
|
||||||
|
)
|
||||||
|
|
||||||
|
It("test init", func() {
|
||||||
|
db, err = setupDB()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
transformer, err := indexer.NewStateDiffIndexer(chainConfig, db)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
// 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,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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]
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
err = tx.Close(err)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert some non-canonical data into the database so that we test our ability to discern canonicity
|
||||||
|
indexAndPublisher, err := indexer.NewStateDiffIndexer(chainConfig, db)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
tx, err := indexAndPublisher.PushBlock(test_helpers.MockBlock, test_helpers.MockReceipts, test_helpers.MockBlock.Difficulty())
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
err = tx.Close(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())
|
||||||
|
|
||||||
|
hash := sdtypes.CodeAndCodeHash{
|
||||||
|
Hash: test_helpers.CodeHash,
|
||||||
|
Code: test_helpers.ContractCode,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = indexAndPublisher.PushCodeAndCodeHash(tx, hash)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
err = tx.Close(err)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
|
||||||
|
defer It("test teardown", func() {
|
||||||
|
eth.TearDownDB(db)
|
||||||
|
chain.Stop()
|
||||||
|
})
|
||||||
|
|
||||||
|
Describe("state_validation", func() {
|
||||||
|
It("Validator", func() {
|
||||||
|
srvc := validator.NewService(db, blockHeight, trail)
|
||||||
|
|
||||||
|
_, err := srvc.Start(context.Background())
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
Loading…
Reference in New Issue
Block a user