mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-10 16:08:26 -05:00
beacon: Check State Hashes while Processing Incoming Blocks (#319)
This commit is contained in:
committed by
Raul Jordan
parent
d33836b48e
commit
c0b4503d5f
@@ -18,6 +18,7 @@ go_library(
|
||||
"@com_github_ethereum_go_ethereum//ethdb:go_default_library",
|
||||
"@com_github_ethereum_go_ethereum//rlp:go_default_library",
|
||||
"@com_github_sirupsen_logrus//:go_default_library",
|
||||
"@org_golang_x_crypto//blake2b:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package blockchain
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"hash"
|
||||
"math"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -15,6 +17,7 @@ import (
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/types"
|
||||
"github.com/prysmaticlabs/prysm/beacon-chain/utils"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/crypto/blake2b"
|
||||
)
|
||||
|
||||
var stateLookupKey = "beaconchainstate"
|
||||
@@ -107,6 +110,24 @@ func (b *BeaconChain) CanProcessBlock(fetcher powchain.POWBlockFetcher, block *t
|
||||
// Calculate the timestamp validity condition.
|
||||
slotDuration := time.Duration(block.Data().SlotNumber*params.SlotLength) * time.Second
|
||||
validTime := time.Now().After(b.GenesisBlock().Data().Timestamp.Add(slotDuration))
|
||||
|
||||
// Verify state hashes from the block are correct
|
||||
hash, err := hashActiveState(*b.ActiveState())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if !bytes.Equal(block.Data().ActiveStateHash.Sum(nil), hash.Sum(nil)) {
|
||||
return false, fmt.Errorf("Active state hash mismatched, wanted: %v, got: %v", hash.Sum(nil), block.Data().ActiveStateHash.Sum(nil))
|
||||
}
|
||||
hash, err = hashCrystallizedState(*b.CrystallizedState())
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if !bytes.Equal(block.Data().CrystallizedStateHash.Sum(nil), hash.Sum(nil)) {
|
||||
return false, fmt.Errorf("Crystallized state hash mismatched, wanted: %v, got: %v", hash.Sum(nil), block.Data().CrystallizedStateHash.Sum(nil))
|
||||
}
|
||||
|
||||
return validTime, nil
|
||||
}
|
||||
|
||||
@@ -144,6 +165,26 @@ func (b *BeaconChain) computeNewActiveState(seed common.Hash) (*types.ActiveStat
|
||||
}, nil
|
||||
}
|
||||
|
||||
// hashActiveState serializes the active state object then uses
|
||||
// blake2b to hash the serialized object.
|
||||
func hashActiveState(state types.ActiveState) (hash.Hash, error) {
|
||||
serializedState, err := rlp.EncodeToBytes(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blake2b.New256(serializedState)
|
||||
}
|
||||
|
||||
// hashCrystallizedState serializes the crystallized state object
|
||||
// then uses blake2b to hash the serialized object.
|
||||
func hashCrystallizedState(state types.CrystallizedState) (hash.Hash, error) {
|
||||
serializedState, err := rlp.EncodeToBytes(state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return blake2b.New256(serializedState)
|
||||
}
|
||||
|
||||
// getAttestersProposer returns lists of random sampled attesters and proposer indices.
|
||||
func (b *BeaconChain) getAttestersProposer(seed common.Hash) ([]int, int, error) {
|
||||
attesterCount := math.Min(params.AttesterCount, float64(len(b.CrystallizedState().ActiveValidators)))
|
||||
|
||||
@@ -195,7 +195,21 @@ func TestCanProcessBlock(t *testing.T) {
|
||||
if _, err := beaconChain.CanProcessBlock(&faultyFetcher{}, block); err == nil {
|
||||
t.Errorf("Using a faulty fetcher should throw an error, received nil")
|
||||
}
|
||||
activeState := &types.ActiveState{TotalAttesterDeposits: 10000}
|
||||
beaconChain.state.ActiveState = activeState
|
||||
|
||||
activeHash, err := hashActiveState(*activeState)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot hash active state: %v", err)
|
||||
}
|
||||
|
||||
block.InsertActiveHash(activeHash)
|
||||
|
||||
crystallizedHash, err := hashCrystallizedState(types.CrystallizedState{})
|
||||
if err != nil {
|
||||
t.Fatalf("Compute crystallized state hash failed: %v", err)
|
||||
}
|
||||
block.InsertCrystallizedHash(crystallizedHash)
|
||||
canProcess, err := beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err != nil {
|
||||
t.Fatalf("CanProcessBlocks failed: %v", err)
|
||||
@@ -207,6 +221,8 @@ func TestCanProcessBlock(t *testing.T) {
|
||||
// Attempting to try a block with that fails the timestamp validity
|
||||
// condition.
|
||||
block = types.NewBlock(1000000)
|
||||
block.InsertActiveHash(activeHash)
|
||||
block.InsertCrystallizedHash(crystallizedHash)
|
||||
canProcess, err = beaconChain.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err != nil {
|
||||
t.Fatalf("CanProcessBlocks failed: %v", err)
|
||||
@@ -215,3 +231,53 @@ func TestCanProcessBlock(t *testing.T) {
|
||||
t.Errorf("Should not be able to process block with invalid timestamp condition")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessBlockWithBadHashes(t *testing.T) {
|
||||
config := &database.BeaconDBConfig{DataDir: "", Name: "", InMemory: true}
|
||||
db, err := database.NewBeaconDB(config)
|
||||
if err != nil {
|
||||
t.Fatalf("unable to setup db: %v", err)
|
||||
}
|
||||
db.Start()
|
||||
b, err := NewBeaconChain(db.DB())
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to setup beacon chain: %v", err)
|
||||
}
|
||||
|
||||
// Test negative scenario where active state hash is different than node's compute
|
||||
block := types.NewBlock(1)
|
||||
activeState := &types.ActiveState{TotalAttesterDeposits: 10000}
|
||||
stateHash, err := hashActiveState(*activeState)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot hash active state: %v", err)
|
||||
}
|
||||
block.InsertActiveHash(stateHash)
|
||||
|
||||
b.state.ActiveState = &types.ActiveState{TotalAttesterDeposits: 9999}
|
||||
|
||||
canProcess, err := b.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err == nil {
|
||||
t.Fatalf("CanProcessBlocks should have failed with diff state hashes")
|
||||
}
|
||||
if canProcess {
|
||||
t.Errorf("CanProcessBlocks should have returned false")
|
||||
}
|
||||
|
||||
// Test negative scenario where crystallized state hash is different than node's compute
|
||||
crystallizedState := &types.CrystallizedState{CurrentEpoch: 10000}
|
||||
stateHash, err = hashCrystallizedState(*crystallizedState)
|
||||
if err != nil {
|
||||
t.Fatalf("Cannot hash crystallized state: %v", err)
|
||||
}
|
||||
block.InsertCrystallizedHash(stateHash)
|
||||
|
||||
b.state.CrystallizedState = &types.CrystallizedState{CurrentEpoch: 9999}
|
||||
|
||||
canProcess, err = b.CanProcessBlock(&mockFetcher{}, block)
|
||||
if err == nil {
|
||||
t.Fatalf("CanProcessBlocks should have failed with diff state hashes")
|
||||
}
|
||||
if canProcess {
|
||||
t.Errorf("CanProcessBlocks should have returned false")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"hash"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
@@ -31,15 +32,15 @@ func NewGenesisBlock() *Block {
|
||||
|
||||
// Data contains the fields in a beacon chain block.
|
||||
type Data struct {
|
||||
ParentHash common.Hash // ParentHash is the hash of the parent beacon block.
|
||||
ParentHash hash.Hash // ParentHash is the hash of the parent beacon block.
|
||||
SlotNumber uint64 // Slot number is the number a client should check to know when it creates block.
|
||||
RandaoReveal common.Hash // RandaoReveal is used for Randao commitment reveal.
|
||||
RandaoReveal hash.Hash // RandaoReveal is used for Randao commitment reveal.
|
||||
AttestationBitmask []byte // AttestationBitmask is the bit field of who from the attestation committee participated.
|
||||
AttestationAggregateSig []uint // AttestationAggregateSig is validator's aggregate sig.
|
||||
ShardAggregateVotes []AggregateVote // ShardAggregateVotes is shard aggregate votes.
|
||||
MainChainRef common.Hash // MainChainRef is the reference to main chain block.
|
||||
ActiveStateHash []byte // ActiveStateHash is the state that changes every block.
|
||||
CrystallizedStateHash []byte // CrystallizedStateHash is the state that changes every epoch.
|
||||
ActiveStateHash hash.Hash // ActiveStateHash is the state that changes every block.
|
||||
CrystallizedStateHash hash.Hash // CrystallizedStateHash is the state that changes every epoch.
|
||||
Timestamp time.Time
|
||||
}
|
||||
|
||||
@@ -50,3 +51,11 @@ type AggregateVote struct {
|
||||
SignerBitmask []byte // SignerBitmask is the bit mask of every validator that signed.
|
||||
AggregateSig []uint // AggregateSig is the aggregated signatures of individual shard.
|
||||
}
|
||||
|
||||
func (b *Block) InsertActiveHash(hash hash.Hash) {
|
||||
b.data.ActiveStateHash = hash
|
||||
}
|
||||
|
||||
func (b *Block) InsertCrystallizedHash(hash hash.Hash) {
|
||||
b.data.CrystallizedStateHash = hash
|
||||
}
|
||||
|
||||
@@ -12,32 +12,20 @@ type ActiveState struct {
|
||||
AttesterBitfields []byte // AttesterBitfields represents which validator has attested.
|
||||
}
|
||||
|
||||
// PartialCrosslinkRecord contains information about cross links
|
||||
// that are being put together during this epoch.
|
||||
type PartialCrosslinkRecord struct {
|
||||
ShardID uint16 // ShardID is the shard crosslink being made for.
|
||||
ShardBlockHash common.Hash // ShardBlockHash is the hash of the block.
|
||||
NotaryBitfield []byte // NotaryBitfield determines which notary has voted.
|
||||
AggregateSig []uint // AggregateSig is the aggregated signature of all the notaries who voted.
|
||||
}
|
||||
|
||||
// CrystallizedState contains fields of every epoch state,
|
||||
// it changes every epoch.
|
||||
type CrystallizedState struct {
|
||||
ActiveValidators []ValidatorRecord // ActiveValidators is the list of active validators.
|
||||
QueuedValidators []ValidatorRecord // QueuedValidators is the list of joined but not yet inducted validators.
|
||||
ExitedValidators []ValidatorRecord // ExitedValidators is the list of removed validators pending withdrawal.
|
||||
CurrentShuffling []uint16 // CurrentShuffling is hhe permutation of validators used to determine who cross-links what shard in this epoch.
|
||||
CurrentEpoch uint64 // CurrentEpoch is the current epoch.
|
||||
LastJustifiedEpoch uint64 // LastJustifiedEpoch is the last justified epoch.
|
||||
LastFinalizedEpoch uint64 // LastFinalizedEpoch is the last finalized epoch.
|
||||
Dynasty uint64 // Dynasty is the current dynasty.
|
||||
NextShard uint16 // NextShard is the next shard that cross-linking assignment will start from.
|
||||
CurrentCheckpoint common.Hash // CurrentCheckpoint is the current FFG checkpoint.
|
||||
CrosslinkRecords []CrosslinkRecord // CrosslinkRecords records about the most recent crosslink for each shard.
|
||||
TotalDeposits uint // TotalDeposits is the Total balance of deposits.
|
||||
CrosslinkSeed common.Hash // CrosslinkSeed is used to select the committees for each shard.
|
||||
CrosslinkSeedLastReset uint64 // CrosslinkSeedLastReset is the last epoch the crosslink seed was reset.
|
||||
ActiveValidators []ValidatorRecord // ActiveValidators is the list of active validators.
|
||||
QueuedValidators []ValidatorRecord // QueuedValidators is the list of joined but not yet inducted validators.
|
||||
ExitedValidators []ValidatorRecord // ExitedValidators is the list of removed validators pending withdrawal.
|
||||
CurrentShuffling []uint16 // CurrentShuffling is hhe permutation of validators used to determine who cross-links what shard in this epoch.
|
||||
CurrentEpoch uint64 // CurrentEpoch is the current epoch.
|
||||
LastJustifiedEpoch uint64 // LastJustifiedEpoch is the last justified epoch.
|
||||
LastFinalizedEpoch uint64 // LastFinalizedEpoch is the last finalized epoch.
|
||||
Dynasty uint64 // Dynasty is the current dynasty.
|
||||
NextShard uint16 // NextShard is the next shard that cross-linking assignment will start from.
|
||||
CurrentCheckpoint common.Hash // CurrentCheckpoint is the current FFG checkpoint.
|
||||
TotalDeposits uint // TotalDeposits is the Total balance of deposits.
|
||||
}
|
||||
|
||||
// ValidatorRecord contains information about a validator
|
||||
@@ -50,13 +38,6 @@ type ValidatorRecord struct {
|
||||
SwitchDynasty uint64 // SwitchDynasty is the dynasty where the validator can (be inducted | be removed | withdraw their balance).
|
||||
}
|
||||
|
||||
// CrosslinkRecord contains the fields of last fully formed
|
||||
// crosslink to be submitted into the chain.
|
||||
type CrosslinkRecord struct {
|
||||
Epoch uint64 // Epoch records the epoch the crosslink was submitted in.
|
||||
Hash common.Hash // Hash is the block hash.
|
||||
}
|
||||
|
||||
// NewGenesisStates initializes a beacon chain with starting parameters.
|
||||
func NewGenesisStates() (*ActiveState, *CrystallizedState) {
|
||||
active := &ActiveState{
|
||||
@@ -73,7 +54,6 @@ func NewGenesisStates() (*ActiveState, *CrystallizedState) {
|
||||
LastFinalizedEpoch: 0,
|
||||
Dynasty: 0,
|
||||
TotalDeposits: 0,
|
||||
CrosslinkSeed: common.BytesToHash([]byte{}),
|
||||
}
|
||||
return active, crystallized
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user