Skip to content

Commit

Permalink
feat: add validator blob signing (#12730)
Browse files Browse the repository at this point in the history
  • Loading branch information
terencechain authored and kasey committed Aug 23, 2023
1 parent f7c7f77 commit fe1b4c2
Show file tree
Hide file tree
Showing 6 changed files with 187 additions and 10 deletions.
4 changes: 4 additions & 0 deletions consensus-types/blocks/testing/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ func NewSignedBeaconBlockFromGeneric(gb *eth.GenericSignedBeaconBlock) (interfac
return blocks.NewSignedBeaconBlock(bb.Capella)
case *eth.GenericSignedBeaconBlock_BlindedCapella:
return blocks.NewSignedBeaconBlock(bb.BlindedCapella)
case *eth.GenericSignedBeaconBlock_Deneb:
return blocks.NewSignedBeaconBlock(bb.Deneb.Block)
case *eth.GenericSignedBeaconBlock_BlindedDeneb:
return blocks.NewSignedBeaconBlock(bb.BlindedDeneb.Block)
// Generic Signed Beacon Block Deneb can't be used here as it is not a block, but block content with blobs
default:
return nil, errors.Wrapf(blocks.ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", gb)
Expand Down
3 changes: 3 additions & 0 deletions validator/client/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ go_library(
"aggregate.go",
"attest.go",
"attest_protect.go",
"blob.go",
"key_reload.go",
"log.go",
"metrics.go",
Expand Down Expand Up @@ -101,6 +102,7 @@ go_test(
"aggregate_test.go",
"attest_protect_test.go",
"attest_test.go",
"blob_test.go",
"key_reload_test.go",
"metrics_test.go",
"propose_protect_test.go",
Expand Down Expand Up @@ -138,6 +140,7 @@ go_test(
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//runtime:go_default_library",
"//runtime/version:go_default_library",
"//testing/assert:go_default_library",
"//testing/mock:go_default_library",
"//testing/require:go_default_library",
Expand Down
39 changes: 39 additions & 0 deletions validator/client/blob.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package client

import (
"context"

"github.com/pkg/errors"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v4/time/slots"
)

func (v *validator) signBlob(ctx context.Context, blob *ethpb.BlobSidecar, pubKey [fieldparams.BLSPubkeyLength]byte) ([]byte, error) {
epoch := slots.ToEpoch(blob.Slot)
domain, err := v.domainData(ctx, epoch, params.BeaconConfig().DomainBlobSidecar[:])
if err != nil {
return nil, errors.Wrap(err, domainDataErr)
}
if domain == nil {
return nil, errors.New(domainDataErr)
}
sr, err := signing.ComputeSigningRoot(blob, domain.SignatureDomain)
if err != nil {
return nil, errors.Wrap(err, signingRootErr)
}
sig, err := v.keyManager.Sign(ctx, &validatorpb.SignRequest{
PublicKey: pubKey[:],
SigningRoot: sr[:],
SignatureDomain: domain.SignatureDomain,
Object: &validatorpb.SignRequest_Blob{Blob: blob},
SigningSlot: blob.Slot,
})
if err != nil {
return nil, errors.Wrap(err, "could not sign block proposal")
}
return sig.Marshal(), nil
}
51 changes: 51 additions & 0 deletions validator/client/blob_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package client

import (
"context"
"testing"

"github.com/golang/mock/gomock"
"github.com/prysmaticlabs/prysm/v4/beacon-chain/core/signing"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/config/params"
"github.com/prysmaticlabs/prysm/v4/crypto/bls"
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
"github.com/prysmaticlabs/prysm/v4/testing/require"
)

func Test_validator_signBlob(t *testing.T) {
v, m, vk, finish := setup(t)
defer finish()

m.validatorClient.EXPECT().
DomainData(gomock.Any(), // ctx
&ethpb.DomainRequest{
Domain: params.BeaconConfig().DomainBlobSidecar[:],
}). // epoch
Return(&ethpb.DomainResponse{
SignatureDomain: bytesutil.PadTo([]byte("signatureDomain"), 32),
}, nil)

blob := &ethpb.BlobSidecar{
BlockRoot: bytesutil.PadTo([]byte("blockRoot"), 32),
Index: 1,
Slot: 2,
BlockParentRoot: bytesutil.PadTo([]byte("blockParentRoot"), 32),
ProposerIndex: 3,
Blob: bytesutil.PadTo([]byte("blob"), fieldparams.BlobLength),
KzgCommitment: bytesutil.PadTo([]byte("kzgCommitment"), 48),
KzgProof: bytesutil.PadTo([]byte("kzgPRoof"), 48),
}
ctx := context.Background()
sig, err := v.signBlob(ctx, blob, [48]byte(vk.PublicKey().Marshal()))
require.NoError(t, err)
pb, err := bls.PublicKeyFromBytes(vk.PublicKey().Marshal())
require.NoError(t, err)
signature, err := bls.SignatureFromBytes(sig)
require.NoError(t, err)
sr, err := signing.ComputeSigningRoot(blob, bytesutil.PadTo([]byte("signatureDomain"), 32))
require.NoError(t, err)

require.Equal(t, true, signature.Verify(pb, sr[:]))
}
46 changes: 38 additions & 8 deletions validator/client/propose.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,16 +121,46 @@ func (v *validator) ProposeBlock(ctx context.Context, slot primitives.Slot, pubK
return
}

// Propose and broadcast block via beacon node
proposal, err := blk.PbGenericBlock()
if err != nil {
log.WithError(err).Error("Failed to create proposal request")
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
var genericSignedBlock *ethpb.GenericSignedBeaconBlock
if blk.Version() >= version.Deneb && !blk.IsBlinded() {
signedBlobs := make([]*ethpb.SignedBlobSidecar, len(b.GetDeneb().Blobs))
for _, blob := range b.GetDeneb().Blobs {
blobSig, err := v.signBlob(ctx, blob, pubKey)
if err != nil {
log.WithError(err).Error("Failed to sign blob")
return
}
signedBlobs = append(signedBlobs, &ethpb.SignedBlobSidecar{
Message: blob,
Signature: blobSig,
})
}
denebBlock, err := blk.PbDenebBlock()
if err != nil {
log.WithError(err).Error("Failed to get deneb block")
return
}
genericSignedBlock = &ethpb.GenericSignedBeaconBlock{
Block: &ethpb.GenericSignedBeaconBlock_Deneb{
Deneb: &ethpb.SignedBeaconBlockAndBlobsDeneb{
Block: denebBlock,
Blobs: signedBlobs,
},
},
}
} else {
// Propose and broadcast block via beacon node
genericSignedBlock, err = blk.PbGenericBlock()
if err != nil {
log.WithError(err).Error("Failed to create proposal request")
if v.emitAccountMetrics {
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
}
return
}
return
}
blkResp, err := v.validatorClient.ProposeBeaconBlock(ctx, proposal)

blkResp, err := v.validatorClient.ProposeBeaconBlock(ctx, genericSignedBlock)
if err != nil {
log.WithField("blockSlot", slot).WithError(err).Error("Failed to propose block")
if v.emitAccountMetrics {
Expand Down
54 changes: 52 additions & 2 deletions validator/client/propose_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v4/runtime/version"
"github.com/prysmaticlabs/prysm/v4/testing/assert"
"github.com/prysmaticlabs/prysm/v4/testing/require"
"github.com/prysmaticlabs/prysm/v4/testing/util"
Expand Down Expand Up @@ -508,8 +509,9 @@ func TestProposeBlock_BroadcastsBlock_WithGraffiti(t *testing.T) {

func testProposeBlock(t *testing.T, graffiti []byte) {
tests := []struct {
name string
block *ethpb.GenericBeaconBlock
name string
block *ethpb.GenericBeaconBlock
version int
}{
{
name: "phase0",
Expand Down Expand Up @@ -583,6 +585,43 @@ func testProposeBlock(t *testing.T, graffiti []byte) {
},
},
},
{
name: "deneb block and blobs",
version: version.Deneb,
block: &ethpb.GenericBeaconBlock{
Block: &ethpb.GenericBeaconBlock_Deneb{
Deneb: func() *ethpb.BeaconBlockAndBlobsDeneb {
blk := util.NewBeaconBlockDeneb()
blk.Block.Body.Graffiti = graffiti
return &ethpb.BeaconBlockAndBlobsDeneb{
Block: blk.Block,
Blobs: []*ethpb.BlobSidecar{
{
BlockRoot: bytesutil.PadTo([]byte("blockRoot"), 32),
Index: 1,
Slot: 2,
BlockParentRoot: bytesutil.PadTo([]byte("blockParentRoot"), 32),
ProposerIndex: 3,
Blob: bytesutil.PadTo([]byte("blob"), fieldparams.BlobLength),
KzgCommitment: bytesutil.PadTo([]byte("kzgCommitment"), 48),
KzgProof: bytesutil.PadTo([]byte("kzgPRoof"), 48),
},
{
BlockRoot: bytesutil.PadTo([]byte("blockRoot1"), 32),
Index: 4,
Slot: 5,
BlockParentRoot: bytesutil.PadTo([]byte("blockParentRoot1"), 32),
ProposerIndex: 6,
Blob: bytesutil.PadTo([]byte("blob1"), fieldparams.BlobLength),
KzgCommitment: bytesutil.PadTo([]byte("kzgCommitment1"), 48),
KzgProof: bytesutil.PadTo([]byte("kzgPRoof1"), 48),
},
},
}
}(),
},
},
},
}

for _, tt := range tests {
Expand Down Expand Up @@ -617,6 +656,17 @@ func testProposeBlock(t *testing.T, graffiti []byte) {
var sentBlock interfaces.ReadOnlySignedBeaconBlock
var err error

if tt.version == version.Deneb {
m.validatorClient.EXPECT().DomainData(
gomock.Any(), // ctx
gomock.Any(), // epoch
).Return(&ethpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
m.validatorClient.EXPECT().DomainData(
gomock.Any(), // ctx
gomock.Any(), // epoch
).Return(&ethpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
}

m.validatorClient.EXPECT().ProposeBeaconBlock(
gomock.Any(), // ctx
gomock.AssignableToTypeOf(&ethpb.GenericSignedBeaconBlock{}),
Expand Down

0 comments on commit fe1b4c2

Please sign in to comment.