Add unit test.
This commit is contained in:
parent
7afb77c18d
commit
530aa2b62f
@ -7,4 +7,4 @@
|
||||
|
||||
[validate]
|
||||
block-height = 1
|
||||
trail = 3100
|
||||
trail = 10
|
@ -2,12 +2,17 @@ package validator
|
||||
|
||||
import (
|
||||
"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/postgres"
|
||||
"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 {
|
||||
dbParams postgres.ConnectionParams
|
||||
dbConfig postgres.ConnectionConfig
|
||||
|
@ -4,8 +4,13 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"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/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
@ -21,6 +26,12 @@ import (
|
||||
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 {
|
||||
db *postgres.DB
|
||||
blockNum, trail uint64
|
||||
@ -85,7 +96,7 @@ func NewDB(connectString string, config postgres.ConnectionConfig, node node.Inf
|
||||
|
||||
// Start is used to begin the service
|
||||
func (s *service) Start(ctx context.Context) (uint64, error) {
|
||||
api, err := ethAPI(s.db)
|
||||
api, err := ethAPI(ctx, s.db)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
@ -106,6 +117,7 @@ func (s *service) Start(ctx context.Context) (uint64, error) {
|
||||
}
|
||||
|
||||
blockStateRoot := validateBlock.Header().Root.String()
|
||||
|
||||
dbStateRoot := stateDB.IntermediateRoot(true).String()
|
||||
if blockStateRoot != dbStateRoot {
|
||||
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
|
||||
}
|
||||
|
||||
func ethAPI(db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
||||
// TODO: decide network for chainConfig.
|
||||
func ethAPI(ctx context.Context, db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
||||
// TODO: decide network for custom chainConfig.
|
||||
backend, err := NewEthBackend(db, &ipldEth.Config{
|
||||
ChainConfig: params.RinkebyChainConfig,
|
||||
GroupCacheConfig: ðServerShared.GroupCacheConfig{
|
||||
StateDB: ethServerShared.GroupConfig{
|
||||
Name: "vulcanize_validator",
|
||||
@ -144,6 +155,16 @@ func ethAPI(db *postgres.DB) (*ipldEth.PublicEthAPI, error) {
|
||||
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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
msg, _ := tx.AsMessage(signer, block.BaseFee())
|
||||
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
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
if backend.Config.ChainConfig.Ethash != nil {
|
||||
accumulateRewards(backend.Config.ChainConfig, stateDB, block.Header(), block.Uncles())
|
||||
}
|
||||
|
||||
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