mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-08 23:18:15 -05:00
feat: add validator blob signing (#12730)
This commit is contained in:
committed by
Preston Van Loon
parent
6314f7fcbf
commit
96a9a6fc16
@@ -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)
|
||||
|
||||
@@ -6,6 +6,7 @@ go_library(
|
||||
"aggregate.go",
|
||||
"attest.go",
|
||||
"attest_protect.go",
|
||||
"blob.go",
|
||||
"key_reload.go",
|
||||
"log.go",
|
||||
"metrics.go",
|
||||
@@ -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",
|
||||
@@ -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",
|
||||
|
||||
39
validator/client/blob.go
Normal file
39
validator/client/blob.go
Normal file
@@ -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
validator/client/blob_test.go
Normal file
51
validator/client/blob_test.go
Normal file
@@ -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
|
||||
ðpb.DomainRequest{
|
||||
Domain: params.BeaconConfig().DomainBlobSidecar[:],
|
||||
}). // epoch
|
||||
Return(ðpb.DomainResponse{
|
||||
SignatureDomain: bytesutil.PadTo([]byte("signatureDomain"), 32),
|
||||
}, nil)
|
||||
|
||||
blob := ðpb.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[:]))
|
||||
}
|
||||
@@ -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, ðpb.SignedBlobSidecar{
|
||||
Message: blob,
|
||||
Signature: blobSig,
|
||||
})
|
||||
}
|
||||
denebBlock, err := blk.PbDenebBlock()
|
||||
if err != nil {
|
||||
log.WithError(err).Error("Failed to get deneb block")
|
||||
return
|
||||
}
|
||||
genericSignedBlock = ðpb.GenericSignedBeaconBlock{
|
||||
Block: ðpb.GenericSignedBeaconBlock_Deneb{
|
||||
Deneb: ðpb.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 {
|
||||
|
||||
@@ -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"
|
||||
@@ -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",
|
||||
@@ -583,6 +585,43 @@ func testProposeBlock(t *testing.T, graffiti []byte) {
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "deneb block and blobs",
|
||||
version: version.Deneb,
|
||||
block: ðpb.GenericBeaconBlock{
|
||||
Block: ðpb.GenericBeaconBlock_Deneb{
|
||||
Deneb: func() *ethpb.BeaconBlockAndBlobsDeneb {
|
||||
blk := util.NewBeaconBlockDeneb()
|
||||
blk.Block.Body.Graffiti = graffiti
|
||||
return ðpb.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 {
|
||||
@@ -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(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
m.validatorClient.EXPECT().DomainData(
|
||||
gomock.Any(), // ctx
|
||||
gomock.Any(), // epoch
|
||||
).Return(ðpb.DomainResponse{SignatureDomain: make([]byte, 32)}, nil /*err*/)
|
||||
}
|
||||
|
||||
m.validatorClient.EXPECT().ProposeBeaconBlock(
|
||||
gomock.Any(), // ctx
|
||||
gomock.AssignableToTypeOf(ðpb.GenericSignedBeaconBlock{}),
|
||||
|
||||
Reference in New Issue
Block a user