mirror of
https://github.com/OffchainLabs/prysm.git
synced 2026-01-09 15:37:56 -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)
|
return blocks.NewSignedBeaconBlock(bb.Capella)
|
||||||
case *eth.GenericSignedBeaconBlock_BlindedCapella:
|
case *eth.GenericSignedBeaconBlock_BlindedCapella:
|
||||||
return blocks.NewSignedBeaconBlock(bb.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
|
// Generic Signed Beacon Block Deneb can't be used here as it is not a block, but block content with blobs
|
||||||
default:
|
default:
|
||||||
return nil, errors.Wrapf(blocks.ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", gb)
|
return nil, errors.Wrapf(blocks.ErrUnsupportedSignedBeaconBlock, "unable to create block from type %T", gb)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ go_library(
|
|||||||
"aggregate.go",
|
"aggregate.go",
|
||||||
"attest.go",
|
"attest.go",
|
||||||
"attest_protect.go",
|
"attest_protect.go",
|
||||||
|
"blob.go",
|
||||||
"key_reload.go",
|
"key_reload.go",
|
||||||
"log.go",
|
"log.go",
|
||||||
"metrics.go",
|
"metrics.go",
|
||||||
@@ -101,6 +102,7 @@ go_test(
|
|||||||
"aggregate_test.go",
|
"aggregate_test.go",
|
||||||
"attest_protect_test.go",
|
"attest_protect_test.go",
|
||||||
"attest_test.go",
|
"attest_test.go",
|
||||||
|
"blob_test.go",
|
||||||
"key_reload_test.go",
|
"key_reload_test.go",
|
||||||
"metrics_test.go",
|
"metrics_test.go",
|
||||||
"propose_protect_test.go",
|
"propose_protect_test.go",
|
||||||
@@ -138,6 +140,7 @@ go_test(
|
|||||||
"//proto/prysm/v1alpha1:go_default_library",
|
"//proto/prysm/v1alpha1:go_default_library",
|
||||||
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
"//proto/prysm/v1alpha1/validator-client:go_default_library",
|
||||||
"//runtime:go_default_library",
|
"//runtime:go_default_library",
|
||||||
|
"//runtime/version:go_default_library",
|
||||||
"//testing/assert:go_default_library",
|
"//testing/assert:go_default_library",
|
||||||
"//testing/mock:go_default_library",
|
"//testing/mock:go_default_library",
|
||||||
"//testing/require: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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Propose and broadcast block via beacon node
|
var genericSignedBlock *ethpb.GenericSignedBeaconBlock
|
||||||
proposal, err := blk.PbGenericBlock()
|
if blk.Version() >= version.Deneb && !blk.IsBlinded() {
|
||||||
if err != nil {
|
signedBlobs := make([]*ethpb.SignedBlobSidecar, len(b.GetDeneb().Blobs))
|
||||||
log.WithError(err).Error("Failed to create proposal request")
|
for _, blob := range b.GetDeneb().Blobs {
|
||||||
if v.emitAccountMetrics {
|
blobSig, err := v.signBlob(ctx, blob, pubKey)
|
||||||
ValidatorProposeFailVec.WithLabelValues(fmtKey).Inc()
|
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 {
|
if err != nil {
|
||||||
log.WithField("blockSlot", slot).WithError(err).Error("Failed to propose block")
|
log.WithField("blockSlot", slot).WithError(err).Error("Failed to propose block")
|
||||||
if v.emitAccountMetrics {
|
if v.emitAccountMetrics {
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ import (
|
|||||||
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
"github.com/prysmaticlabs/prysm/v4/encoding/bytesutil"
|
||||||
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
ethpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
|
||||||
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
|
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/assert"
|
||||||
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
"github.com/prysmaticlabs/prysm/v4/testing/require"
|
||||||
"github.com/prysmaticlabs/prysm/v4/testing/util"
|
"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) {
|
func testProposeBlock(t *testing.T, graffiti []byte) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
block *ethpb.GenericBeaconBlock
|
block *ethpb.GenericBeaconBlock
|
||||||
|
version int
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
name: "phase0",
|
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 {
|
for _, tt := range tests {
|
||||||
@@ -617,6 +656,17 @@ func testProposeBlock(t *testing.T, graffiti []byte) {
|
|||||||
var sentBlock interfaces.ReadOnlySignedBeaconBlock
|
var sentBlock interfaces.ReadOnlySignedBeaconBlock
|
||||||
var err error
|
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(
|
m.validatorClient.EXPECT().ProposeBeaconBlock(
|
||||||
gomock.Any(), // ctx
|
gomock.Any(), // ctx
|
||||||
gomock.AssignableToTypeOf(ðpb.GenericSignedBeaconBlock{}),
|
gomock.AssignableToTypeOf(ðpb.GenericSignedBeaconBlock{}),
|
||||||
|
|||||||
Reference in New Issue
Block a user