beacon: Improve Test Coverage for Beacon Chain Package (#402)

This commit is contained in:
terence tsao
2018-08-14 21:49:59 -07:00
committed by Raul Jordan
parent f3fad7339d
commit 06ca8e758f
10 changed files with 438 additions and 113 deletions

View File

@@ -36,8 +36,10 @@ go_test(
"//beacon-chain/utils:go_default_library",
"//proto/beacon/p2p/v1:go_default_library",
"//shared/database:go_default_library",
"@com_github_ethereum_go_ethereum//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//event:go_default_library",
"@com_github_gogo_protobuf//proto:go_default_library",
"@com_github_sirupsen_logrus//hooks/test:go_default_library",
"@io_bazel_rules_go//proto/wkt:timestamp_go_proto",

View File

@@ -306,7 +306,7 @@ func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error)
// getAttestersTotalDeposit returns the total deposit combined by attesters.
// TODO: Consider slashing condition.
func (b *BeaconChain) getAttestersTotalDeposit() (uint64, error) {
func (b *BeaconChain) getAttestersTotalDeposit() uint64 {
var numOfBits int
for _, attestation := range b.ActiveState().PendingAttestations() {
for _, byte := range attestation.AttesterBitfield {
@@ -314,7 +314,7 @@ func (b *BeaconChain) getAttestersTotalDeposit() (uint64, error) {
}
}
// Assume there's no slashing condition, the following logic will change later phase.
return uint64(numOfBits) * params.DefaultBalance, nil
return uint64(numOfBits) * params.DefaultBalance
}
// calculateRewardsFFG adjusts validators balances by applying rewards or penalties
@@ -324,10 +324,7 @@ func (b *BeaconChain) calculateRewardsFFG(block *types.Block) error {
defer b.lock.Unlock()
validators := b.CrystallizedState().Validators()
activeValidators := b.activeValidatorIndices()
attesterDeposits, err := b.getAttestersTotalDeposit()
if err != nil {
return err
}
attesterDeposits := b.getAttestersTotalDeposit()
totalDeposit := b.state.CrystallizedState.TotalDeposits()
attesterFactor := attesterDeposits * 3

View File

@@ -189,15 +189,42 @@ func TestSetCrystallizedState(t *testing.T) {
func TestGetAttestersProposer(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()
// Create validators more than params.MaxValidators, this should fail.
var validators []*pb.ValidatorRecord
for i := 0; i < params.MaxValidators+1; i++ {
validator := &pb.ValidatorRecord{StartDynasty: 1, EndDynasty: 100}
validators = append(validators, validator)
}
_, crystallized := types.NewGenesisStates()
crystallized.SetValidators(validators)
crystallized.IncrementCurrentDynasty()
beaconChain.SetCrystallizedState(crystallized)
_, _, err := beaconChain.getAttestersProposer(common.Hash{'A'})
if err == nil {
t.Errorf("GetAttestersProposer should have failed")
}
// computeNewActiveState should fail the same.
_, err = beaconChain.computeNewActiveState(common.BytesToHash([]byte{'A'}))
if err == nil {
t.Errorf("computeNewActiveState should have failed")
}
// validatorsByHeightShard should fail the same.
_, err = beaconChain.validatorsByHeightShard()
if err == nil {
t.Errorf("validatorsByHeightShard should have failed")
}
// Create 1000 validators in ActiveValidators.
validators = validators[:0]
for i := 0; i < 1000; i++ {
validator := &pb.ValidatorRecord{StartDynasty: 1, EndDynasty: 100}
validators = append(validators, validator)
}
_, crystallized := types.NewGenesisStates()
_, crystallized = types.NewGenesisStates()
crystallized.SetValidators(validators)
crystallized.IncrementCurrentDynasty()
beaconChain.SetCrystallizedState(crystallized)
@@ -220,6 +247,14 @@ func TestGetAttestersProposer(t *testing.T) {
if !reflect.DeepEqual(attesters, validatorList[:len(attesters)]) {
t.Errorf("Get attesters failed, expected: %v got: %v", validatorList[:len(attesters)], attesters)
}
indices, err := beaconChain.validatorsByHeightShard()
if err != nil {
t.Errorf("validatorsByHeightShard failed with %v:", err)
}
if len(indices) != 8192 {
t.Errorf("incorret length for validator indices. Want: 8192. Got: %v", len(indices))
}
}
func TestCanProcessBlock(t *testing.T) {
@@ -228,7 +263,7 @@ func TestCanProcessBlock(t *testing.T) {
clock = &fakeClock{}
// Initialize a parent block
// Initialize a parent block.
parentBlock := NewBlock(t, &pb.BeaconBlock{
SlotNumber: 1,
})
@@ -248,7 +283,7 @@ func TestCanProcessBlock(t *testing.T) {
t.Error("Using a faulty fetcher should throw an error, received nil")
}
// Initialize initial state
// Initialize initial state.
activeState := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'A'}}})
beaconChain.state.ActiveState = activeState
activeHash, err := activeState.Hash()
@@ -270,7 +305,7 @@ func TestCanProcessBlock(t *testing.T) {
ParentHash: parentHash[:],
})
// A properly initialize block should not fail
// A properly initialize block should not fail.
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
if err != nil {
t.Fatalf("CanProcessBlocks failed: %v", err)
@@ -279,7 +314,7 @@ func TestCanProcessBlock(t *testing.T) {
t.Error("Should be able to process block, could not")
}
// Test timestamp validity condition
// Test timestamp validity condition.
block = NewBlock(t, &pb.BeaconBlock{
SlotNumber: 1000000,
ActiveStateHash: activeHash[:],
@@ -299,7 +334,7 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()
// Test negative scenario where active state hash is different than node's compute
// Test negative scenario where active state hash is different than node's compute.
parentBlock := NewBlock(t, nil)
parentHash, err := parentBlock.Hash()
if err != nil {
@@ -309,7 +344,7 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
t.Fatalf("Failed to put parent block on db: %v", err)
}
// Initialize state
// Initialize state.
active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'A'}}})
activeStateHash, err := active.Hash()
if err != nil {
@@ -329,7 +364,7 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
ParentHash: parentHash[:],
})
// Test negative scenario where active state hash is different than node's compute
// Test negative scenario where active state hash is different than node's compute.
beaconChain.state.ActiveState = types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'B'}}})
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
@@ -340,7 +375,7 @@ func TestProcessBlockWithBadHashes(t *testing.T) {
t.Error("CanProcessBlocks should have returned false")
}
// Test negative scenario where crystallized state hash is different than node's compute
// Test negative scenario where crystallized state hash is different than node's compute.
beaconChain.state.CrystallizedState = types.NewCrystallizedState(&pb.CrystallizedState{LastStateRecalc: 9999})
canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
@@ -371,7 +406,7 @@ func TestProcessBlockWithInvalidParent(t *testing.T) {
}
beaconChain.state.CrystallizedState = crystallized
// Test that block processing is invalid without a parent hash
// Test that block processing is invalid without a parent hash.
block := NewBlock(t, &pb.BeaconBlock{
SlotNumber: 2,
ActiveStateHash: activeStateHash[:],
@@ -430,11 +465,11 @@ func TestRotateValidatorSet(t *testing.T) {
defer db.Close()
validators := []*pb.ValidatorRecord{
{Balance: 10, StartDynasty: 0, EndDynasty: params.DefaultEndDynasty}, // half below default balance, should be moved to exit
{Balance: 15, StartDynasty: 1, EndDynasty: params.DefaultEndDynasty}, // half below default balance, should be moved to exit
{Balance: 20, StartDynasty: 2, EndDynasty: params.DefaultEndDynasty}, // stays in active
{Balance: 25, StartDynasty: 3, EndDynasty: params.DefaultEndDynasty}, // stays in active
{Balance: 30, StartDynasty: 4, EndDynasty: params.DefaultEndDynasty}, // stays in active
{Balance: 10, StartDynasty: 0, EndDynasty: params.DefaultEndDynasty}, // half below default balance, should be moved to exit.
{Balance: 15, StartDynasty: 1, EndDynasty: params.DefaultEndDynasty}, // half below default balance, should be moved to exit.
{Balance: 20, StartDynasty: 2, EndDynasty: params.DefaultEndDynasty}, // stays in active.
{Balance: 25, StartDynasty: 3, EndDynasty: params.DefaultEndDynasty}, // stays in active.
{Balance: 30, StartDynasty: 4, EndDynasty: params.DefaultEndDynasty}, // stays in active.
}
data := &pb.CrystallizedState{
@@ -444,7 +479,7 @@ func TestRotateValidatorSet(t *testing.T) {
state := types.NewCrystallizedState(data)
beaconChain.SetCrystallizedState(state)
// rotate validator set and increment dynasty count by 1
// rotate validator set and increment dynasty count by 1.
beaconChain.rotateValidatorSet()
beaconChain.CrystallizedState().IncrementCurrentDynasty()
@@ -478,7 +513,7 @@ func TestHasVoted(t *testing.T) {
beaconChain, db := startInMemoryBeaconChain(t)
defer db.Close()
// Setting bit field to 11111111
// Setting bit field to 11111111.
pendingAttestation := &pb.AttestationRecord{
AttesterBitfield: []byte{255},
}
@@ -494,7 +529,7 @@ func TestHasVoted(t *testing.T) {
}
}
// Setting bit field to 01010101
// Setting bit field to 01010101.
pendingAttestation = &pb.AttestationRecord{
AttesterBitfield: []byte{85},
}
@@ -654,12 +689,12 @@ func TestValidatorIndices(t *testing.T) {
data := &pb.CrystallizedState{
Validators: []*pb.ValidatorRecord{
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active
{PublicKey: 0, StartDynasty: 1, EndDynasty: 2}, // active
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active
{PublicKey: 0, StartDynasty: 0, EndDynasty: 3}, // active
{PublicKey: 0, StartDynasty: 2, EndDynasty: uint64(math.Inf(0))}, // queued
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active.
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active.
{PublicKey: 0, StartDynasty: 1, EndDynasty: 2}, // active.
{PublicKey: 0, StartDynasty: 0, EndDynasty: 2}, // active.
{PublicKey: 0, StartDynasty: 0, EndDynasty: 3}, // active.
{PublicKey: 0, StartDynasty: 2, EndDynasty: uint64(math.Inf(0))}, // queued.
},
CurrentDynasty: 1,
}
@@ -681,12 +716,12 @@ func TestValidatorIndices(t *testing.T) {
data = &pb.CrystallizedState{
Validators: []*pb.ValidatorRecord{
{PublicKey: 0, StartDynasty: 1, EndDynasty: uint64(math.Inf(0))}, // active
{PublicKey: 0, StartDynasty: 2, EndDynasty: uint64(math.Inf(0))}, // active
{PublicKey: 0, StartDynasty: 6, EndDynasty: uint64(math.Inf(0))}, // queued
{PublicKey: 0, StartDynasty: 7, EndDynasty: uint64(math.Inf(0))}, // queued
{PublicKey: 0, StartDynasty: 1, EndDynasty: 2}, // exited
{PublicKey: 0, StartDynasty: 1, EndDynasty: 3}, // exited
{PublicKey: 0, StartDynasty: 1, EndDynasty: uint64(math.Inf(0))}, // active.
{PublicKey: 0, StartDynasty: 2, EndDynasty: uint64(math.Inf(0))}, // active.
{PublicKey: 0, StartDynasty: 6, EndDynasty: uint64(math.Inf(0))}, // queued.
{PublicKey: 0, StartDynasty: 7, EndDynasty: uint64(math.Inf(0))}, // queued.
{PublicKey: 0, StartDynasty: 1, EndDynasty: 2}, // exited.
{PublicKey: 0, StartDynasty: 1, EndDynasty: 3}, // exited.
},
CurrentDynasty: 5,
}
@@ -708,7 +743,7 @@ func TestValidatorIndices(t *testing.T) {
}
// NewBlock is a helper method to create blocks with valid defaults.
// For a generic block, use NewBlock(t, nil)
// For a generic block, use NewBlock(t, nil).
func NewBlock(t *testing.T, b *pb.BeaconBlock) *types.Block {
if b == nil {
b = &pb.BeaconBlock{}

View File

@@ -41,10 +41,11 @@ func DefaultConfig() *Config {
// NewChainService instantiates a new service instance that will
// be registered into a running beacon node.
func NewChainService(ctx context.Context, cfg *Config, beaconDB *database.DB, web3Service *powchain.Web3Service) (*ChainService, error) {
func NewChainService(ctx context.Context, cfg *Config, beaconChain *BeaconChain, beaconDB *database.DB, web3Service *powchain.Web3Service) (*ChainService, error) {
ctx, cancel := context.WithCancel(ctx)
return &ChainService{
ctx: ctx,
chain: beaconChain,
cancel: cancel,
beaconDB: beaconDB,
web3Service: web3Service,
@@ -59,11 +60,6 @@ func NewChainService(ctx context.Context, cfg *Config, beaconDB *database.DB, we
func (c *ChainService) Start() {
log.Infof("Starting service")
beaconChain, err := NewBeaconChain(c.beaconDB.DB())
if err != nil {
log.Errorf("Unable to setup blockchain: %v", err)
}
c.chain = beaconChain
go c.run(c.ctx.Done())
}
@@ -200,17 +196,6 @@ func (c *ChainService) run(done <-chan struct{}) {
for {
select {
case block := <-c.latestBeaconBlock:
// TODO: Using latest block hash for seed, this will eventually be replaced by randao.
activeState, err := c.chain.computeNewActiveState(c.web3Service.LatestBlockHash())
if err != nil {
log.Errorf("Compute active state failed: %v", err)
}
err = c.chain.SetActiveState(activeState)
if err != nil {
log.Errorf("Write active state to disk failed: %v", err)
}
// TODO: Apply 2.1 fork choice logic using the following.
validatorsByHeight, err := c.chain.validatorsByHeightShard()
if err != nil {
@@ -226,6 +211,16 @@ func (c *ChainService) run(done <-chan struct{}) {
}
}
// TODO: Using latest block hash for seed, this will eventually be replaced by randao.
activeState, err := c.chain.computeNewActiveState(c.web3Service.LatestBlockHash())
if err != nil {
log.Errorf("Compute active state failed: %v", err)
}
err = c.chain.SetActiveState(activeState)
if err != nil {
log.Errorf("Write active state to disk failed: %v", err)
}
case <-done:
log.Debug("Chain service context closed, exiting goroutine")
return

View File

@@ -6,12 +6,41 @@ import (
"os"
"testing"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/event"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/types"
pb "github.com/prysmaticlabs/prysm/proto/beacon/p2p/v1"
"github.com/prysmaticlabs/prysm/shared/database"
logTest "github.com/sirupsen/logrus/hooks/test"
)
type mockClient struct{}
func (f *mockClient) SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error) {
return new(event.Feed).Subscribe(ch), nil
}
func (f *mockClient) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) {
return nil, nil
}
func (f *mockClient) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error) {
return new(event.Feed).Subscribe(ch), nil
}
func (f *mockClient) LatestBlockHash() common.Hash {
return common.BytesToHash([]byte{'A'})
}
func TestDefaultConfig(t *testing.T) {
if DefaultConfig().BeaconBlockBuf != 10 {
t.Errorf("Default block buffer should be 10, got: %v", DefaultConfig().BeaconBlockBuf)
}
}
func TestStartStop(t *testing.T) {
hook := logTest.NewGlobal()
ctx := context.Background()
@@ -25,57 +54,317 @@ func TestStartStop(t *testing.T) {
}
endpoint := "ws://127.0.0.1"
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}})
client := &mockClient{}
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client)
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
}
cfg := &Config{
BeaconBlockBuf: 0,
}
chainService, err := NewChainService(ctx, cfg, db, web3Service)
beaconChain, err := NewBeaconChain(db.DB())
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
}
chainService, err := NewChainService(ctx, cfg, beaconChain, db, web3Service)
if err != nil {
t.Fatalf("unable to setup chain service: %v", err)
}
chainService.Start()
if len(chainService.ProcessedBlockHashes()) != 0 {
t.Errorf("incorrect processedBlockHashes size")
}
if len(chainService.ProcessedCrystallizedStateHashes()) != 0 {
t.Errorf("incorrect processedCrystallizedStateHashes size")
}
if len(chainService.ProcessedActiveStateHashes()) != 0 {
t.Errorf("incorrect processedActiveStateHashes size")
}
if len(chainService.CurrentActiveState().RecentBlockHashes()) != 0 {
t.Errorf("incorrect recent block hashes")
}
if len(chainService.CurrentCrystallizedState().Validators()) != 0 {
t.Errorf("incorrect default validator size")
}
if chainService.ContainsBlock([32]byte{}) {
t.Errorf("chain is not empty")
}
if chainService.ContainsCrystallizedState([32]byte{}) {
t.Errorf("cyrstallized states is not empty")
}
if chainService.ContainsActiveState([32]byte{}) {
t.Errorf("active states is not empty")
}
hasState, err := chainService.HasStoredState()
if err != nil {
t.Fatalf("calling HasStoredState failed")
}
if hasState {
t.Errorf("has stored state should return false")
}
chainService, _ = NewChainService(ctx, cfg, beaconChain, db, web3Service)
active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'A'}}})
activeStateHash, err := active.Hash()
if err != nil {
t.Fatalf("Cannot hash active state: %v", err)
}
chainService.chain.SetActiveState(active)
crystallized := types.NewCrystallizedState(&pb.CrystallizedState{LastStateRecalc: 10000})
crystallizedStateHash, err := crystallized.Hash()
if err != nil {
t.Fatalf("Cannot hash crystallized state: %v", err)
}
chainService.chain.SetCrystallizedState(crystallized)
parentBlock := NewBlock(t, nil)
parentHash, _ := parentBlock.Hash()
block := NewBlock(t, &pb.BeaconBlock{
SlotNumber: 2,
ActiveStateHash: activeStateHash[:],
CrystallizedStateHash: crystallizedStateHash[:],
ParentHash: parentHash[:],
PowChainRef: []byte("a"),
})
if err := chainService.SaveBlock(block); err != nil {
t.Errorf("save block should have failed")
}
// Save states so HasStoredState state should return true.
chainService.chain.SetActiveState(types.NewActiveState(&pb.ActiveState{}))
chainService.chain.SetCrystallizedState(types.NewCrystallizedState(&pb.CrystallizedState{}))
hasState, _ = chainService.HasStoredState()
if !hasState {
t.Errorf("has stored state should return false")
}
if err := chainService.Stop(); err != nil {
t.Fatalf("unable to stop chain service: %v", err)
}
msg := hook.AllEntries()[0].Message
want := "Starting service"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
msg = hook.AllEntries()[1].Message
want = "No genesis block found on disk, initializing genesis block"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
msg = hook.AllEntries()[2].Message
want = "No chainstate found on disk, initializing beacon from genesis"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
msg = hook.AllEntries()[3].Message
want = "Stopping service"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
msg = hook.AllEntries()[4].Message
want = "Persisting current active and crystallized states before closing"
if msg != want {
t.Errorf("incorrect log, expected %s, got %s", want, msg)
}
// The context should have been canceled.
if chainService.ctx.Err() == nil {
t.Error("context was not canceled")
}
hook.Reset()
}
func TestFaultyStop(t *testing.T) {
hook := logTest.NewGlobal()
ctx := context.Background()
tmp := fmt.Sprintf("%s/beacontest", os.TempDir())
defer os.RemoveAll(tmp)
config := &database.DBConfig{DataDir: tmp, Name: "beacontestdata", InMemory: false}
db, err := database.NewDB(config)
if err != nil {
t.Fatalf("could not setup beaconDB: %v", err)
}
endpoint := "ws://127.0.0.1"
client := &mockClient{}
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client)
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
}
cfg := &Config{
BeaconBlockBuf: 0,
}
beaconChain, err := NewBeaconChain(db.DB())
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
}
chainService, err := NewChainService(ctx, cfg, beaconChain, db, web3Service)
if err != nil {
t.Fatalf("unable to setup chain service: %v", err)
}
chainService.Start()
chainService.chain.SetActiveState(types.NewActiveState(nil))
err = chainService.Stop()
if err == nil {
t.Errorf("chain stop should have failed with persist active state")
}
chainService.chain.SetActiveState(types.NewActiveState(&pb.ActiveState{}))
chainService.chain.SetCrystallizedState(types.NewCrystallizedState(nil))
err = chainService.Stop()
if err == nil {
t.Errorf("chain stop should have failed with persist crystallized state")
}
hook.Reset()
}
func TestProcessingStates(t *testing.T) {
ctx := context.Background()
tmp := fmt.Sprintf("%s/beacontest", os.TempDir())
defer os.RemoveAll(tmp)
config := &database.DBConfig{DataDir: tmp, Name: "beacontestdata", InMemory: false}
db, err := database.NewDB(config)
if err != nil {
t.Fatalf("could not setup beaconDB: %v", err)
}
endpoint := "ws://127.0.0.1"
client := &mockClient{}
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client)
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
}
cfg := &Config{
BeaconBlockBuf: 0,
}
beaconChain, err := NewBeaconChain(db.DB())
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
}
chainService, _ := NewChainService(ctx, cfg, beaconChain, db, web3Service)
if err := chainService.ProcessCrystallizedState(types.NewCrystallizedState(nil)); err == nil {
t.Errorf("processing crystallized state should have failed")
}
if err := chainService.ProcessActiveState(types.NewActiveState(nil)); err == nil {
t.Errorf("processing active state should have failed")
}
chainService.ProcessCrystallizedState(types.NewCrystallizedState(&pb.CrystallizedState{}))
chainService.ProcessActiveState(types.NewActiveState(&pb.ActiveState{}))
}
func TestProcessingBadBlock(t *testing.T) {
ctx := context.Background()
tmp := fmt.Sprintf("%s/beacontest", os.TempDir())
defer os.RemoveAll(tmp)
config := &database.DBConfig{DataDir: tmp, Name: "beacontestdata", InMemory: false}
db, err := database.NewDB(config)
if err != nil {
t.Fatalf("could not setup beaconDB: %v", err)
}
endpoint := "ws://127.0.0.1"
client := &mockClient{}
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client)
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
}
cfg := &Config{
BeaconBlockBuf: 0,
}
beaconChain, err := NewBeaconChain(db.DB())
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
}
chainService, _ := NewChainService(ctx, cfg, beaconChain, db, web3Service)
active := types.NewActiveState(&pb.ActiveState{RecentBlockHashes: [][]byte{{'A'}}})
activeStateHash, err := active.Hash()
if err != nil {
t.Fatalf("Cannot hash active state: %v", err)
}
chainService.chain.SetActiveState(active)
crystallized := types.NewCrystallizedState(&pb.CrystallizedState{LastStateRecalc: 10000})
crystallizedStateHash, err := crystallized.Hash()
if err != nil {
t.Fatalf("Cannot hash crystallized state: %v", err)
}
chainService.chain.SetCrystallizedState(crystallized)
parentBlock := NewBlock(t, nil)
parentHash, _ := parentBlock.Hash()
block := NewBlock(t, &pb.BeaconBlock{
SlotNumber: 2,
ActiveStateHash: activeStateHash[:],
CrystallizedStateHash: crystallizedStateHash[:],
ParentHash: parentHash[:],
PowChainRef: []byte("a"),
})
if err = chainService.ProcessBlock(block); err == nil {
t.Error("process block should have failed with parent hash points to nil")
}
}
func TestRunningChainService(t *testing.T) {
ctx := context.Background()
tmp := fmt.Sprintf("%s/beacontest", os.TempDir())
defer os.RemoveAll(tmp)
config := &database.DBConfig{DataDir: tmp, Name: "beacontestdata", InMemory: false}
db, err := database.NewDB(config)
if err != nil {
t.Fatalf("could not setup beaconDB: %v", err)
}
endpoint := "ws://127.0.0.1"
client := &mockClient{}
web3Service, err := powchain.NewWeb3Service(ctx, &powchain.Web3ServiceConfig{Endpoint: endpoint, Pubkey: "", VrcAddr: common.Address{}}, client, client, client)
if err != nil {
t.Fatalf("unable to set up web3 service: %v", err)
}
cfg := &Config{
BeaconBlockBuf: 0,
}
beaconChain, err := NewBeaconChain(db.DB())
if err != nil {
t.Fatalf("could not register blockchain service: %v", err)
}
testAttesterBitfield := []byte{200, 148, 146, 179, 49}
state := types.NewActiveState(&pb.ActiveState{PendingAttestations: []*pb.AttestationRecord{{AttesterBitfield: testAttesterBitfield}}})
if err := beaconChain.SetActiveState(state); err != nil {
t.Fatalf("unable to Mutate Active state: %v", err)
}
chainService, _ := NewChainService(ctx, cfg, beaconChain, db, web3Service)
exitRoutine := make(chan bool)
go func() {
chainService.run(chainService.ctx.Done())
<-exitRoutine
}()
parentBlock := NewBlock(t, nil)
parentHash, _ := parentBlock.Hash()
activeStateHash, err := state.Hash()
if err != nil {
t.Fatalf("Cannot hash active state: %v", err)
}
var validators []*pb.ValidatorRecord
for i := 0; i < 40; i++ {
validator := &pb.ValidatorRecord{Balance: 32, StartDynasty: 1, EndDynasty: 10}
validators = append(validators, validator)
}
crystallized := types.NewCrystallizedState(&pb.CrystallizedState{Validators: validators, CurrentDynasty: 5})
crystallizedStateHash, err := crystallized.Hash()
if err != nil {
t.Fatalf("Cannot hash crystallized state: %v", err)
}
chainService.chain.SetCrystallizedState(crystallized)
block := NewBlock(t, &pb.BeaconBlock{
SlotNumber: 65,
ActiveStateHash: activeStateHash[:],
CrystallizedStateHash: crystallizedStateHash[:],
ParentHash: parentHash[:],
PowChainRef: []byte("a"),
})
chainService.latestBeaconBlock <- block
chainService.cancel()
exitRoutine <- true
}

View File

@@ -18,6 +18,8 @@ go_library(
"//shared/debug:go_default_library",
"//shared/p2p:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli//:go_default_library",
],

View File

@@ -10,6 +10,8 @@ import (
"syscall"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/ethclient"
gethRPC "github.com/ethereum/go-ethereum/rpc"
"github.com/prysmaticlabs/prysm/beacon-chain/blockchain"
"github.com/prysmaticlabs/prysm/beacon-chain/powchain"
"github.com/prysmaticlabs/prysm/beacon-chain/rpc"
@@ -150,7 +152,12 @@ func (b *BeaconNode) registerBlockchainService() error {
return err
}
blockchainService, err := blockchain.NewChainService(context.TODO(), blockchain.DefaultConfig(), b.db, web3Service)
beaconChain, err := blockchain.NewBeaconChain(b.db.DB())
if err != nil {
return fmt.Errorf("could not register blockchain service: %v", err)
}
blockchainService, err := blockchain.NewChainService(context.TODO(), blockchain.DefaultConfig(), beaconChain, b.db, web3Service)
if err != nil {
return fmt.Errorf("could not register blockchain service: %v", err)
}
@@ -158,11 +165,17 @@ func (b *BeaconNode) registerBlockchainService() error {
}
func (b *BeaconNode) registerPOWChainService() error {
rpcClient, err := gethRPC.Dial(b.ctx.GlobalString(utils.Web3ProviderFlag.Name))
if err != nil {
log.Errorf("Unable to connect to Geth node: %v", err)
}
powClient := ethclient.NewClient(rpcClient)
web3Service, err := powchain.NewWeb3Service(context.TODO(), &powchain.Web3ServiceConfig{
Endpoint: b.ctx.GlobalString(utils.Web3ProviderFlag.Name),
Pubkey: b.ctx.GlobalString(utils.PubKeyFlag.Name),
VrcAddr: common.HexToAddress(b.ctx.GlobalString(utils.VrcContractFlag.Name)),
})
}, powClient, powClient, powClient)
if err != nil {
return fmt.Errorf("could not register proof-of-work chain web3Service: %v", err)
}

View File

@@ -10,8 +10,6 @@ go_library(
"@com_github_ethereum_go_ethereum//:go_default_library",
"@com_github_ethereum_go_ethereum//common:go_default_library",
"@com_github_ethereum_go_ethereum//core/types:go_default_library",
"@com_github_ethereum_go_ethereum//ethclient:go_default_library",
"@com_github_ethereum_go_ethereum//rpc:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)

View File

@@ -10,8 +10,6 @@ import (
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
gethTypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc"
"github.com/prysmaticlabs/prysm/beacon-chain/types"
"github.com/sirupsen/logrus"
)
@@ -27,7 +25,7 @@ var log = logrus.WithField("prefix", "powchain")
type Web3Service struct {
ctx context.Context
cancel context.CancelFunc
client *ethclient.Client
client types.POWChainClient
headerChan chan *gethTypes.Header
logChan chan gethTypes.Log
pubKey string
@@ -49,7 +47,7 @@ type Web3ServiceConfig struct {
// NewWeb3Service sets up a new instance with an ethclient when
// given a web3 endpoint as a string in the config.
func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Service, error) {
func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig, client types.POWChainClient, reader types.Reader, logger types.Logger) (*Web3Service, error) {
if !strings.HasPrefix(config.Endpoint, "ws") && !strings.HasPrefix(config.Endpoint, "ipc") {
return nil, fmt.Errorf("web3service requires either an IPC or WebSocket endpoint, provided %s", config.Endpoint)
}
@@ -65,6 +63,9 @@ func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Servic
blockNumber: nil,
blockHash: common.BytesToHash([]byte{}),
vrcAddress: config.VrcAddr,
client: client,
reader: reader,
logger: logger,
}, nil
}
@@ -73,13 +74,6 @@ func (w *Web3Service) Start() {
log.WithFields(logrus.Fields{
"endpoint": w.endpoint,
}).Info("Starting service")
rpcClient, err := rpc.Dial(w.endpoint)
if err != nil {
log.Errorf("Cannot connect to PoW chain RPC client: %v", err)
return
}
w.client = ethclient.NewClient(rpcClient)
w.reader, w.logger = w.client, w.client
go w.run(w.ctx.Done())
}

View File

@@ -42,19 +42,19 @@ func (g *goodLogger) SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQ
func TestNewWeb3Service(t *testing.T) {
endpoint := "http://127.0.0.1"
ctx := context.Background()
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err == nil {
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{}); err == nil {
t.Errorf("passing in an HTTP endpoint should throw an error, received nil")
}
endpoint = "ftp://127.0.0.1"
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err == nil {
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{}); err == nil {
t.Errorf("passing in a non-ws, wss, or ipc endpoint should throw an error, received nil")
}
endpoint = "ws://127.0.0.1"
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err != nil {
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{}); err != nil {
t.Errorf("passing in as ws endpoint should not throw error, received %v", err)
}
endpoint = "ipc://geth.ipc"
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}); err != nil {
if _, err := NewWeb3Service(ctx, &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{}); err != nil {
t.Errorf("passing in an ipc endpoint should not throw error, received %v", err)
}
}
@@ -63,7 +63,7 @@ func TestStart(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -82,7 +82,7 @@ func TestStop(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -107,7 +107,7 @@ func TestStop(t *testing.T) {
func TestBadReader(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &badReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -124,7 +124,7 @@ func TestBadReader(t *testing.T) {
func TestLatestMainchainInfo(t *testing.T) {
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -156,7 +156,7 @@ func TestLatestMainchainInfo(t *testing.T) {
func TestBadLogger(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -175,7 +175,7 @@ func TestBadLogger(t *testing.T) {
func TestGoodLogger(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}
@@ -218,7 +218,7 @@ func TestGoodLogger(t *testing.T) {
func TestHeaderAfterValidation(t *testing.T) {
hook := logTest.NewGlobal()
endpoint := "ws://127.0.0.1"
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}})
web3Service, err := NewWeb3Service(context.Background(), &Web3ServiceConfig{endpoint, "", common.Address{}}, nil, &goodReader{}, &goodLogger{})
if err != nil {
t.Fatalf("unable to setup web3 PoW chain service: %v", err)
}