mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-24 12:27:18 +00:00
785 lines
30 KiB
Go
785 lines
30 KiB
Go
package blockchain
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"math"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/ethereum/go-ethereum/common"
|
|
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
|
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/params"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/types"
|
|
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
|
|
pb "github.com/prysmaticlabs/prysm/proto/sharding/v1"
|
|
"github.com/prysmaticlabs/prysm/shared/database"
|
|
logTest "github.com/sirupsen/logrus/hooks/test"
|
|
)
|
|
|
|
type faultyFetcher struct{}
|
|
|
|
func (f *faultyFetcher) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) {
|
|
return nil, errors.New("cannot fetch block")
|
|
}
|
|
|
|
type mockFetcher struct{}
|
|
|
|
func (m *mockFetcher) BlockByHash(ctx context.Context, hash common.Hash) (*gethTypes.Block, error) {
|
|
block := gethTypes.NewBlock(&gethTypes.Header{}, nil, nil, nil)
|
|
return block, nil
|
|
}
|
|
|
|
func startInMemoryBeaconChain(t *testing.T) (*BeaconChain, *database.DB) {
|
|
config := &database.DBConfig{DataDir: "", Name: "", InMemory: true}
|
|
db, err := database.NewDB(config)
|
|
if err != nil {
|
|
t.Fatalf("unable to setup db: %v", err)
|
|
}
|
|
beaconChain, err := NewBeaconChain(db.DB())
|
|
if err != nil {
|
|
t.Fatalf("unable to setup beacon chain: %v", err)
|
|
}
|
|
|
|
return beaconChain, db
|
|
}
|
|
|
|
func TestNewBeaconChain(t *testing.T) {
|
|
hook := logTest.NewGlobal()
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
msg := hook.LastEntry().Message
|
|
want := "No chainstate found on disk, initializing beacon from genesis"
|
|
if msg != want {
|
|
t.Errorf("incorrect log, expected %s, got %s", want, msg)
|
|
}
|
|
|
|
hook.Reset()
|
|
active, crystallized := types.NewGenesisStates()
|
|
if !reflect.DeepEqual(beaconChain.ActiveState(), active) {
|
|
t.Errorf("active states not equal. received: %v, wanted: %v", beaconChain.ActiveState(), active)
|
|
}
|
|
if !reflect.DeepEqual(beaconChain.CrystallizedState(), crystallized) {
|
|
t.Errorf("crystallized states not equal. received: %v, wanted: %v", beaconChain.CrystallizedState(), crystallized)
|
|
}
|
|
}
|
|
|
|
func TestMutateActiveState(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
data := &pb.ActiveStateResponse{
|
|
TotalAttesterDeposits: 4096,
|
|
AttesterBitfield: []byte{'A', 'B', 'C'},
|
|
}
|
|
active := types.NewActiveState(data)
|
|
|
|
if err := beaconChain.MutateActiveState(active); err != nil {
|
|
t.Fatalf("unable to mutate active state: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(beaconChain.state.ActiveState, active) {
|
|
t.Errorf("active state was not updated. wanted %v, got %v", active, beaconChain.state.ActiveState)
|
|
}
|
|
|
|
// Initializing a new beacon chain should deserialize persisted state from disk.
|
|
newBeaconChain, err := NewBeaconChain(db.DB())
|
|
if err != nil {
|
|
t.Fatalf("unable to setup second beacon chain: %v", err)
|
|
}
|
|
|
|
// The active state should still be the one we mutated and persited earlier.
|
|
if active.TotalAttesterDeposits() != newBeaconChain.state.ActiveState.TotalAttesterDeposits() {
|
|
t.Errorf("active state height incorrect. wanted %v, got %v", active.TotalAttesterDeposits(), newBeaconChain.state.ActiveState.TotalAttesterDeposits())
|
|
}
|
|
if !bytes.Equal(active.AttesterBitfield(), newBeaconChain.state.ActiveState.AttesterBitfield()) {
|
|
t.Errorf("active state randao incorrect. wanted %v, got %v", active.AttesterBitfield(), newBeaconChain.state.ActiveState.AttesterBitfield())
|
|
}
|
|
}
|
|
|
|
func TestMutateCrystallizedState(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
data := &pb.CrystallizedStateResponse{
|
|
CurrentDynasty: 3,
|
|
CurrentCheckPoint: []byte("checkpoint"),
|
|
}
|
|
crystallized := types.NewCrystallizedState(data)
|
|
|
|
if err := beaconChain.MutateCrystallizedState(crystallized); err != nil {
|
|
t.Fatalf("unable to mutate crystallized state: %v", err)
|
|
}
|
|
if !reflect.DeepEqual(beaconChain.state.CrystallizedState, crystallized) {
|
|
t.Errorf("crystallized state was not updated. wanted %v, got %v", crystallized, beaconChain.state.CrystallizedState)
|
|
}
|
|
|
|
// Initializing a new beacon chain should deserialize persisted state from disk.
|
|
newBeaconChain, err := NewBeaconChain(db.DB())
|
|
if err != nil {
|
|
t.Fatalf("unable to setup second beacon chain: %v", err)
|
|
}
|
|
|
|
// The crystallized state should still be the one we mutated and persited earlier.
|
|
if crystallized.CurrentDynasty() != newBeaconChain.state.CrystallizedState.CurrentDynasty() {
|
|
t.Errorf("crystallized state dynasty incorrect. wanted %v, got %v", crystallized.CurrentDynasty(), newBeaconChain.state.CrystallizedState.CurrentDynasty())
|
|
}
|
|
if crystallized.CurrentCheckPoint().Hex() != newBeaconChain.state.CrystallizedState.CurrentCheckPoint().Hex() {
|
|
t.Errorf("crystallized state current checkpoint incorrect. wanted %v, got %v", crystallized.CurrentCheckPoint().Hex(), newBeaconChain.state.CrystallizedState.CurrentCheckPoint().Hex())
|
|
}
|
|
}
|
|
|
|
func TestGetAttestersProposer(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
var validators []*pb.ValidatorRecord
|
|
// Create 1000 validators in ActiveValidators.
|
|
for i := 0; i < 1000; i++ {
|
|
validator := &pb.ValidatorRecord{WithdrawalAddress: []byte{'A'}, PublicKey: 0}
|
|
validators = append(validators, validator)
|
|
}
|
|
|
|
_, crystallized := types.NewGenesisStates()
|
|
crystallized.UpdateActiveValidators(validators)
|
|
beaconChain.MutateCrystallizedState(crystallized)
|
|
|
|
attesters, proposer, err := beaconChain.getAttestersProposer(common.Hash{'A'})
|
|
if err != nil {
|
|
t.Errorf("GetAttestersProposer function failed: %v", err)
|
|
}
|
|
|
|
validatorList, err := utils.ShuffleIndices(common.Hash{'A'}, len(validators))
|
|
if err != nil {
|
|
t.Errorf("Shuffle function function failed: %v", err)
|
|
}
|
|
|
|
if !reflect.DeepEqual(proposer, validatorList[len(validatorList)-1]) {
|
|
t.Errorf("Get proposer failed, expected: %v got: %v", validatorList[len(validatorList)-1], proposer)
|
|
}
|
|
if !reflect.DeepEqual(attesters, validatorList[:len(attesters)]) {
|
|
t.Errorf("Get attesters failed, expected: %v got: %v", validatorList[:len(attesters)], attesters)
|
|
}
|
|
}
|
|
|
|
func TestCanProcessBlock(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
// Initialize a parent block
|
|
parentBlock := NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 1,
|
|
})
|
|
parentHash, err := parentBlock.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Failed to compute parent block's hash: %v", err)
|
|
}
|
|
if err = db.DB().Put(parentHash[:], []byte{}); err != nil {
|
|
t.Fatalf("Failed to put parent block on db: %v", err)
|
|
}
|
|
|
|
// Using a faulty fetcher should throw an error.
|
|
block := NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 2,
|
|
})
|
|
if _, err := beaconChain.CanProcessBlock(&faultyFetcher{}, block); err == nil {
|
|
t.Error("Using a faulty fetcher should throw an error, received nil")
|
|
}
|
|
|
|
// Initialize initial state
|
|
activeState := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
|
|
beaconChain.state.ActiveState = activeState
|
|
activeHash, err := activeState.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Cannot hash active state: %v", err)
|
|
}
|
|
|
|
crystallized := types.NewCrystallizedState(&pb.CrystallizedStateResponse{CurrentEpoch: 5})
|
|
beaconChain.state.CrystallizedState = crystallized
|
|
crystallizedHash, err := crystallized.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Compute crystallized state hash failed: %v", err)
|
|
}
|
|
|
|
block = NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 2,
|
|
ActiveStateHash: activeHash[:],
|
|
CrystallizedStateHash: crystallizedHash[:],
|
|
ParentHash: parentHash[:],
|
|
})
|
|
|
|
// A properly initialize block should not fail
|
|
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err != nil {
|
|
t.Fatalf("CanProcessBlocks failed: %v", err)
|
|
}
|
|
if !canProcess {
|
|
t.Error("Should be able to process block, could not")
|
|
}
|
|
|
|
// Test timestamp validity condition
|
|
block = NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 1000000,
|
|
ActiveStateHash: activeHash[:],
|
|
CrystallizedStateHash: crystallizedHash[:],
|
|
ParentHash: parentHash[:],
|
|
})
|
|
canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err != nil {
|
|
t.Fatalf("CanProcessBlocks failed: %v", err)
|
|
}
|
|
if canProcess {
|
|
t.Error("Should not be able to process block with invalid timestamp condition")
|
|
}
|
|
}
|
|
|
|
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
|
|
parentBlock := NewBlock(t, nil)
|
|
parentHash, err := parentBlock.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Failed to compute parent block's hash: %v", err)
|
|
}
|
|
if err = db.DB().Put(parentHash[:], []byte{}); err != nil {
|
|
t.Fatalf("Failed to put parent block on db: %v", err)
|
|
}
|
|
|
|
// Initialize state
|
|
active := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
|
|
activeStateHash, err := active.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Cannot hash active state: %v", err)
|
|
}
|
|
crystallized := types.NewCrystallizedState(&pb.CrystallizedStateResponse{CurrentEpoch: 10000})
|
|
crystallizedStateHash, err := crystallized.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Cannot hash crystallized state: %v", err)
|
|
}
|
|
|
|
block := NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 1,
|
|
ActiveStateHash: activeStateHash[:],
|
|
CrystallizedStateHash: crystallizedStateHash[:],
|
|
ParentHash: parentHash[:],
|
|
})
|
|
|
|
// Test negative scenario where active state hash is different than node's compute
|
|
beaconChain.state.ActiveState = types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 9999})
|
|
|
|
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err == nil {
|
|
t.Fatal("CanProcessBlocks should have failed with diff state hashes")
|
|
}
|
|
if canProcess {
|
|
t.Error("CanProcessBlocks should have returned false")
|
|
}
|
|
|
|
// Test negative scenario where crystallized state hash is different than node's compute
|
|
beaconChain.state.CrystallizedState = types.NewCrystallizedState(&pb.CrystallizedStateResponse{CurrentEpoch: 9999})
|
|
|
|
canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err == nil {
|
|
t.Fatal("CanProcessBlocks should have failed with diff state hashes")
|
|
}
|
|
if canProcess {
|
|
t.Error("CanProcessBlocks should have returned false")
|
|
}
|
|
}
|
|
|
|
func TestProcessBlockWithInvalidParent(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
// If parent hash is non-existent, processing block should fail.
|
|
active := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
|
|
activeStateHash, err := active.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Cannot hash active state: %v", err)
|
|
}
|
|
beaconChain.state.ActiveState = active
|
|
|
|
crystallized := types.NewCrystallizedState(&pb.CrystallizedStateResponse{CurrentEpoch: 10000})
|
|
crystallizedStateHash, err := crystallized.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Cannot hash crystallized state: %v", err)
|
|
}
|
|
beaconChain.state.CrystallizedState = crystallized
|
|
|
|
// Test that block processing is invalid without a parent hash
|
|
block := NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 2,
|
|
ActiveStateHash: activeStateHash[:],
|
|
CrystallizedStateHash: crystallizedStateHash[:],
|
|
})
|
|
if _, err = beaconChain.CanProcessBlock(&mockFetcher{}, block); err == nil {
|
|
t.Error("Processing without a valid parent hash should fail")
|
|
}
|
|
|
|
// If parent hash is not stored in db, processing block should fail.
|
|
parentBlock := NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 1,
|
|
})
|
|
parentHash, err := parentBlock.Hash()
|
|
if err != nil {
|
|
t.Fatalf("Failed to compute parent block's hash: %v", err)
|
|
}
|
|
block = NewBlock(t, &pb.BeaconBlockResponse{
|
|
SlotNumber: 2,
|
|
ActiveStateHash: activeStateHash[:],
|
|
CrystallizedStateHash: crystallizedStateHash[:],
|
|
ParentHash: parentHash[:],
|
|
})
|
|
|
|
if _, err := beaconChain.CanProcessBlock(&mockFetcher{}, block); err == nil {
|
|
t.Error("Processing block should fail when parent hash is not in db")
|
|
}
|
|
|
|
if err = db.DB().Put(parentHash[:], nil); err != nil {
|
|
t.Fatalf("Failed to put parent block on db: %v", err)
|
|
}
|
|
_, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err == nil {
|
|
t.Error("Processing block should fail when parent hash points to nil in db")
|
|
}
|
|
want := "parent hash points to nil in beaconDB"
|
|
if err.Error() != want {
|
|
t.Errorf("invalid log, expected \"%s\", got \"%s\"", want, err.Error())
|
|
}
|
|
|
|
if err = db.DB().Put(parentHash[:], []byte{}); err != nil {
|
|
t.Fatalf("Failed to put parent block on db: %v", err)
|
|
}
|
|
|
|
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
|
if err != nil {
|
|
t.Errorf("Should have been able to process block: %v", err)
|
|
}
|
|
if !canProcess {
|
|
t.Error("Should have been able to process block")
|
|
}
|
|
}
|
|
|
|
func TestRotateValidatorSet(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
activeValidators := []*pb.ValidatorRecord{
|
|
{Balance: 10000, WithdrawalAddress: []byte{'A'}},
|
|
{Balance: 15000, WithdrawalAddress: []byte{'B'}},
|
|
{Balance: 20000, WithdrawalAddress: []byte{'C'}},
|
|
{Balance: 25000, WithdrawalAddress: []byte{'D'}},
|
|
{Balance: 30000, WithdrawalAddress: []byte{'E'}},
|
|
}
|
|
|
|
queuedValidators := []*pb.ValidatorRecord{
|
|
{Balance: params.DefaultBalance, WithdrawalAddress: []byte{'F'}},
|
|
{Balance: params.DefaultBalance, WithdrawalAddress: []byte{'G'}},
|
|
{Balance: params.DefaultBalance, WithdrawalAddress: []byte{'H'}},
|
|
{Balance: params.DefaultBalance, WithdrawalAddress: []byte{'I'}},
|
|
{Balance: params.DefaultBalance, WithdrawalAddress: []byte{'J'}},
|
|
}
|
|
|
|
exitedValidators := []*pb.ValidatorRecord{
|
|
{Balance: 99999, WithdrawalAddress: []byte{'K'}},
|
|
{Balance: 99999, WithdrawalAddress: []byte{'L'}},
|
|
{Balance: 99999, WithdrawalAddress: []byte{'M'}},
|
|
{Balance: 99999, WithdrawalAddress: []byte{'N'}},
|
|
{Balance: 99999, WithdrawalAddress: []byte{'O'}},
|
|
}
|
|
|
|
beaconChain.CrystallizedState().UpdateActiveValidators(activeValidators)
|
|
beaconChain.CrystallizedState().UpdateQueuedValidators(queuedValidators)
|
|
beaconChain.CrystallizedState().UpdateExitedValidators(exitedValidators)
|
|
|
|
if beaconChain.CrystallizedState().ActiveValidatorsLength() != 5 {
|
|
t.Errorf("Get active validator count failed, wanted 5, got %v", beaconChain.CrystallizedState().ActiveValidatorsLength())
|
|
}
|
|
if beaconChain.CrystallizedState().QueuedValidatorsLength() != 5 {
|
|
t.Errorf("Get queued validator count failed, wanted 5, got %v", beaconChain.CrystallizedState().QueuedValidatorsLength())
|
|
}
|
|
if beaconChain.CrystallizedState().ExitedValidatorsLength() != 5 {
|
|
t.Errorf("Get exited validator count failed, wanted 5, got %v", beaconChain.CrystallizedState().ExitedValidatorsLength())
|
|
}
|
|
|
|
newQueuedValidators, newActiveValidators, newExitedValidators := beaconChain.RotateValidatorSet()
|
|
|
|
if len(newActiveValidators) != 4 {
|
|
t.Errorf("Get active validator count failed, wanted 5, got %v", len(newActiveValidators))
|
|
}
|
|
if len(newQueuedValidators) != 4 {
|
|
t.Errorf("Get queued validator count failed, wanted 4, got %v", len(newQueuedValidators))
|
|
}
|
|
if len(newExitedValidators) != 7 {
|
|
t.Errorf("Get exited validator count failed, wanted 6, got %v", len(newExitedValidators))
|
|
}
|
|
}
|
|
|
|
func TestCutOffValidatorSet(t *testing.T) {
|
|
// Test scenario #1: Assume there's enough validators to fill in all the heights.
|
|
validatorCount := params.EpochLength * params.MinCommiteeSize
|
|
cutoffsValidators := GetCutoffs(validatorCount)
|
|
|
|
// The length of cutoff list should be 65. Since there is 64 heights per epoch,
|
|
// it means during every height, a new set of 128 validators will form a committee.
|
|
expectedCount := int(math.Ceil(float64(validatorCount)/params.MinCommiteeSize)) + 1
|
|
if len(cutoffsValidators) != expectedCount {
|
|
t.Errorf("Incorrect count for cutoffs validator. Wanted: %v, Got: %v", expectedCount, len(cutoffsValidators))
|
|
}
|
|
|
|
// Verify each cutoff is an increment of MinCommiteeSize, it means 128 validators forms a
|
|
// a committee and get to attest per height.
|
|
count := 0
|
|
for _, cutoff := range cutoffsValidators {
|
|
if cutoff != count {
|
|
t.Errorf("cutoffsValidators did not get 128 increment. Wanted: count, Got: %v", cutoff)
|
|
}
|
|
count += params.MinCommiteeSize
|
|
}
|
|
|
|
// Test scenario #2: Assume there's not enough validators to fill in all the heights.
|
|
validatorCount = 1000
|
|
cutoffsValidators = unique(GetCutoffs(validatorCount))
|
|
// With 1000 validators, we can't attest every height. Given min committee size is 128,
|
|
// we can only attest 7 heights. round down 1000 / 128 equals to 7, means the length is 8.
|
|
expectedCount = int(math.Ceil(float64(validatorCount) / params.MinCommiteeSize))
|
|
if len(unique(cutoffsValidators)) != expectedCount {
|
|
t.Errorf("Incorrect count for cutoffs validator. Wanted: %v, Got: %v", expectedCount, validatorCount/params.MinCommiteeSize)
|
|
}
|
|
|
|
// Verify each cutoff is an increment of 142~143 (1000 / 7).
|
|
count = 0
|
|
for _, cutoff := range cutoffsValidators {
|
|
num := count * validatorCount / (len(cutoffsValidators) - 1)
|
|
if cutoff != num {
|
|
t.Errorf("cutoffsValidators did not get correct increment. Wanted: %v, Got: %v", num, cutoff)
|
|
}
|
|
count++
|
|
}
|
|
}
|
|
|
|
func TestIsEpochTransition(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
if err := beaconChain.MutateCrystallizedState(types.NewCrystallizedState(&pb.CrystallizedStateResponse{CurrentEpoch: 1})); err != nil {
|
|
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
|
}
|
|
if !beaconChain.isEpochTransition(128) {
|
|
t.Errorf("there was supposed to be an epoch transition but there isn't one now")
|
|
}
|
|
if beaconChain.isEpochTransition(80) {
|
|
t.Errorf("there is not supposed to be an epoch transition but there is one now")
|
|
}
|
|
}
|
|
|
|
func TestHasVoted(t *testing.T) {
|
|
for i := 0; i < 8; i++ {
|
|
testfield := int(math.Pow(2, float64(i)))
|
|
bitfields := []byte{byte(testfield), 0, 0}
|
|
attesterBlock := 1
|
|
attesterFieldIndex := (8 - i)
|
|
|
|
voted := hasVoted(bitfields, attesterBlock, attesterFieldIndex)
|
|
if !voted {
|
|
t.Fatalf("attester was supposed to have voted but the test shows they have not, this is their bitfield and index: %b :%d", bitfields[0], attesterFieldIndex)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestApplyRewardAndPenalty(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
balance1 := uint64(10000)
|
|
balance2 := uint64(15000)
|
|
balance3 := uint64(20000)
|
|
balance4 := uint64(25000)
|
|
balance5 := uint64(30000)
|
|
|
|
activeValidators := []*pb.ValidatorRecord{
|
|
{Balance: balance1, WithdrawalAddress: []byte{'A'}, PublicKey: 0},
|
|
{Balance: balance2, WithdrawalAddress: []byte{'B'}, PublicKey: 1},
|
|
{Balance: balance3, WithdrawalAddress: []byte{'C'}, PublicKey: 2},
|
|
{Balance: balance4, WithdrawalAddress: []byte{'D'}, PublicKey: 3},
|
|
{Balance: balance5, WithdrawalAddress: []byte{'E'}, PublicKey: 4},
|
|
}
|
|
|
|
beaconChain.CrystallizedState().UpdateActiveValidators(activeValidators)
|
|
|
|
beaconChain.applyRewardAndPenalty(0, true)
|
|
beaconChain.applyRewardAndPenalty(1, false)
|
|
beaconChain.applyRewardAndPenalty(2, true)
|
|
beaconChain.applyRewardAndPenalty(3, false)
|
|
beaconChain.applyRewardAndPenalty(4, true)
|
|
|
|
expectedBalance1 := balance1 + params.AttesterReward
|
|
expectedBalance2 := balance2 - params.AttesterReward
|
|
expectedBalance3 := balance3 + params.AttesterReward
|
|
expectedBalance4 := balance4 - params.AttesterReward
|
|
expectedBalance5 := balance5 + params.AttesterReward
|
|
|
|
if expectedBalance1 != beaconChain.CrystallizedState().ActiveValidators()[0].Balance {
|
|
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance1, beaconChain.CrystallizedState().ActiveValidators()[0].Balance)
|
|
}
|
|
if expectedBalance2 != beaconChain.CrystallizedState().ActiveValidators()[1].Balance {
|
|
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance2, beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
|
|
}
|
|
if expectedBalance3 != beaconChain.CrystallizedState().ActiveValidators()[2].Balance {
|
|
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance3, beaconChain.CrystallizedState().ActiveValidators()[2].Balance)
|
|
}
|
|
if expectedBalance4 != beaconChain.CrystallizedState().ActiveValidators()[3].Balance {
|
|
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance4, beaconChain.CrystallizedState().ActiveValidators()[3].Balance)
|
|
}
|
|
if expectedBalance5 != beaconChain.CrystallizedState().ActiveValidators()[4].Balance {
|
|
t.Errorf("rewards and penalties were not able to be applied correctly:%d , %d", expectedBalance5, beaconChain.CrystallizedState().ActiveValidators()[4].Balance)
|
|
}
|
|
}
|
|
|
|
func TestResetAttesterBitfields(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
// Testing validator set sizes from 1 to 100.
|
|
for j := 1; j <= 100; j++ {
|
|
var validators []*pb.ValidatorRecord
|
|
|
|
for i := 0; i < j; i++ {
|
|
validator := &pb.ValidatorRecord{WithdrawalAddress: []byte{}, PublicKey: 0}
|
|
validators = append(validators, validator)
|
|
}
|
|
|
|
beaconChain.CrystallizedState().UpdateActiveValidators(validators)
|
|
|
|
testAttesterBitfield := []byte{2, 4, 6, 9}
|
|
if err := beaconChain.MutateActiveState(types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})); err != nil {
|
|
t.Fatal("unable to mutate active state")
|
|
}
|
|
if err := beaconChain.resetAttesterBitfields(); err != nil {
|
|
t.Fatalf("unable to reset Attester Bitfields")
|
|
}
|
|
if bytes.Equal(testAttesterBitfield, beaconChain.state.ActiveState.AttesterBitfield()) {
|
|
t.Fatalf("attester bitfields have not been able to be reset: %v", testAttesterBitfield)
|
|
}
|
|
|
|
bitfieldLength := j / 8
|
|
if j%8 != 0 {
|
|
bitfieldLength++
|
|
}
|
|
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfield(), make([]byte, bitfieldLength)) {
|
|
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfield())
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestResetTotalAttesterDeposit(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
active := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 10000})
|
|
if err := beaconChain.MutateActiveState(active); err != nil {
|
|
t.Fatalf("unable to Mutate Active state: %v", err)
|
|
}
|
|
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(10000) {
|
|
t.Fatalf("attester deposit was not saved: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
|
|
}
|
|
if err := beaconChain.resetTotalAttesterDeposit(); err != nil {
|
|
t.Fatalf("unable to reset total attester deposit: %v", err)
|
|
}
|
|
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(0) {
|
|
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
|
|
}
|
|
}
|
|
|
|
func TestUpdateJustifiedEpoch(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
data := &pb.CrystallizedStateResponse{CurrentEpoch: 5, LastJustifiedEpoch: 4, LastFinalizedEpoch: 3}
|
|
beaconChain.MutateCrystallizedState(types.NewCrystallizedState(data))
|
|
|
|
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(3) ||
|
|
beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(4) ||
|
|
beaconChain.state.CrystallizedState.CurrentEpoch() != uint64(5) {
|
|
t.Fatal("crystallized state unable to be saved")
|
|
}
|
|
|
|
if err := beaconChain.updateJustifiedEpoch(); err != nil {
|
|
t.Fatalf("unable to update justified epoch: %v", err)
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(5) {
|
|
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch())
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(4) {
|
|
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch())
|
|
}
|
|
|
|
data = &pb.CrystallizedStateResponse{CurrentEpoch: 8, LastJustifiedEpoch: 4, LastFinalizedEpoch: 3}
|
|
beaconChain.MutateCrystallizedState(types.NewCrystallizedState(data))
|
|
|
|
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(3) ||
|
|
beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(4) ||
|
|
beaconChain.state.CrystallizedState.CurrentEpoch() != uint64(8) {
|
|
t.Fatal("crystallized state unable to be saved")
|
|
}
|
|
|
|
if err := beaconChain.updateJustifiedEpoch(); err != nil {
|
|
t.Fatalf("unable to update justified epoch: %v", err)
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(8) {
|
|
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch())
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(3) {
|
|
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch())
|
|
}
|
|
}
|
|
|
|
func TestUpdateRewardsAndPenalties(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
var validators []*pb.ValidatorRecord
|
|
|
|
for i := 0; i < 40; i++ {
|
|
validator := &pb.ValidatorRecord{Balance: 1000, WithdrawalAddress: []byte{'A'}, PublicKey: 0}
|
|
validators = append(validators, validator)
|
|
}
|
|
beaconChain.state.CrystallizedState.UpdateActiveValidators(validators)
|
|
|
|
//Binary Representation of Bitfield: 00010110 00101011 00101110 01001111 01010000
|
|
testAttesterBitfield := []byte{22, 43, 46, 79, 80}
|
|
if err := beaconChain.MutateActiveState(types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})); err != nil {
|
|
t.Fatalf("unable to mutate active state: %v", err)
|
|
}
|
|
// Test Validator with index 10 would refer to the 11th bit in the bitfield
|
|
|
|
if err := beaconChain.updateRewardsAndPenalties(10); err != nil {
|
|
t.Fatalf("unable to update rewards and penalties: %v", err)
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[10].Balance != uint64(1001) {
|
|
t.Fatal("validator balance not updated")
|
|
}
|
|
|
|
// Test Validator with index 15 would refer to the 16th bit in the bitfield
|
|
if err := beaconChain.updateRewardsAndPenalties(15); err != nil {
|
|
t.Fatalf("unable to update rewards and penalties: %v", err)
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[15].Balance != uint64(1001) {
|
|
t.Fatal("validator balance not updated")
|
|
}
|
|
|
|
// Test Validator with index 23 would refer to the 24th bit in the bitfield
|
|
if err := beaconChain.updateRewardsAndPenalties(23); err != nil {
|
|
t.Fatalf("unable to update rewards and penalties: %v", err)
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[23].Balance == uint64(1001) {
|
|
t.Fatal("validator balance not updated")
|
|
}
|
|
|
|
err := beaconChain.updateRewardsAndPenalties(230)
|
|
if err == nil {
|
|
t.Fatal("no error displayed when there is supposed to be one")
|
|
|
|
}
|
|
if err.Error() != "attester index does not exist" {
|
|
t.Fatalf("incorrect error message: ` %s ` is displayed when it should have been: attester index does not exist", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestComputeValidatorRewardsAndPenalties(t *testing.T) {
|
|
beaconChain, db := startInMemoryBeaconChain(t)
|
|
defer db.Close()
|
|
|
|
var validators []*pb.ValidatorRecord
|
|
for i := 0; i < 40; i++ {
|
|
validator := &pb.ValidatorRecord{Balance: 1000, WithdrawalAddress: []byte{'A'}, PublicKey: 0}
|
|
validators = append(validators, validator)
|
|
}
|
|
|
|
data := &pb.CrystallizedStateResponse{
|
|
ActiveValidators: validators,
|
|
CurrentCheckPoint: []byte("checkpoint"),
|
|
TotalDeposits: 10000,
|
|
CurrentEpoch: 5,
|
|
LastJustifiedEpoch: 4,
|
|
LastFinalizedEpoch: 3,
|
|
}
|
|
if err := beaconChain.MutateCrystallizedState(types.NewCrystallizedState(data)); err != nil {
|
|
t.Fatalf("unable to mutate crystallizedstate: %v", err)
|
|
}
|
|
|
|
//Binary representation of bitfield: 11001000 10010100 10010010 10110011 00110001
|
|
testAttesterBitfield := []byte{200, 148, 146, 179, 49}
|
|
types.NewActiveState(&pb.ActiveStateResponse{AttesterBitfield: testAttesterBitfield})
|
|
ActiveState := types.NewActiveState(&pb.ActiveStateResponse{TotalAttesterDeposits: 8000, AttesterBitfield: testAttesterBitfield})
|
|
if err := beaconChain.MutateActiveState(ActiveState); err != nil {
|
|
t.Fatalf("unable to Mutate Active state: %v", err)
|
|
}
|
|
if err := beaconChain.computeValidatorRewardsAndPenalties(); err != nil {
|
|
t.Fatalf("could not compute validator rewards and penalties: %v", err)
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastJustifiedEpoch() != uint64(5) {
|
|
t.Fatalf("unable to update last justified epoch: %d", beaconChain.state.CrystallizedState.LastJustifiedEpoch())
|
|
}
|
|
if beaconChain.state.CrystallizedState.LastFinalizedEpoch() != uint64(4) {
|
|
t.Fatalf("unable to update last finalized epoch: %d", beaconChain.state.CrystallizedState.LastFinalizedEpoch())
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[0].Balance != uint64(1001) {
|
|
t.Fatalf("validator balance not updated: %d", beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[7].Balance != uint64(999) {
|
|
t.Fatalf("validator balance not updated: %d", beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
|
|
}
|
|
if beaconChain.CrystallizedState().ActiveValidators()[29].Balance != uint64(999) {
|
|
t.Fatalf("validator balance not updated: %d", beaconChain.CrystallizedState().ActiveValidators()[1].Balance)
|
|
}
|
|
if beaconChain.state.ActiveState.TotalAttesterDeposits() != uint64(0) {
|
|
t.Fatalf("attester deposit was not able to be reset: %d", beaconChain.state.ActiveState.TotalAttesterDeposits())
|
|
}
|
|
if !bytes.Equal(beaconChain.state.ActiveState.AttesterBitfield(), make([]byte, 5)) {
|
|
t.Fatalf("attester bitfields are not zeroed out: %v", beaconChain.state.ActiveState.AttesterBitfield())
|
|
}
|
|
}
|
|
|
|
// helper function to remove duplicates in a int slice.
|
|
func unique(ints []int) []int {
|
|
keys := make(map[int]bool)
|
|
list := []int{}
|
|
for _, int := range ints {
|
|
if _, value := keys[int]; !value {
|
|
keys[int] = true
|
|
list = append(list, int)
|
|
}
|
|
}
|
|
return list
|
|
|
|
}
|
|
|
|
// NewBlock is a helper method to create blocks with valid defaults.
|
|
// For a generic block, use NewBlock(t, nil)
|
|
func NewBlock(t *testing.T, b *pb.BeaconBlockResponse) *types.Block {
|
|
if b == nil {
|
|
b = &pb.BeaconBlockResponse{}
|
|
}
|
|
if b.ActiveStateHash == nil {
|
|
b.ActiveStateHash = make([]byte, 32)
|
|
}
|
|
if b.CrystallizedStateHash == nil {
|
|
b.CrystallizedStateHash = make([]byte, 32)
|
|
}
|
|
if b.ParentHash == nil {
|
|
b.ParentHash = make([]byte, 32)
|
|
}
|
|
|
|
blk, err := types.NewBlock(b)
|
|
if err != nil {
|
|
t.Fatalf("failed to instantiate block with slot number: %v", err)
|
|
}
|
|
|
|
return blk
|
|
}
|