only viable head is invalid (#11117)

* failing onBlock syncing

* passing merge check

* failing signature verification

* still failing block signature

* mock full bellatrix blocks

* working unit test

* return error from FCU if head fails to update

* move bellatrix block generator

* remove bellatrix signature function

* Add liveness unit tests

* revert removal of sync_aggregate.go

* gaz

* Terence's suggestion

Co-authored-by: terencechain <terence@prysmaticlabs.com>

* go fmt

* Nishant's suggestion

Co-authored-by: Nishant Das <nishdas93@gmail.com>

* Fix build

Co-authored-by: terencechain <terence@prysmaticlabs.com>
Co-authored-by: Nishant Das <nishdas93@gmail.com>
This commit is contained in:
Potuz
2022-08-02 11:55:05 -03:00
committed by GitHub
parent c1f89cc4c8
commit 4b46dead2f
11 changed files with 1368 additions and 75 deletions

View File

@@ -1,6 +1,7 @@
package blockchain
import (
"bytes"
"context"
"time"
@@ -299,13 +300,14 @@ func (s *Service) ForkChoicer() forkchoice.ForkChoicer {
// IsOptimistic returns true if the current head is optimistic.
func (s *Service) IsOptimistic(ctx context.Context) (bool, error) {
s.headLock.RLock()
defer s.headLock.RUnlock()
if slots.ToEpoch(s.CurrentSlot()) < params.BeaconConfig().BellatrixForkEpoch {
return false, nil
}
s.headLock.RLock()
headRoot := s.head.root
s.headLock.RUnlock()
return s.IsOptimisticForRoot(ctx, s.head.root)
return s.IsOptimisticForRoot(ctx, headRoot)
}
// IsFinalized returns true if the input root is finalized.
@@ -332,7 +334,17 @@ func (s *Service) IsOptimisticForRoot(ctx context.Context, root [32]byte) (bool,
return false, err
}
if ss == nil {
return false, errInvalidNilSummary
// if the requested root is the headroot we should treat the
// node as optimistic. This can happen if we pruned INVALID
// nodes and no viable head is available.
headRoot, err := s.HeadRoot(ctx)
if err != nil {
return true, err
}
if bytes.Equal(headRoot, root[:]) {
return true, nil
}
return true, errInvalidNilSummary
}
validatedCheckpoint, err := s.cfg.BeaconDB.LastValidatedCheckpoint(ctx)

View File

@@ -106,8 +106,12 @@ func (s *Service) notifyForkchoiceUpdate(ctx context.Context, arg *notifyForkcho
r, err := s.cfg.ForkChoiceStore.Head(ctx, s.justifiedBalances.balances)
if err != nil {
log.WithError(err).Error("Could not get head root")
return nil, nil
log.WithFields(logrus.Fields{
"slot": headBlk.Slot(),
"blockRoot": fmt.Sprintf("%#x", bytesutil.Trunc(headRoot[:])),
"invalidCount": len(invalidRoots),
}).Warn("Pruned invalid blocks, could not update head root")
return nil, invalidBlock{error: ErrInvalidPayload, root: arg.headRoot, invalidAncestorRoots: invalidRoots}
}
b, err := s.getBlock(ctx, r)
if err != nil {

File diff suppressed because it is too large Load Diff

View File

@@ -2,8 +2,8 @@ package altair
import (
"context"
"errors"
"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
p2pType "github.com/prysmaticlabs/prysm/beacon-chain/p2p/types"
@@ -47,11 +47,11 @@ import (
func ProcessSyncAggregate(ctx context.Context, s state.BeaconState, sync *ethpb.SyncAggregate) (state.BeaconState, error) {
votedKeys, votedIndices, didntVoteIndices, err := FilterSyncCommitteeVotes(s, sync)
if err != nil {
return nil, err
return nil, errors.Wrap(err, "could not filter sync committee votes")
}
if err := VerifySyncCommitteeSig(s, votedKeys, sync.SyncCommitteeSignature); err != nil {
return nil, err
return nil, errors.Wrap(err, "could not verify sync committee signature")
}
return ApplySyncRewardsPenalties(ctx, s, votedIndices, didntVoteIndices)

View File

@@ -99,7 +99,7 @@ func TestExecuteBellatrixStateTransitionNoVerify_FullProcess(t *testing.T) {
block.Block.StateRoot = stateRoot[:]
c := beaconState.Copy()
sig, err := util.BlockSignatureBellatrix(c, block.Block, privKeys)
sig, err := util.BlockSignature(c, block.Block, privKeys)
require.NoError(t, err)
block.Signature = sig.Marshal()
@@ -187,7 +187,7 @@ func TestExecuteBellatrixStateTransitionNoVerifySignature_CouldNotVerifyStateRoo
block.Block.StateRoot = stateRoot[:]
c := beaconState.Copy()
sig, err := util.BlockSignatureBellatrix(c, block.Block, privKeys)
sig, err := util.BlockSignature(c, block.Block, privKeys)
require.NoError(t, err)
block.Signature = sig.Marshal()

View File

@@ -194,6 +194,7 @@ func TestProcessBlock_AllEventsTrackedVals(t *testing.T) {
genConfig := util.DefaultBlockGenConfig()
genConfig.NumProposerSlashings = 1
genConfig.FullSyncAggregate = true
b, err := util.GenerateFullBlockAltair(genesis, keys, genConfig, 1)
require.NoError(t, err)
s := setupService(t)

View File

@@ -311,7 +311,7 @@ func BlockSignatureAltair(
return privKeys[proposerIdx].Sign(blockRoot[:]), nil
}
// GenerateFullBlockAltair generates a fully valid block with the requested parameters.
// GenerateFullBlockAltair generates a fully valid Altair block with the requested parameters.
// Use BlockGenConfig to declare the conditions you would like the block generated under.
func GenerateFullBlockAltair(
bState state.BeaconState,
@@ -388,26 +388,44 @@ func GenerateFullBlockAltair(
return nil, err
}
var newSyncAggregate *ethpb.SyncAggregate
if conf.FullSyncAggregate {
newSyncAggregate, err = generateSyncAggregate(bState, privs, parentRoot)
if err != nil {
return nil, errors.Wrap(err, "failed generating syncAggregate")
}
} else {
var syncCommitteeBits []byte
currSize := new(ethpb.SyncAggregate).SyncCommitteeBits.Len()
switch currSize {
case 512:
syncCommitteeBits = bitfield.NewBitvector512()
case 32:
syncCommitteeBits = bitfield.NewBitvector32()
default:
return nil, errors.New("invalid bit vector size")
}
newSyncAggregate = &ethpb.SyncAggregate{
SyncCommitteeBits: syncCommitteeBits,
SyncCommitteeSignature: append([]byte{0xC0}, make([]byte, 95)...),
}
}
if slot == currentSlot {
slot = currentSlot + 1
}
syncAgg, err := generateSyncAggregate(bState, privs, parentRoot)
stCopy := bState.Copy()
stCopy, err = transition.ProcessSlots(context.Background(), stCopy, slot)
if err != nil {
return nil, err
}
reveal, err := RandaoReveal(stCopy, time.CurrentEpoch(stCopy), privs)
if err != nil {
return nil, err
}
// Temporarily incrementing the beacon state slot here since BeaconProposerIndex is a
// function deterministic on beacon state slot.
if err := bState.SetSlot(slot); err != nil {
return nil, err
}
reveal, err := RandaoReveal(bState, time.CurrentEpoch(bState), privs)
if err != nil {
return nil, err
}
idx, err := helpers.BeaconProposerIndex(ctx, bState)
idx, err := helpers.BeaconProposerIndex(ctx, stCopy)
if err != nil {
return nil, err
}
@@ -424,15 +442,12 @@ func GenerateFullBlockAltair(
Attestations: atts,
VoluntaryExits: exits,
Deposits: newDeposits,
Graffiti: make([]byte, 32),
SyncAggregate: syncAgg,
Graffiti: make([]byte, fieldparams.RootLength),
SyncAggregate: newSyncAggregate,
},
}
if err := bState.SetSlot(currentSlot); err != nil {
return nil, err
}
signature, err := BlockSignatureAltair(bState, block, privs)
signature, err := BlockSignature(bState, block, privs)
if err != nil {
return nil, err
}

View File

@@ -13,6 +13,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/state"
v1 "github.com/prysmaticlabs/prysm/beacon-chain/state/v1"
v2 "github.com/prysmaticlabs/prysm/beacon-chain/state/v2"
v3 "github.com/prysmaticlabs/prysm/beacon-chain/state/v3"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
@@ -89,6 +90,16 @@ func GenerateAttestations(
return nil, err
}
headState = genState
case version.Bellatrix:
pbState, err := v3.ProtobufBeaconState(bState.CloneInnerState())
if err != nil {
return nil, err
}
genState, err := v3.InitializeFromProtoUnsafe(pbState)
if err != nil {
return nil, err
}
headState = genState
default:
return nil, errors.New("state type isn't supported")
}

View File

@@ -2,54 +2,203 @@ package util
import (
"context"
"encoding/binary"
"fmt"
"github.com/pkg/errors"
"github.com/prysmaticlabs/go-bitfield"
"github.com/prysmaticlabs/prysm/beacon-chain/core/helpers"
"github.com/prysmaticlabs/prysm/beacon-chain/core/signing"
"github.com/prysmaticlabs/prysm/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
fieldparams "github.com/prysmaticlabs/prysm/config/fieldparams"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/crypto/bls"
"github.com/prysmaticlabs/prysm/crypto/hash"
"github.com/prysmaticlabs/prysm/encoding/bytesutil"
enginev1 "github.com/prysmaticlabs/prysm/proto/engine/v1"
ethpb "github.com/prysmaticlabs/prysm/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/time/slots"
)
// BlockSignatureBellatrix calculates the post-state root of the block and returns the signature.
func BlockSignatureBellatrix(
// GenerateFullBlockBellatrix generates a fully valid Bellatrix block with the requested parameters.
// Use BlockGenConfig to declare the conditions you would like the block generated under.
// This function modifies the passed state as follows:
func GenerateFullBlockBellatrix(
bState state.BeaconState,
block *ethpb.BeaconBlockBellatrix,
privKeys []bls.SecretKey,
) (bls.Signature, error) {
var err error
wsb, err := wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: block})
if err != nil {
return nil, err
}
s, err := transition.CalculateStateRoot(context.Background(), bState, wsb)
if err != nil {
return nil, err
}
block.StateRoot = s[:]
domain, err := signing.Domain(bState.Fork(), time.CurrentEpoch(bState), params.BeaconConfig().DomainBeaconProposer, bState.GenesisValidatorsRoot())
if err != nil {
return nil, err
}
blockRoot, err := signing.ComputeSigningRoot(block, domain)
if err != nil {
return nil, err
}
// Temporarily increasing the beacon state slot here since BeaconProposerIndex is a
// function deterministic on beacon state slot.
privs []bls.SecretKey,
conf *BlockGenConfig,
slot types.Slot,
) (*ethpb.SignedBeaconBlockBellatrix, error) {
ctx := context.Background()
currentSlot := bState.Slot()
if err := bState.SetSlot(block.Slot); err != nil {
return nil, err
if currentSlot > slot {
return nil, fmt.Errorf("current slot in state is larger than given slot. %d > %d", currentSlot, slot)
}
proposerIdx, err := helpers.BeaconProposerIndex(context.Background(), bState)
bState = bState.Copy()
if conf == nil {
conf = &BlockGenConfig{}
}
var err error
var pSlashings []*ethpb.ProposerSlashing
numToGen := conf.NumProposerSlashings
if numToGen > 0 {
pSlashings, err = generateProposerSlashings(bState, privs, numToGen)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d proposer slashings:", numToGen)
}
}
numToGen = conf.NumAttesterSlashings
var aSlashings []*ethpb.AttesterSlashing
if numToGen > 0 {
aSlashings, err = generateAttesterSlashings(bState, privs, numToGen)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen)
}
}
numToGen = conf.NumAttestations
var atts []*ethpb.Attestation
if numToGen > 0 {
atts, err = GenerateAttestations(bState, privs, numToGen, slot, false)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attestations:", numToGen)
}
}
numToGen = conf.NumDeposits
var newDeposits []*ethpb.Deposit
eth1Data := bState.Eth1Data()
if numToGen > 0 {
newDeposits, eth1Data, err = generateDepositsAndEth1Data(bState, numToGen)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d deposits:", numToGen)
}
}
numToGen = conf.NumVoluntaryExits
var exits []*ethpb.SignedVoluntaryExit
if numToGen > 0 {
exits, err = generateVoluntaryExits(bState, privs, numToGen)
if err != nil {
return nil, errors.Wrapf(err, "failed generating %d attester slashings:", numToGen)
}
}
numToGen = conf.NumTransactions
newTransactions := make([][]byte, numToGen)
for i := uint64(0); i < numToGen; i++ {
newTransactions[i] = bytesutil.Uint64ToBytesLittleEndian(i)
}
random, err := helpers.RandaoMix(bState, time.CurrentEpoch(bState))
if err != nil {
return nil, errors.Wrap(err, "could not process randao mix")
}
timestamp, err := slots.ToTime(bState.GenesisTime(), slot)
if err != nil {
return nil, errors.Wrap(err, "could not get current timestamp")
}
stCopy := bState.Copy()
stCopy, err = transition.ProcessSlots(context.Background(), stCopy, slot)
if err != nil {
return nil, err
}
if err := bState.SetSlot(currentSlot); err != nil {
parentExecution, err := stCopy.LatestExecutionPayloadHeader()
if err != nil {
return nil, err
}
return privKeys[proposerIdx].Sign(blockRoot[:]), nil
blockHash := indexToHash(uint64(slot))
newExecutionPayload := &enginev1.ExecutionPayload{
ParentHash: parentExecution.BlockHash,
FeeRecipient: make([]byte, 20),
StateRoot: params.BeaconConfig().ZeroHash[:],
ReceiptsRoot: params.BeaconConfig().ZeroHash[:],
LogsBloom: make([]byte, 256),
PrevRandao: random,
BlockNumber: uint64(slot),
ExtraData: params.BeaconConfig().ZeroHash[:],
BaseFeePerGas: params.BeaconConfig().ZeroHash[:],
BlockHash: blockHash[:],
Timestamp: uint64(timestamp.Unix()),
Transactions: newTransactions,
}
var syncCommitteeBits []byte
currSize := new(ethpb.SyncAggregate).SyncCommitteeBits.Len()
switch currSize {
case 512:
syncCommitteeBits = bitfield.NewBitvector512()
case 32:
syncCommitteeBits = bitfield.NewBitvector32()
default:
return nil, errors.New("invalid bit vector size")
}
newSyncAggregate := &ethpb.SyncAggregate{
SyncCommitteeBits: syncCommitteeBits,
SyncCommitteeSignature: append([]byte{0xC0}, make([]byte, 95)...),
}
newHeader := bState.LatestBlockHeader()
prevStateRoot, err := bState.HashTreeRoot(ctx)
if err != nil {
return nil, errors.Wrap(err, "could not hash state")
}
newHeader.StateRoot = prevStateRoot[:]
parentRoot, err := newHeader.HashTreeRoot()
if err != nil {
return nil, errors.Wrap(err, "could not hash the new header")
}
if slot == currentSlot {
slot = currentSlot + 1
}
reveal, err := RandaoReveal(stCopy, time.CurrentEpoch(stCopy), privs)
if err != nil {
return nil, errors.Wrap(err, "could not compute randao reveal")
}
idx, err := helpers.BeaconProposerIndex(ctx, stCopy)
if err != nil {
return nil, errors.Wrap(err, "could not compute beacon proposer index")
}
block := &ethpb.BeaconBlockBellatrix{
Slot: slot,
ParentRoot: parentRoot[:],
ProposerIndex: idx,
Body: &ethpb.BeaconBlockBodyBellatrix{
Eth1Data: eth1Data,
RandaoReveal: reveal,
ProposerSlashings: pSlashings,
AttesterSlashings: aSlashings,
Attestations: atts,
VoluntaryExits: exits,
Deposits: newDeposits,
Graffiti: make([]byte, fieldparams.RootLength),
SyncAggregate: newSyncAggregate,
ExecutionPayload: newExecutionPayload,
},
}
// The fork can change after processing the state
signature, err := BlockSignature(bState, block, privs)
if err != nil {
return nil, errors.Wrap(err, "could not compute block signature")
}
return &ethpb.SignedBeaconBlockBellatrix{Block: block, Signature: signature.Marshal()}, nil
}
func indexToHash(i uint64) [32]byte {
var b [8]byte
binary.LittleEndian.PutUint64(b[:], i)
return hash.Hash(b[:])
}

View File

@@ -34,6 +34,8 @@ type BlockGenConfig struct {
NumAttestations uint64
NumDeposits uint64
NumVoluntaryExits uint64
NumTransactions uint64 // Only for post Bellatrix blocks
FullSyncAggregate bool
}
// DefaultBlockGenConfig returns the block config that utilizes the
@@ -45,6 +47,7 @@ func DefaultBlockGenConfig() *BlockGenConfig {
NumAttestations: 1,
NumDeposits: 0,
NumVoluntaryExits: 0,
NumTransactions: 0,
}
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/prysmaticlabs/prysm/beacon-chain/core/transition"
"github.com/prysmaticlabs/prysm/beacon-chain/state"
"github.com/prysmaticlabs/prysm/config/params"
"github.com/prysmaticlabs/prysm/consensus-types/interfaces"
types "github.com/prysmaticlabs/prysm/consensus-types/primitives"
"github.com/prysmaticlabs/prysm/consensus-types/wrapper"
"github.com/prysmaticlabs/prysm/crypto/bls"
@@ -37,39 +38,80 @@ func RandaoReveal(beaconState state.ReadOnlyBeaconState, epoch types.Epoch, priv
// BlockSignature calculates the post-state root of the block and returns the signature.
func BlockSignature(
bState state.BeaconState,
block *ethpb.BeaconBlock,
block interface{},
privKeys []bls.SecretKey,
) (bls.Signature, error) {
wsb, err := wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlock{Block: block})
var wsb interfaces.SignedBeaconBlock
var err error
// copy the state since we need to process slots
bState = bState.Copy()
switch b := block.(type) {
case *ethpb.BeaconBlock:
wsb, err = wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlock{Block: b})
case *ethpb.BeaconBlockAltair:
wsb, err = wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlockAltair{Block: b})
case *ethpb.BeaconBlockBellatrix:
wsb, err = wrapper.WrappedSignedBeaconBlock(&ethpb.SignedBeaconBlockBellatrix{Block: b})
default:
return nil, errors.New("unsupported block type")
}
if err != nil {
return nil, errors.Wrap(err, "could not wrap block")
}
s, err := transition.CalculateStateRoot(context.Background(), bState, wsb)
if err != nil {
return nil, errors.Wrap(err, "could not calculate state root")
}
switch b := block.(type) {
case *ethpb.BeaconBlock:
b.StateRoot = s[:]
case *ethpb.BeaconBlockAltair:
b.StateRoot = s[:]
case *ethpb.BeaconBlockBellatrix:
b.StateRoot = s[:]
}
// Temporarily increasing the beacon state slot here since BeaconProposerIndex is a
// function deterministic on beacon state slot.
var blockSlot types.Slot
switch b := block.(type) {
case *ethpb.BeaconBlock:
blockSlot = b.Slot
case *ethpb.BeaconBlockAltair:
blockSlot = b.Slot
case *ethpb.BeaconBlockBellatrix:
blockSlot = b.Slot
}
// process slots to get the right fork
bState, err = transition.ProcessSlots(context.Background(), bState, blockSlot)
if err != nil {
return nil, err
}
block.StateRoot = s[:]
domain, err := signing.Domain(bState.Fork(), time.CurrentEpoch(bState), params.BeaconConfig().DomainBeaconProposer, bState.GenesisValidatorsRoot())
if err != nil {
return nil, err
}
blockRoot, err := signing.ComputeSigningRoot(block, domain)
var blockRoot [32]byte
switch b := block.(type) {
case *ethpb.BeaconBlock:
blockRoot, err = signing.ComputeSigningRoot(b, domain)
case *ethpb.BeaconBlockAltair:
blockRoot, err = signing.ComputeSigningRoot(b, domain)
case *ethpb.BeaconBlockBellatrix:
blockRoot, err = signing.ComputeSigningRoot(b, domain)
}
if err != nil {
return nil, err
}
// Temporarily increasing the beacon state slot here since BeaconProposerIndex is a
// function deterministic on beacon state slot.
currentSlot := bState.Slot()
if err := bState.SetSlot(block.Slot); err != nil {
return nil, err
}
proposerIdx, err := helpers.BeaconProposerIndex(context.Background(), bState)
if err != nil {
return nil, err
}
if err := bState.SetSlot(currentSlot); err != nil {
return nil, err
}
return privKeys[proposerIdx].Sign(blockRoot[:]), nil
}