Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Identify invalid signature within batch verification (#11582) #11741

Merged
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 9 additions & 11 deletions beacon-chain/blockchain/process_block.go
Original file line number Diff line number Diff line change
Expand Up @@ -337,11 +337,7 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac

jCheckpoints := make([]*ethpb.Checkpoint, len(blks))
fCheckpoints := make([]*ethpb.Checkpoint, len(blks))
sigSet := &bls.SignatureBatch{
Signatures: [][]byte{},
PublicKeys: []bls.PublicKey{},
Messages: [][32]byte{},
}
sigSet := bls.NewSet()
type versionAndHeader struct {
version int
header interfaces.ExecutionData
Expand Down Expand Up @@ -381,7 +377,13 @@ func (s *Service) onBlockBatch(ctx context.Context, blks []interfaces.SignedBeac
}
sigSet.Join(set)
}
verify, err := sigSet.Verify()

var verify bool
if features.Get().EnableVerboseSigVerification {
verify, err = sigSet.VerifyVerbosely()
} else {
verify, err = sigSet.Verify()
}
if err != nil {
return invalidBlock{error: err}
}
Expand Down Expand Up @@ -529,11 +531,7 @@ func (s *Service) insertBlockToForkchoiceStore(ctx context.Context, blk interfac
}
}

if err := s.cfg.ForkChoiceStore.InsertNode(ctx, st, root); err != nil {
return err
}

return nil
return s.cfg.ForkChoiceStore.InsertNode(ctx, st, root)
}

// This feeds in the attestations included in the block to fork choice store. It's allows fork choice store
Expand Down
23 changes: 14 additions & 9 deletions beacon-chain/core/blocks/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import (
)

// retrieves the signature batch from the raw data, public key,signature and domain provided.
func signatureBatch(signedData, pub, signature, domain []byte) (*bls.SignatureBatch, error) {
func signatureBatch(signedData, pub, signature, domain []byte, desc string) (*bls.SignatureBatch, error) {
publicKey, err := bls.PublicKeyFromBytes(pub)
if err != nil {
return nil, errors.Wrap(err, "could not convert bytes to public key")
Expand All @@ -33,15 +33,16 @@ func signatureBatch(signedData, pub, signature, domain []byte) (*bls.SignatureBa
return nil, errors.Wrap(err, "could not hash container")
}
return &bls.SignatureBatch{
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Descriptions: []string{desc},
}, nil
}

// verifies the signature from the raw data, public key and domain provided.
func verifySignature(signedData, pub, signature, domain []byte) error {
set, err := signatureBatch(signedData, pub, signature, domain)
set, err := signatureBatch(signedData, pub, signature, domain, bls.UnknownSignature)
if err != nil {
return err
}
Expand Down Expand Up @@ -146,7 +147,7 @@ func RandaoSignatureBatch(
if err != nil {
return nil, err
}
set, err := signatureBatch(buf, proposerPub, reveal, domain)
set, err := signatureBatch(buf, proposerPub, reveal, domain, bls.RandaoSignature)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -186,6 +187,7 @@ func createAttestationSignatureBatch(
sigs := make([][]byte, len(atts))
pks := make([]bls.PublicKey, len(atts))
msgs := make([][32]byte, len(atts))
descs := make([]string, len(atts))
for i, a := range atts {
sigs[i] = a.Signature
c, err := helpers.BeaconCommitteeFromState(ctx, beaconState, a.Data.Slot, a.Data.CommitteeIndex)
Expand Down Expand Up @@ -216,11 +218,14 @@ func createAttestationSignatureBatch(
return nil, errors.Wrap(err, "could not get signing root of object")
}
msgs[i] = root

descs[i] = bls.AttestationSignature
}
return &bls.SignatureBatch{
Signatures: sigs,
PublicKeys: pks,
Messages: msgs,
Signatures: sigs,
PublicKeys: pks,
Messages: msgs,
Descriptions: descs,
}, nil
}

Expand Down
8 changes: 5 additions & 3 deletions beacon-chain/core/blocks/withdrawals.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,9 +160,10 @@ func BLSChangesSignatureBatch(
return bls.NewSet(), nil
}
batch := &bls.SignatureBatch{
Signatures: make([][]byte, len(changes)),
PublicKeys: make([]bls.PublicKey, len(changes)),
Messages: make([][32]byte, len(changes)),
Signatures: make([][]byte, len(changes)),
PublicKeys: make([]bls.PublicKey, len(changes)),
Messages: make([][32]byte, len(changes)),
Descriptions: make([]string, len(changes)),
}
epoch := slots.ToEpoch(st.Slot())
domain, err := signing.Domain(st.Fork(), epoch, params.BeaconConfig().DomainBLSToExecutionChange, st.GenesisValidatorsRoot())
Expand All @@ -181,6 +182,7 @@ func BLSChangesSignatureBatch(
return nil, errors.Wrap(err, "could not compute BLSToExecutionChange signing data")
}
batch.Messages[i] = htr
batch.Descriptions[i] = bls.BlsChangeSignature
}
return batch, nil
}
8 changes: 5 additions & 3 deletions beacon-chain/core/signing/signing_root.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,12 @@ func BlockSignatureBatch(pub, signature, domain []byte, rootFunc func() ([32]byt
if err != nil {
return nil, errors.Wrap(err, "could not compute signing root")
}
desc := bls.BlockSignature
return &bls.SignatureBatch{
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Signatures: [][]byte{signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Descriptions: []string{desc},
}, nil
}

Expand Down
1 change: 1 addition & 0 deletions beacon-chain/core/transition/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ go_library(
"//beacon-chain/state/stateutil:go_default_library",
"//config/fieldparams:go_default_library",
"//config/params:go_default_library",
"//config/features:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
Expand Down
9 changes: 8 additions & 1 deletion beacon-chain/core/transition/transition.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/execution"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/core/time"
"github.com/prysmaticlabs/prysm/v3/beacon-chain/state"
"github.com/prysmaticlabs/prysm/v3/config/features"
"github.com/prysmaticlabs/prysm/v3/config/params"
"github.com/prysmaticlabs/prysm/v3/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v3/consensus-types/interfaces"
Expand Down Expand Up @@ -66,7 +67,13 @@ func ExecuteStateTransition(
if err != nil {
return nil, errors.Wrap(err, "could not execute state transition")
}
valid, err := set.Verify()

var valid bool
if features.Get().EnableVerboseSigVerification {
valid, err = set.VerifyVerbosely()
} else {
valid, err = set.Verify()
}
if err != nil {
return nil, errors.Wrap(err, "could not batch verify signature")
}
Expand Down
12 changes: 12 additions & 0 deletions beacon-chain/core/transition/transition_no_verify_sig_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,18 @@ func TestProcessBlockNoVerifyAnySigAltair_OK(t *testing.T) {
require.Equal(t, true, verified, "Could not verify signature set")
}

func TestProcessBlockNoVerify_SigSetContainsDescriptions(t *testing.T) {
beaconState, block, _, _, _ := createFullBlockWithOperations(t)
wsb, err := blocks.NewSignedBeaconBlock(block)
require.NoError(t, err)
set, _, err := transition.ProcessBlockNoVerifyAnySig(context.Background(), beaconState, wsb)
require.NoError(t, err)
assert.Equal(t, len(set.Signatures), len(set.Descriptions), "Signatures and descriptions do not match up")
assert.Equal(t, "block signature", set.Descriptions[0])
assert.Equal(t, "randao signature", set.Descriptions[1])
assert.Equal(t, "attestation signature", set.Descriptions[2])
}

func TestProcessOperationsNoVerifyAttsSigs_OK(t *testing.T) {
beaconState, block := createFullAltairBlockWithOperations(t)
wsb, err := blocks.NewSignedBeaconBlock(block)
Expand Down
14 changes: 8 additions & 6 deletions beacon-chain/sync/batch_verifier_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,16 @@ func TestValidateWithBatchVerifier(t *testing.T) {
sig := keys[0].Sign(make([]byte, 32))
badSig := keys[1].Sign(make([]byte, 32))
validSet := &bls.SignatureBatch{
Messages: [][32]byte{{}},
PublicKeys: []bls.PublicKey{keys[0].PublicKey()},
Signatures: [][]byte{sig.Marshal()},
Messages: [][32]byte{{}},
PublicKeys: []bls.PublicKey{keys[0].PublicKey()},
Signatures: [][]byte{sig.Marshal()},
Descriptions: []string{bls.UnknownSignature},
}
invalidSet := &bls.SignatureBatch{
Messages: [][32]byte{{}},
PublicKeys: []bls.PublicKey{keys[0].PublicKey()},
Signatures: [][]byte{badSig.Marshal()},
Messages: [][32]byte{{}},
PublicKeys: []bls.PublicKey{keys[0].PublicKey()},
Signatures: [][]byte{badSig.Marshal()},
Descriptions: []string{bls.UnknownSignature},
}
tests := []struct {
name string
Expand Down
14 changes: 8 additions & 6 deletions beacon-chain/sync/validate_aggregate_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -299,9 +299,10 @@ func validateSelectionIndex(
return nil, err
}
return &bls.SignatureBatch{
Signatures: [][]byte{proof},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Signatures: [][]byte{proof},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Descriptions: []string{bls.SelectionProof},
}, nil
}

Expand All @@ -326,8 +327,9 @@ func aggSigSet(s state.ReadOnlyBeaconState, a *ethpb.SignedAggregateAttestationA
return nil, err
}
return &bls.SignatureBatch{
Signatures: [][]byte{a.Signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Signatures: [][]byte{a.Signature},
PublicKeys: []bls.PublicKey{publicKey},
Messages: [][32]byte{root},
Descriptions: []string{bls.AggregatorSignature},
}, nil
}
7 changes: 4 additions & 3 deletions beacon-chain/sync/validate_sync_committee_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -243,9 +243,10 @@ func (s *Service) rejectInvalidSyncCommitteeSignature(m *ethpb.SyncCommitteeMess
// the signature to a G2 point if batch verification is
// enabled.
set := &bls.SignatureBatch{
Messages: [][32]byte{sigRoot},
PublicKeys: []bls.PublicKey{pKey},
Signatures: [][]byte{m.Signature},
Messages: [][32]byte{sigRoot},
PublicKeys: []bls.PublicKey{pKey},
Signatures: [][]byte{m.Signature},
Descriptions: []string{bls.SyncCommitteeSignature},
}
return s.validateWithBatchVerifier(ctx, "sync committee message", set)
}
Expand Down
21 changes: 12 additions & 9 deletions beacon-chain/sync/validate_sync_contribution_proof.go
Original file line number Diff line number Diff line change
Expand Up @@ -236,9 +236,10 @@ func (s *Service) rejectInvalidContributionSignature(m *ethpb.SignedContribution
return pubsub.ValidationReject, err
}
set := &bls.SignatureBatch{
Messages: [][32]byte{root},
PublicKeys: []bls.PublicKey{publicKey},
Signatures: [][]byte{m.Signature},
Messages: [][32]byte{root},
PublicKeys: []bls.PublicKey{publicKey},
Signatures: [][]byte{m.Signature},
Descriptions: []string{bls.ContributionSignature},
}
return s.validateWithBatchVerifier(ctx, "sync contribution signature", set)
}
Expand Down Expand Up @@ -292,9 +293,10 @@ func (s *Service) rejectInvalidSyncAggregateSignature(m *ethpb.SignedContributio
return pubsub.ValidationIgnore, err
}
set := &bls.SignatureBatch{
Messages: [][32]byte{sigRoot},
PublicKeys: []bls.PublicKey{aggKey},
Signatures: [][]byte{m.Message.Contribution.Signature},
Messages: [][32]byte{sigRoot},
PublicKeys: []bls.PublicKey{aggKey},
Signatures: [][]byte{m.Message.Contribution.Signature},
Descriptions: []string{bls.SyncAggregateSignature},
}
return s.validateWithBatchVerifier(ctx, "sync contribution aggregate signature", set)
}
Expand Down Expand Up @@ -403,9 +405,10 @@ func (s *Service) verifySyncSelectionData(ctx context.Context, m *ethpb.Contribu
return err
}
set := &bls.SignatureBatch{
Messages: [][32]byte{root},
PublicKeys: []bls.PublicKey{publicKey},
Signatures: [][]byte{m.SelectionProof},
Messages: [][32]byte{root},
PublicKeys: []bls.PublicKey{publicKey},
Signatures: [][]byte{m.SelectionProof},
Descriptions: []string{bls.SyncSelectionProof},
}
valid, err := s.validateWithBatchVerifier(ctx, "sync contribution selection signature", set)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions config/features/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ type Flags struct {

DisableStakinContractCheck bool // Disables check for deposit contract when proposing blocks

EnableVerboseSigVerification bool // EnableVerboseSigVerification specifies whether to verify individual signature if batch verification fails

// KeystoreImportDebounceInterval specifies the time duration the validator waits to reload new keys if they have
// changed on disk. This feature is for advanced use cases only.
KeystoreImportDebounceInterval time.Duration
Expand Down Expand Up @@ -257,6 +259,10 @@ func ConfigureBeaconChain(ctx *cli.Context) error {
logEnabled(enableFullSSZDataLogging)
cfg.EnableFullSSZDataLogging = true
}
if ctx.IsSet(enableVerboseSigVerification.Name) {
logEnabled(enableVerboseSigVerification)
cfg.EnableVerboseSigVerification = true
}
Init(cfg)
return nil
}
Expand Down
5 changes: 5 additions & 0 deletions config/features/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ var (
Name: "enable-beacon-rest-api",
Usage: "Experimental enable of the beacon REST API when querying a beacon node",
}
enableVerboseSigVerification = &cli.BoolFlag{
Name: "enable-verbose-sig-verification",
Usage: "Enables identifying invalid signatures if batch verification fails when processing block",
}
)

// devModeFlags holds list of flags that are set when development mode is on.
Expand Down Expand Up @@ -178,6 +182,7 @@ var BeaconChainFlags = append(deprecatedBeaconFlags, append(deprecatedFlags, []c
enableStartupOptimistic,
disableDefensivePull,
enableFullSSZDataLogging,
enableVerboseSigVerification,
}...)...)

// E2EBeaconChainFlags contains a list of the beacon chain feature flags to be tested in E2E.
Expand Down
5 changes: 5 additions & 0 deletions crypto/bls/bls.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,11 @@ func AggregateCompressedSignatures(multiSigs [][]byte) (common.Signature, error)
return blst.AggregateCompressedSignatures(multiSigs)
}

// VerifySignature verifies a single signature. For performance reason, always use VerifyMultipleSignatures if possible.
func VerifySignature(sig []byte, msg [32]byte, pubKey common.PublicKey) (bool, error) {
return blst.VerifySignature(sig, msg, pubKey)
}

// VerifyMultipleSignatures verifies multiple signatures for distinct messages securely.
func VerifyMultipleSignatures(sigs [][]byte, msgs [][32]byte, pubKeys []common.PublicKey) (bool, error) {
return blst.VerifyMultipleSignatures(sigs, msgs, pubKeys)
Expand Down
9 changes: 9 additions & 0 deletions crypto/bls/blst/signature.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,6 +196,15 @@ func AggregateSignatures(sigs []common.Signature) common.Signature {
return &Signature{s: signature.ToAffine()}
}

// VerifySignature verifies a single signature using public key and message.
func VerifySignature(sig []byte, msg [32]byte, pubKey common.PublicKey) (bool, error) {
rSig, err := SignatureFromBytes(sig)
if err != nil {
return false, err
}
return rSig.Verify(pubKey, msg[:]), nil
}

// VerifyMultipleSignatures verifies a non-singular set of signatures and its respective pubkeys and messages.
// This method provides a safe way to verify multiple signatures at once. We pick a number randomly from 1 to max
// uint64 and then multiply the signature by it. We continue doing this for all signatures and its respective pubkeys.
Expand Down
Loading