Skip to content

Commit

Permalink
combine ProcessOperations signature verifications
Browse files Browse the repository at this point in the history
  • Loading branch information
shotasilagadzetaal committed Oct 17, 2024
1 parent 48f3d6c commit d88e0cf
Show file tree
Hide file tree
Showing 3 changed files with 161 additions and 124 deletions.
94 changes: 4 additions & 90 deletions cl/transition/impl/eth2/operations.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
package eth2

import (
"bytes"
"errors"
"fmt"
"slices"
Expand All @@ -42,6 +41,10 @@ import (
"github.com/erigontech/erigon/cl/utils"
)

func (I *impl) FullValidate() bool {
return I.FullValidation
}

func (I *impl) ProcessProposerSlashing(
s abstract.BeaconState,
propSlashing *cltypes.ProposerSlashing,
Expand Down Expand Up @@ -73,33 +76,6 @@ func (I *impl) ProcessProposerSlashing(
return fmt.Errorf("proposer is not slashable: %v", proposer)
}

for _, signedHeader := range []*cltypes.SignedBeaconBlockHeader{propSlashing.Header1, propSlashing.Header2} {
domain, err := s.GetDomain(
s.BeaconConfig().DomainBeaconProposer,
state.GetEpochAtSlot(s.BeaconConfig(), signedHeader.Header.Slot),
)
if err != nil {
return fmt.Errorf("unable to get domain: %v", err)
}
signingRoot, err := fork.ComputeSigningRoot(signedHeader.Header, domain)
if err != nil {
return fmt.Errorf("unable to compute signing root: %v", err)
}
pk := proposer.PublicKey()
valid, err := bls.Verify(signedHeader.Signature[:], signingRoot[:], pk[:])
if err != nil {
return fmt.Errorf("unable to verify signature: %v", err)
}
if !valid {
return fmt.Errorf(
"invalid signature: signature %v, root %v, pubkey %v",
signedHeader.Signature[:],
signingRoot[:],
pk,
)
}
}

// Set whistleblower index to 0 so current proposer gets reward.
pr, err := s.SlashValidator(h1.ProposerIndex, nil)
if I.BlockRewardsCollector != nil {
Expand Down Expand Up @@ -264,35 +240,7 @@ func (I *impl) ProcessVoluntaryExit(
if err != nil {
return err
}
validator, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex))
if err != nil {
return err
}

// We can skip it in some instances if we want to optimistically sync up.
if I.FullValidation {
var domain []byte
if s.Version() < clparams.DenebVersion {
domain, err = s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
} else if s.Version() >= clparams.DenebVersion {
domain, err = fork.ComputeDomain(s.BeaconConfig().DomainVoluntaryExit[:], utils.Uint32ToBytes4(uint32(s.BeaconConfig().CapellaForkVersion)), s.GenesisValidatorsRoot())
}
if err != nil {
return err
}
signingRoot, err := fork.ComputeSigningRoot(voluntaryExit, domain)
if err != nil {
return err
}
pk := validator.PublicKey()
valid, err := bls.Verify(signedVoluntaryExit.Signature[:], signingRoot[:], pk[:])
if err != nil {
return err
}
if !valid {
return errors.New("ProcessVoluntaryExit: BLS verification failed")
}
}
// Do the exit (same process in slashing).
return s.InitiateValidatorExit(voluntaryExit.ValidatorIndex)
}
Expand Down Expand Up @@ -477,7 +425,6 @@ func (I *impl) ProcessBlsToExecutionChange(
signedChange *cltypes.SignedBLSToExecutionChange,
) error {
change := signedChange.Message

beaconConfig := s.BeaconConfig()
validator, err := s.ValidatorForValidatorIndex(int(change.ValidatorIndex))
if err != nil {
Expand All @@ -486,39 +433,6 @@ func (I *impl) ProcessBlsToExecutionChange(

// Perform full validation if requested.
wc := validator.WithdrawalCredentials()
if I.FullValidation {
// Check the validator's withdrawal credentials prefix.
if wc[0] != byte(beaconConfig.BLSWithdrawalPrefixByte) {
return errors.New("invalid withdrawal credentials prefix")
}

// Check the validator's withdrawal credentials against the provided message.
hashedFrom := utils.Sha256(change.From[:])
if !bytes.Equal(hashedFrom[1:], wc[1:]) {
return errors.New("invalid withdrawal credentials")
}

// Compute the signing domain and verify the message signature.
domain, err := fork.ComputeDomain(
beaconConfig.DomainBLSToExecutionChange[:],
utils.Uint32ToBytes4(uint32(beaconConfig.GenesisForkVersion)),
s.GenesisValidatorsRoot(),
)
if err != nil {
return err
}
signedRoot, err := fork.ComputeSigningRoot(change, domain)
if err != nil {
return err
}
valid, err := bls.Verify(signedChange.Signature[:], signedRoot[:], change.From[:])
if err != nil {
return err
}
if !valid {
return errors.New("invalid signature")
}
}
credentials := wc
// Reset the validator's withdrawal credentials.
credentials[0] = byte(beaconConfig.ETH1AddressWithdrawalPrefixByte)
Expand Down
190 changes: 156 additions & 34 deletions cl/transition/machine/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,14 @@
package machine

import (
"bytes"
"fmt"

"github.com/Giulio2002/bls"
"github.com/erigontech/erigon/cl/abstract"
"github.com/erigontech/erigon/cl/fork"
"github.com/erigontech/erigon/cl/phase1/core/state"
"github.com/erigontech/erigon/cl/utils"
"github.com/pkg/errors"

"github.com/erigontech/erigon/cl/clparams"
Expand Down Expand Up @@ -79,10 +83,23 @@ func ProcessBlock(impl BlockProcessor, s abstract.BeaconState, block cltypes.Gen
if err := impl.ProcessEth1Data(s, body.GetEth1Data()); err != nil {
return fmt.Errorf("processBlock: failed to process Eth1 data: %v", err)
}

// Process block body operations.
if err := ProcessOperations(impl, s, body); err != nil {
signatures, messages, pubKeys, err := ProcessOperations(impl, s, body)
if err != nil {
return fmt.Errorf("processBlock: failed to process block body operations: %v", err)

}

// process signature validation
valid, err := bls.VerifyMultipleSignatures(signatures, messages, pubKeys)
if err != nil {
return err
}
if !valid {
return errors.New("block signature validation failed")
}

// Process sync aggregate in case of Altair version.
if version >= clparams.AltairVersion {
if err := impl.ProcessSyncAggregate(s, body.GetSyncAggregate()); err != nil {
Expand All @@ -94,33 +111,21 @@ func ProcessBlock(impl BlockProcessor, s abstract.BeaconState, block cltypes.Gen
}

// ProcessOperations is called by ProcessBlock and prcesses the block body operations
func ProcessOperations(impl BlockOperationProcessor, s abstract.BeaconState, blockBody cltypes.GenericBeaconBody) error {
func ProcessOperations(impl BlockOperationProcessor, s abstract.BeaconState, blockBody cltypes.GenericBeaconBody) (signatures [][]byte, messages [][]byte, publicKeys [][]byte, err error) {
if blockBody.GetDeposits().Len() != int(maximumDeposits(s)) {
return errors.New("outstanding deposits do not match maximum deposits")
}
// Process each proposer slashing
var err error
if err := solid.RangeErr[*cltypes.ProposerSlashing](blockBody.GetProposerSlashings(), func(index int, slashing *cltypes.ProposerSlashing, length int) error {
if err = impl.ProcessProposerSlashing(s, slashing); err != nil {
return fmt.Errorf("ProcessProposerSlashing: %s", err)
}
return nil
}); err != nil {
return err
return nil, nil, nil, errors.New("outstanding deposits do not match maximum deposits")
}

if err := solid.RangeErr[*cltypes.AttesterSlashing](blockBody.GetAttesterSlashings(), func(index int, slashing *cltypes.AttesterSlashing, length int) error {
if err = impl.ProcessAttesterSlashing(s, slashing); err != nil {
return fmt.Errorf("ProcessAttesterSlashing: %s", err)
}
return nil
}); err != nil {
return err
// Process each proposer slashing
sigs, msgs, pubKeys, err := processProposerSlashings(impl, s, blockBody)
if err != nil {
return
}
signatures, messages, publicKeys = append(signatures, sigs...), append(messages, msgs...), append(publicKeys, pubKeys...)

// Process each attestations
if err := impl.ProcessAttestations(s, blockBody.GetAttestations()); err != nil {
return fmt.Errorf("ProcessAttestation: %s", err)
return nil, nil, nil, fmt.Errorf("ProcessAttestation: %s", err)
}

// Process each deposit
Expand All @@ -130,31 +135,148 @@ func ProcessOperations(impl BlockOperationProcessor, s abstract.BeaconState, blo
}
return nil
}); err != nil {
return err
return nil, nil, nil, err
}

// Process each voluntary exit.
if err := solid.RangeErr[*cltypes.SignedVoluntaryExit](blockBody.GetVoluntaryExits(), func(index int, exit *cltypes.SignedVoluntaryExit, length int) error {
sigs, msgs, pubKeys, err = processVoluntaryExits(impl, s, blockBody)
if err != nil {
return nil, nil, nil, err
}
signatures, messages, publicKeys = append(signatures, sigs...), append(messages, msgs...), append(publicKeys, pubKeys...)

if s.Version() < clparams.CapellaVersion {
return
}

// Process each execution change. this will only have entries after the capella fork.
sigs, msgs, pubKeys, err = processBlsToExecutionChanges(impl, s, blockBody)
if err != nil {
return nil, nil, nil, err
}
signatures, messages, publicKeys = append(signatures, sigs...), append(messages, msgs...), append(publicKeys, pubKeys...)

return
}

func processProposerSlashings(impl BlockOperationProcessor, s abstract.BeaconState, blockBody cltypes.GenericBeaconBody) (sigs [][]byte, msgs [][]byte, pubKeys [][]byte, err error) {
// Process each proposer slashing
err = solid.RangeErr[*cltypes.ProposerSlashing](blockBody.GetProposerSlashings(), func(index int, propSlashing *cltypes.ProposerSlashing, length int) error {
for _, signedHeader := range []*cltypes.SignedBeaconBlockHeader{propSlashing.Header1, propSlashing.Header2} {
proposer, err := s.ValidatorForValidatorIndex(int(propSlashing.Header1.Header.ProposerIndex))
if err != nil {
return err
}

domain, err := s.GetDomain(
s.BeaconConfig().DomainBeaconProposer,
state.GetEpochAtSlot(s.BeaconConfig(), signedHeader.Header.Slot),
)
if err != nil {
return fmt.Errorf("unable to get domain: %v", err)
}
signingRoot, err := fork.ComputeSigningRoot(signedHeader.Header, domain)
if err != nil {
return fmt.Errorf("unable to compute signing root: %v", err)
}
pk := proposer.PublicKey()
sigs, msgs, pubKeys = append(sigs, signedHeader.Signature[:]), append(msgs, signingRoot[:]), append(pubKeys, pk[:])
}

if err = impl.ProcessProposerSlashing(s, propSlashing); err != nil {
return fmt.Errorf("ProcessProposerSlashing: %s", err)
}
return nil
})

return
}

func processVoluntaryExits(impl BlockOperationProcessor, s abstract.BeaconState, blockBody cltypes.GenericBeaconBody) (sigs [][]byte, msgs [][]byte, pubKeys [][]byte, err error) {
// Process each voluntary exit.
err = solid.RangeErr[*cltypes.SignedVoluntaryExit](blockBody.GetVoluntaryExits(), func(index int, exit *cltypes.SignedVoluntaryExit, length int) error {
voluntaryExit := exit.VoluntaryExit
validator, err := s.ValidatorForValidatorIndex(int(voluntaryExit.ValidatorIndex))
if err != nil {
return err
}

// We can skip it in some instances if we want to optimistically sync up.
if impl.FullValidate() {
var domain []byte
if s.Version() < clparams.DenebVersion {
domain, err = s.GetDomain(s.BeaconConfig().DomainVoluntaryExit, voluntaryExit.Epoch)
} else if s.Version() >= clparams.DenebVersion {
domain, err = fork.ComputeDomain(s.BeaconConfig().DomainVoluntaryExit[:], utils.Uint32ToBytes4(uint32(s.BeaconConfig().CapellaForkVersion)), s.GenesisValidatorsRoot())
}
if err != nil {
return err
}
signingRoot, err := fork.ComputeSigningRoot(voluntaryExit, domain)
if err != nil {
return err
}
pk := validator.PublicKey()
sigs, msgs, pubKeys = append(sigs, exit.Signature[:]), append(msgs, signingRoot[:]), append(pubKeys, pk[:])
}

if err = impl.ProcessVoluntaryExit(s, exit); err != nil {
return fmt.Errorf("ProcessVoluntaryExit: %s", err)
}
return nil
}); err != nil {
return err
}
if s.Version() < clparams.CapellaVersion {
return nil
}
})

return
}

func processBlsToExecutionChanges(impl BlockOperationProcessor, s abstract.BeaconState, blockBody cltypes.GenericBeaconBody) (sigs [][]byte, msgs [][]byte, pubKeys [][]byte, err error) {
// Process each execution change. this will only have entries after the capella fork.
if err := solid.RangeErr[*cltypes.SignedBLSToExecutionChange](blockBody.GetExecutionChanges(), func(index int, addressChange *cltypes.SignedBLSToExecutionChange, length int) error {
err = solid.RangeErr[*cltypes.SignedBLSToExecutionChange](blockBody.GetExecutionChanges(), func(index int, addressChange *cltypes.SignedBLSToExecutionChange, length int) error {
change := addressChange.Message

beaconConfig := s.BeaconConfig()
validator, err := s.ValidatorForValidatorIndex(int(change.ValidatorIndex))
if err != nil {
return err
}

// Perform full validation if requested.
wc := validator.WithdrawalCredentials()
if impl.FullValidate() {
// Check the validator's withdrawal credentials prefix.
if wc[0] != byte(beaconConfig.BLSWithdrawalPrefixByte) {
return errors.New("invalid withdrawal credentials prefix")
}

// Check the validator's withdrawal credentials against the provided message.
hashedFrom := utils.Sha256(change.From[:])
if !bytes.Equal(hashedFrom[1:], wc[1:]) {
return errors.New("invalid withdrawal credentials")
}

// Compute the signing domain and verify the message signature.
domain, err := fork.ComputeDomain(
beaconConfig.DomainBLSToExecutionChange[:],
utils.Uint32ToBytes4(uint32(beaconConfig.GenesisForkVersion)),
s.GenesisValidatorsRoot(),
)
if err != nil {
return err
}
signedRoot, err := fork.ComputeSigningRoot(change, domain)
if err != nil {
return err
}
sigs, msgs, pubKeys = append(sigs, addressChange.Signature[:]), append(msgs, signedRoot[:]), append(pubKeys, change.From[:])
}

if err := impl.ProcessBlsToExecutionChange(s, addressChange); err != nil {
return fmt.Errorf("ProcessBlsToExecutionChange: %s", err)
}
return nil
}); err != nil {
return err
}
return nil
})

return
}

func maximumDeposits(s abstract.BeaconState) (maxDeposits uint64) {
Expand Down
1 change: 1 addition & 0 deletions cl/transition/machine/machine.go
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,5 @@ type BlockOperationProcessor interface {
ProcessDeposit(s abstract.BeaconState, deposit *cltypes.Deposit) error
ProcessVoluntaryExit(s abstract.BeaconState, signedVoluntaryExit *cltypes.SignedVoluntaryExit) error
ProcessBlsToExecutionChange(state abstract.BeaconState, signedChange *cltypes.SignedBLSToExecutionChange) error
FullValidate() bool
}

0 comments on commit d88e0cf

Please sign in to comment.