Deneb - web3signer (#12767)

* wip

* adding deneb block

* adding in support for blobs and fixing unit tests for deneb

* fixing linting

* gaz

* adding support for new web3signer version

* fixing tag name

* addressing feedback

* fixing tests

* adding unit test for review

* updating test name

* updating unit tests and length logic

* adding in lengthfor root

* adjusting max blob length

* fixing mock

* fixing another mock

* gaz

* adding network configs

* removing duplicate

* changing based on nishant's feedback

* Update validator/keymanager/remote-web3signer/v1/requests.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* Update validator/keymanager/remote-web3signer/metrics.go

Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>

* sammy's suggestions

* removing temp file

---------

Co-authored-by: Nishant Das <nishdas93@gmail.com>
Co-authored-by: Sammy Rosso <15244892+saolyn@users.noreply.github.com>
Co-authored-by: prylabs-bulldozer[bot] <58059840+prylabs-bulldozer[bot]@users.noreply.github.com>
This commit is contained in:
james-prysm
2023-10-16 13:06:36 -05:00
committed by GitHub
parent b52baba2f1
commit 493a7179d7
15 changed files with 659 additions and 153 deletions

View File

@@ -214,3 +214,28 @@ func ConfigToYaml(cfg *BeaconChainConfig) []byte {
yamlFile := []byte(strings.Join(lines, "\n"))
return yamlFile
}
// NetworkConfigToYaml takes a provided network config and outputs its contents
// in yaml. This allows prysm's network configs to be read by other clients.
func NetworkConfigToYaml(cfg *NetworkConfig) []byte {
lines := []string{
fmt.Sprintf("GOSSIP_MAX_SIZE: %d", cfg.GossipMaxSize),
fmt.Sprintf("GOSSIP_MAX_SIZE_BELLATRIX: %d", cfg.GossipMaxSizeBellatrix),
fmt.Sprintf("MAX_CHUNK_SIZE: %d", cfg.MaxChunkSize),
fmt.Sprintf("MAX_CHUNK_SIZE_BELLATRIX: %d", cfg.MaxChunkSizeBellatrix),
fmt.Sprintf("ATTESTATION_SUBNET_COUNT: %d", cfg.AttestationSubnetCount),
fmt.Sprintf("ATTESTATION_PROPAGATION_SLOT_RANGE: %d", cfg.AttestationPropagationSlotRange),
fmt.Sprintf("MAX_REQUEST_BLOCKS: %d", cfg.MaxRequestBlocks),
fmt.Sprintf("TTFB_TIMEOUT: %d", int(cfg.TtfbTimeout.Seconds())),
fmt.Sprintf("RESP_TIMEOUT: %d", int(cfg.RespTimeout.Seconds())),
fmt.Sprintf("MAXIMUM_GOSSIP_CLOCK_DISPARITY: %d", int(cfg.MaximumGossipClockDisparity.Seconds())),
fmt.Sprintf("MESSAGE_DOMAIN_INVALID_SNAPPY: %#x", cfg.MessageDomainInvalidSnappy),
fmt.Sprintf("MESSAGE_DOMAIN_VALID_SNAPPY: %#x", cfg.MessageDomainValidSnappy),
fmt.Sprintf("MIN_EPOCHS_FOR_BLOBS_SIDECARS_REQUEST: %d", cfg.MinEpochsForBlobsSidecarsRequest),
fmt.Sprintf("MAX_REQUEST_BLOB_SIDECARS: %d", cfg.MaxRequestBlobSidecars),
fmt.Sprintf("MAX_REQUEST_BLOCKS_DENEB: %d", cfg.MaxRequestBlocksDeneb),
}
yamlFile := []byte(strings.Join(lines, "\n"))
return yamlFile
}

View File

@@ -96,7 +96,7 @@ func SlashingsRoot(slashings []uint64) ([32]byte, error) {
func TransactionsRoot(txs [][]byte) ([32]byte, error) {
txRoots := make([][32]byte, 0)
for i := 0; i < len(txs); i++ {
rt, err := transactionRoot(txs[i])
rt, err := ByteSliceRoot(txs[i], fieldparams.MaxBytesPerTxLength) // getting the transaction root here
if err != nil {
return [32]byte{}, err
}
@@ -141,19 +141,21 @@ func WithdrawalSliceRoot(withdrawals []*enginev1.Withdrawal, limit uint64) ([32]
return MixInLength(bytesRoot, bytesRootBufRoot), nil
}
func transactionRoot(tx []byte) ([32]byte, error) {
chunkedRoots, err := PackByChunk([][]byte{tx})
// ByteSliceRoot is a helper func to merkleize an arbitrary List[Byte, N]
// this func runs Chunkify + MerkleizeVector
// max length is dividable by 32 ( root length )
func ByteSliceRoot(slice []byte, maxLength uint64) ([32]byte, error) {
chunkedRoots, err := PackByChunk([][]byte{slice})
if err != nil {
return [32]byte{}, err
}
maxLength := (fieldparams.MaxBytesPerTxLength + 31) / 32
bytesRoot, err := BitwiseMerkleize(chunkedRoots, uint64(len(chunkedRoots)), uint64(maxLength))
maxRootLength := (maxLength + 31) / 32 // nearest number divisible by root length (32)
bytesRoot, err := BitwiseMerkleize(chunkedRoots, uint64(len(chunkedRoots)), maxRootLength)
if err != nil {
return [32]byte{}, errors.Wrap(err, "could not compute merkleization")
}
bytesRootBuf := new(bytes.Buffer)
if err := binary.Write(bytesRootBuf, binary.LittleEndian, uint64(len(tx))); err != nil {
if err := binary.Write(bytesRootBuf, binary.LittleEndian, uint64(len(slice))); err != nil {
return [32]byte{}, errors.Wrap(err, "could not marshal length")
}
bytesRootBufRoot := make([]byte, 32)

View File

@@ -122,6 +122,58 @@ func TestTransactionsRoot(t *testing.T) {
}
}
func TestByteSliceRoot(t *testing.T) {
tests := []struct {
name string
slice []byte
maxLength uint64
want [32]byte
wantErr bool
}{
{
name: "nil",
slice: nil,
want: [32]byte{245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, 39, 89, 251, 75},
},
{
name: "empty",
slice: []byte{},
want: [32]byte{245, 165, 253, 66, 209, 106, 32, 48, 39, 152, 239, 110, 211, 9, 151, 155, 67, 0, 61, 35, 32, 217, 240, 232, 234, 152, 49, 169, 39, 89, 251, 75},
},
{
name: "byte slice 3 values",
slice: []byte{1, 2, 3},
want: [32]byte{20, 159, 26, 252, 247, 204, 44, 159, 161, 135, 211, 195, 106, 59, 220, 149, 199, 163, 228, 155, 113, 118, 64, 126, 173, 223, 102, 1, 241, 158, 164, 185},
},
{
name: "byte slice 32 values",
slice: []byte{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32},
want: [32]byte{7, 30, 46, 77, 237, 240, 59, 126, 232, 232, 232, 6, 145, 210, 31, 18, 117, 12, 217, 40, 204, 141, 90, 236, 241, 128, 221, 45, 126, 39, 39, 202},
},
{
name: "over max length",
slice: make([]byte, fieldparams.RootLength+1),
want: [32]byte{},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.maxLength == 0 {
tt.maxLength = fieldparams.RootLength
}
got, err := ssz.ByteSliceRoot(tt.slice, tt.maxLength)
if (err != nil) != tt.wantErr {
t.Errorf("ByteSliceRoot() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("ByteSliceRoot() got = %v, want %v", got, tt.want)
}
})
}
}
func TestPackByChunk_SingleList(t *testing.T) {
tests := []struct {
name string

View File

@@ -256,9 +256,17 @@ func createTestnetDir() (string, error) {
testNetDir := e2e.TestParams.TestPath + "/web3signer-testnet"
configPath := filepath.Join(testNetDir, "config.yaml")
rawYaml := params.ConfigToYaml(params.BeaconConfig())
// Add in deposit contract in yaml
depContractStr := fmt.Sprintf("\nDEPOSIT_CONTRACT_ADDRESS: %s", params.BeaconConfig().DepositContractAddress)
depContractStr := fmt.Sprintf("\nDEPOSIT_CONTRACT_ADDRESS: %s\n", params.BeaconConfig().DepositContractAddress)
rawYaml = append(rawYaml, []byte(depContractStr)...)
rawYaml = append(rawYaml, params.NetworkConfigToYaml(params.BeaconNetworkConfig())...)
rawYaml = append(rawYaml, []byte("\nEPOCHS_PER_SUBNET_SUBSCRIPTION: 256\n")...)
rawYaml = append(rawYaml, []byte("\nMIN_EPOCHS_FOR_BLOCK_REQUESTS: 33024\n")...)
rawYaml = append(rawYaml, []byte("\nSUBNETS_PER_NODE: 2\n")...)
rawYaml = append(rawYaml, []byte("\nATTESTATION_SUBNET_EXTRA_BITS: 0\n")...)
rawYaml = append(rawYaml, []byte("\nATTESTATION_SUBNET_PREFIX_BITS: 6\n")...)
if err := file.MkdirAll(testNetDir); err != nil {
return "", err

View File

@@ -6,10 +6,10 @@ lighthouse_archive_name = "lighthouse-%s-x86_64-unknown-linux-gnu-portable.tar.g
def e2e_deps():
http_archive(
name = "web3signer",
urls = ["https://artifacts.consensys.net/public/web3signer/raw/names/web3signer.tar.gz/versions/23.3.1/web3signer-23.3.1.tar.gz"],
sha256 = "32dfbfd8d5900f19aa426d3519724dd696e6529b7ec2f99e0cb1690dae52b3d6",
urls = ["https://artifacts.consensys.net/public/web3signer/raw/names/web3signer.tar.gz/versions/23.9.1/web3signer-23.9.1.tar.gz"],
sha256 = "aec9dc745cb25fd8d7b38b06e435e3138972c2cf842dd6f851d50be7bf081629",
build_file = "@prysm//testing/endtoend:web3signer.BUILD",
strip_prefix = "web3signer-23.3.1",
strip_prefix = "web3signer-23.9.1",
)
http_archive(

View File

@@ -6,15 +6,22 @@ searching `Consensys' Web3Signer API specification`
issue: https://github.com/prysmaticlabs/prysm/issues/9994
## Support
WIP
API interface: https://github.com/ethereum/remote-signing-api
## Features
### CLI
WIP
detailed info found on https://docs.prylabs.network/docs/wallet/web3signer
Flags used on validator client
- `--validators-external-signer-url=http://localhost:9000`
with hex keys
- `--validators-external-signer-public-keys=0xa99a...e44c,0xb89b...4a0b`
with url
- `--validators-external-signer-public-keys=https://web3signer.com/api/v1/eth2/publicKeys`
### API

View File

@@ -113,95 +113,27 @@ func getSignRequestJson(ctx context.Context, validator *validator.Validate, requ
}
switch request.Object.(type) {
case *validatorpb.SignRequest_Block:
bockSignRequest, err := web3signerv1.GetBlockSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, bockSignRequest); err != nil {
return nil, err
}
blockSignRequestsTotal.Inc()
return json.Marshal(bockSignRequest)
return handleBlock(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_AttestationData:
attestationSignRequest, err := web3signerv1.GetAttestationSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, attestationSignRequest); err != nil {
return nil, err
}
attestationSignRequestsTotal.Inc()
return json.Marshal(attestationSignRequest)
return handleAttestationData(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_AggregateAttestationAndProof:
aggregateAndProofSignRequest, err := web3signerv1.GetAggregateAndProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, aggregateAndProofSignRequest); err != nil {
return nil, err
}
aggregateAndProofSignRequestsTotal.Inc()
return json.Marshal(aggregateAndProofSignRequest)
return handleAggregateAttestationAndProof(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_Slot:
aggregationSlotSignRequest, err := web3signerv1.GetAggregationSlotSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, aggregationSlotSignRequest); err != nil {
return nil, err
}
aggregationSlotSignRequestsTotal.Inc()
return json.Marshal(aggregationSlotSignRequest)
return handleAggregationSlot(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlockAltair:
blockv2AltairSignRequest, err := web3signerv1.GetBlockAltairSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2AltairSignRequest); err != nil {
return nil, err
}
blockAltairSignRequestsTotal.Inc()
return json.Marshal(blockv2AltairSignRequest)
return handleBlockAltair(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlockBellatrix:
blockv2BellatrixSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2BellatrixSignRequest); err != nil {
return nil, err
}
blockBellatrixSignRequestsTotal.Inc()
return json.Marshal(blockv2BellatrixSignRequest)
return handleBlockBellatrix(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlindedBlockBellatrix:
blindedBlockv2SignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlockv2SignRequest); err != nil {
return nil, err
}
blindedBlockBellatrixSignRequestsTotal.Inc()
return json.Marshal(blindedBlockv2SignRequest)
return handleBlindedBlockBellatrix(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlockCapella:
blockv2CapellaSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2CapellaSignRequest); err != nil {
return nil, err
}
blockCapellaSignRequestsTotal.Inc()
return json.Marshal(blockv2CapellaSignRequest)
return handleBlockCapella(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlindedBlockCapella:
blindedBlockv2CapellaSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlockv2CapellaSignRequest); err != nil {
return nil, err
}
blindedBlockCapellaSignRequestsTotal.Inc()
return json.Marshal(blindedBlockv2CapellaSignRequest)
return handleBlindedBlockCapella(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlockDeneb:
return handleBlockDeneb(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlindedBlockDeneb:
return handleBlindedBlockDeneb(ctx, validator, request, genesisValidatorsRoot)
// We do not support "DEPOSIT" type.
/*
case *validatorpb.:
@@ -209,70 +141,255 @@ func getSignRequestJson(ctx context.Context, validator *validator.Validate, requ
*/
case *validatorpb.SignRequest_Epoch:
randaoRevealSignRequest, err := web3signerv1.GetRandaoRevealSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, randaoRevealSignRequest); err != nil {
return nil, err
}
randaoRevealSignRequestsTotal.Inc()
return json.Marshal(randaoRevealSignRequest)
// tech debt that prysm uses signing type epoch
return handleRandaoReveal(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_Exit:
voluntaryExitRequest, err := web3signerv1.GetVoluntaryExitSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, voluntaryExitRequest); err != nil {
return nil, err
}
voluntaryExitSignRequestsTotal.Inc()
return json.Marshal(voluntaryExitRequest)
return handleVoluntaryExit(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_SyncMessageBlockRoot:
syncCommitteeMessageRequest, err := web3signerv1.GetSyncCommitteeMessageSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, syncCommitteeMessageRequest); err != nil {
return nil, err
}
syncCommitteeMessageSignRequestsTotal.Inc()
return json.Marshal(syncCommitteeMessageRequest)
return handleSyncMessageBlockRoot(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_SyncAggregatorSelectionData:
syncCommitteeSelectionProofRequest, err := web3signerv1.GetSyncCommitteeSelectionProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, syncCommitteeSelectionProofRequest); err != nil {
return nil, err
}
syncCommitteeSelectionProofSignRequestsTotal.Inc()
return json.Marshal(syncCommitteeSelectionProofRequest)
return handleSyncAggregatorSelectionData(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_ContributionAndProof:
contributionAndProofRequest, err := web3signerv1.GetSyncCommitteeContributionAndProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, contributionAndProofRequest); err != nil {
return nil, err
}
syncCommitteeContributionAndProofSignRequestsTotal.Inc()
return json.Marshal(contributionAndProofRequest)
return handleContributionAndProof(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_Registration:
validatorRegistrationRequest, err := web3signerv1.GetValidatorRegistrationSignRequest(request)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, validatorRegistrationRequest); err != nil {
return nil, err
}
validatorRegistrationSignRequestsTotal.Inc()
return json.Marshal(validatorRegistrationRequest)
return handleRegistration(ctx, validator, request)
case *validatorpb.SignRequest_Blob:
return handleBlob(ctx, validator, request, genesisValidatorsRoot)
case *validatorpb.SignRequest_BlindedBlob:
return handleBlindedBlob(ctx, validator, request, genesisValidatorsRoot)
default:
return nil, fmt.Errorf("web3signer sign request type %T not supported", request.Object)
}
}
func handleBlock(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
bockSignRequest, err := web3signerv1.GetBlockSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, bockSignRequest); err != nil {
return nil, err
}
blockSignRequestsTotal.Inc()
return json.Marshal(bockSignRequest)
}
func handleAttestationData(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
attestationSignRequest, err := web3signerv1.GetAttestationSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, attestationSignRequest); err != nil {
return nil, err
}
attestationSignRequestsTotal.Inc()
return json.Marshal(attestationSignRequest)
}
func handleAggregateAttestationAndProof(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
aggregateAndProofSignRequest, err := web3signerv1.GetAggregateAndProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, aggregateAndProofSignRequest); err != nil {
return nil, err
}
aggregateAndProofSignRequestsTotal.Inc()
return json.Marshal(aggregateAndProofSignRequest)
}
func handleAggregationSlot(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
aggregationSlotSignRequest, err := web3signerv1.GetAggregationSlotSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, aggregationSlotSignRequest); err != nil {
return nil, err
}
aggregationSlotSignRequestsTotal.Inc()
return json.Marshal(aggregationSlotSignRequest)
}
func handleBlockAltair(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blockv2AltairSignRequest, err := web3signerv1.GetBlockAltairSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2AltairSignRequest); err != nil {
return nil, err
}
blockAltairSignRequestsTotal.Inc()
return json.Marshal(blockv2AltairSignRequest)
}
func handleBlockBellatrix(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blockv2BellatrixSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2BellatrixSignRequest); err != nil {
return nil, err
}
blockBellatrixSignRequestsTotal.Inc()
return json.Marshal(blockv2BellatrixSignRequest)
}
func handleBlindedBlockBellatrix(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blindedBlockv2SignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlockv2SignRequest); err != nil {
return nil, err
}
blindedBlockBellatrixSignRequestsTotal.Inc()
return json.Marshal(blindedBlockv2SignRequest)
}
func handleBlockCapella(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blockv2CapellaSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2CapellaSignRequest); err != nil {
return nil, err
}
blockCapellaSignRequestsTotal.Inc()
return json.Marshal(blockv2CapellaSignRequest)
}
func handleBlindedBlockCapella(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blindedBlockv2CapellaSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlockv2CapellaSignRequest); err != nil {
return nil, err
}
blindedBlockCapellaSignRequestsTotal.Inc()
return json.Marshal(blindedBlockv2CapellaSignRequest)
}
func handleBlockDeneb(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blockv2DenebSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blockv2DenebSignRequest); err != nil {
return nil, err
}
blockDenebSignRequestsTotal.Inc()
return json.Marshal(blockv2DenebSignRequest)
}
func handleBlindedBlockDeneb(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blindedBlockv2DenebSignRequest, err := web3signerv1.GetBlockV2BlindedSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlockv2DenebSignRequest); err != nil {
return nil, err
}
blindedBlockDenebSignRequestsTotal.Inc()
return json.Marshal(blindedBlockv2DenebSignRequest)
}
func handleRandaoReveal(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
randaoRevealSignRequest, err := web3signerv1.GetRandaoRevealSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, randaoRevealSignRequest); err != nil {
return nil, err
}
randaoRevealSignRequestsTotal.Inc()
return json.Marshal(randaoRevealSignRequest)
}
func handleVoluntaryExit(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
voluntaryExitRequest, err := web3signerv1.GetVoluntaryExitSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, voluntaryExitRequest); err != nil {
return nil, err
}
voluntaryExitSignRequestsTotal.Inc()
return json.Marshal(voluntaryExitRequest)
}
func handleSyncMessageBlockRoot(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
syncCommitteeMessageRequest, err := web3signerv1.GetSyncCommitteeMessageSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, syncCommitteeMessageRequest); err != nil {
return nil, err
}
syncCommitteeMessageSignRequestsTotal.Inc()
return json.Marshal(syncCommitteeMessageRequest)
}
func handleSyncAggregatorSelectionData(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
syncCommitteeSelectionProofRequest, err := web3signerv1.GetSyncCommitteeSelectionProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, syncCommitteeSelectionProofRequest); err != nil {
return nil, err
}
syncCommitteeSelectionProofSignRequestsTotal.Inc()
return json.Marshal(syncCommitteeSelectionProofRequest)
}
func handleContributionAndProof(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
contributionAndProofRequest, err := web3signerv1.GetSyncCommitteeContributionAndProofSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, contributionAndProofRequest); err != nil {
return nil, err
}
syncCommitteeContributionAndProofSignRequestsTotal.Inc()
return json.Marshal(contributionAndProofRequest)
}
func handleRegistration(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest) ([]byte, error) {
validatorRegistrationRequest, err := web3signerv1.GetValidatorRegistrationSignRequest(request)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, validatorRegistrationRequest); err != nil {
return nil, err
}
validatorRegistrationSignRequestsTotal.Inc()
return json.Marshal(validatorRegistrationRequest)
}
func handleBlob(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blobRequest, err := web3signerv1.GetBlobSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blobRequest); err != nil {
return nil, err
}
blobSignRequestsTotal.Inc()
return json.Marshal(blobRequest)
}
func handleBlindedBlob(ctx context.Context, validator *validator.Validate, request *validatorpb.SignRequest, genesisValidatorsRoot []byte) ([]byte, error) {
blindedBlobRequest, err := web3signerv1.GetBlobSignRequest(request, genesisValidatorsRoot)
if err != nil {
return nil, err
}
if err = validator.StructCtx(ctx, blindedBlobRequest); err != nil {
return nil, err
}
blindedBlobSignRequestsTotal.Inc()
return json.Marshal(blindedBlobRequest)
}
// SubscribeAccountChanges returns the event subscription for changes to public keys.
func (km *Keymanager) SubscribeAccountChanges(pubKeysChan chan [][fieldparams.BLSPubkeyLength]byte) event.Subscription {
return km.accountsChangedFeed.Subscribe(pubKeysChan)

View File

@@ -171,12 +171,20 @@ func TestKeymanager_Sign(t *testing.T) {
want: desiredSig,
wantErr: false,
},
{
name: "BLOB_SIDECAR",
args: args{
request: mock.GetMockSignRequest("BLOB_SIDECAR"),
},
want: desiredSig,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := km.Sign(ctx, tt.args.request)
if (err != nil) != tt.wantErr {
t.Errorf("GetVoluntaryExitSignRequest() error = %v, wantErr %v", err, tt.wantErr)
t.Errorf("name:%s error = %v, wantErr %v", tt.name, err, tt.wantErr)
return
}
require.DeepEqual(t, got, tt.want)

View File

@@ -50,6 +50,14 @@ var (
Name: "remote_web3signer_blinded_block_capella_sign_requests_total",
Help: "Total number of block capella sign requests",
})
blockDenebSignRequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
Name: "remote_web3signer_block_deneb_sign_requests_total",
Help: "Total number of block deneb sign requests",
})
blindedBlockDenebSignRequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
Name: "remote_web3signer_blinded_block_deneb_sign_requests_total",
Help: "Total number of blinded block deneb sign requests",
})
randaoRevealSignRequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
Name: "remote_web3signer_randao_reveal_sign_requests_total",
Help: "Total number of randao reveal sign requests",
@@ -74,4 +82,12 @@ var (
Name: "remote_web3signer_validator_registration_sign_requests_total",
Help: "Total number of validator registration sign requests",
})
blobSignRequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
Name: "remote_web3signer_blob_sign_requests_total",
Help: "Total number of blob sign requests",
})
blindedBlobSignRequestsTotal = promauto.NewCounter(prometheus.CounterOpts{
Name: "remote_web3signer_blinded_blob_sign_requests_total",
Help: "Total number of blinded blob sign requests",
})
)

View File

@@ -10,9 +10,11 @@ go_library(
importpath = "github.com/prysmaticlabs/prysm/v4/validator/keymanager/remote-web3signer/v1",
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams:go_default_library",
"//consensus-types/blocks:go_default_library",
"//consensus-types/interfaces:go_default_library",
"//consensus-types/primitives:go_default_library",
"//encoding/ssz:go_default_library",
"//network/forks:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",

View File

@@ -8,11 +8,13 @@ go_library(
visibility = ["//visibility:public"],
deps = [
"//config/fieldparams:go_default_library",
"//encoding/ssz:go_default_library",
"//proto/prysm/v1alpha1:go_default_library",
"//proto/prysm/v1alpha1/validator-client:go_default_library",
"//testing/util:go_default_library",
"//validator/keymanager/remote-web3signer/v1:go_default_library",
"@com_github_ethereum_go_ethereum//common/hexutil:go_default_library",
"@com_github_prysmaticlabs_go_bitfield//:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
],
)

View File

@@ -6,10 +6,12 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/prysmaticlabs/go-bitfield"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
eth "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1"
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
"github.com/prysmaticlabs/prysm/v4/testing/util"
v1 "github.com/prysmaticlabs/prysm/v4/validator/keymanager/remote-web3signer/v1"
log "github.com/sirupsen/logrus"
)
/////////////////////////////////////////////////////////////////////////////////////////////////
@@ -363,6 +365,24 @@ func GetMockSignRequest(t string) *validatorpb.SignRequest {
BlindedBlockCapella: util.HydrateBlindedBeaconBlockCapella(&eth.BlindedBeaconBlockCapella{}),
},
}
case "BLOCK_V2_DENEB":
return &validatorpb.SignRequest{
PublicKey: make([]byte, fieldparams.BLSPubkeyLength),
SigningRoot: make([]byte, fieldparams.RootLength),
SignatureDomain: make([]byte, 4),
Object: &validatorpb.SignRequest_BlockDeneb{
BlockDeneb: util.HydrateBeaconBlockDeneb(&eth.BeaconBlockDeneb{}),
},
}
case "BLOCK_V2_BLINDED_DENEB":
return &validatorpb.SignRequest{
PublicKey: make([]byte, fieldparams.BLSPubkeyLength),
SigningRoot: make([]byte, fieldparams.RootLength),
SignatureDomain: make([]byte, 4),
Object: &validatorpb.SignRequest_BlindedBlockDeneb{
BlindedBlockDeneb: util.HydrateBlindedBeaconBlockDeneb(&eth.BlindedBeaconBlockDeneb{}),
},
}
case "RANDAO_REVEAL":
return &validatorpb.SignRequest{
PublicKey: make([]byte, fieldparams.BLSPubkeyLength),
@@ -444,6 +464,49 @@ func GetMockSignRequest(t string) *validatorpb.SignRequest {
},
SigningSlot: 0,
}
case "BLOB_SIDECAR":
return &validatorpb.SignRequest{
PublicKey: make([]byte, fieldparams.BLSPubkeyLength),
SigningRoot: make([]byte, fieldparams.RootLength),
SignatureDomain: make([]byte, 4),
Object: &validatorpb.SignRequest_Blob{
Blob: &eth.BlobSidecar{
BlockRoot: make([]byte, fieldparams.RootLength),
Index: uint64(0),
Slot: 0,
BlockParentRoot: make([]byte, fieldparams.RootLength),
ProposerIndex: 0,
Blob: make([]byte, fieldparams.BlobLength),
KzgCommitment: make([]byte, fieldparams.BLSPubkeyLength),
KzgProof: make([]byte, fieldparams.BLSPubkeyLength),
},
},
SigningSlot: 0,
}
case "BLINDED_BLOB_SIDECAR":
blobRoot, err := ssz.ByteSliceRoot(make([]byte, fieldparams.BlobLength), fieldparams.BlobLength)
if err != nil {
log.Error(err)
return nil
}
return &validatorpb.SignRequest{
PublicKey: make([]byte, fieldparams.BLSPubkeyLength),
SigningRoot: make([]byte, fieldparams.RootLength),
SignatureDomain: make([]byte, 4),
Object: &validatorpb.SignRequest_BlindedBlob{
BlindedBlob: &eth.BlindedBlobSidecar{
BlockRoot: make([]byte, fieldparams.RootLength),
Index: uint64(0),
Slot: 0,
BlockParentRoot: make([]byte, fieldparams.RootLength),
ProposerIndex: 0,
BlobRoot: blobRoot[:],
KzgCommitment: make([]byte, fieldparams.BLSPubkeyLength),
KzgProof: make([]byte, fieldparams.BLSPubkeyLength),
},
},
SigningSlot: 0,
}
default:
fmt.Printf("Web3signer sign request type: %v not found", t)
return nil
@@ -606,6 +669,30 @@ func MockValidatorRegistrationSignRequest() *v1.ValidatorRegistrationSignRequest
}
}
// MockBlobSidecarSignRequest is a mock implementation of the BlobSidecarSignRequest.
func MockBlobSidecarSignRequest() *v1.BlobSidecarSignRequest {
blobRoot, err := ssz.ByteSliceRoot(make([]byte, fieldparams.BlobLength), fieldparams.BlobLength)
if err != nil {
log.Error(err)
return nil
}
return &v1.BlobSidecarSignRequest{
Type: "BLOB_SIDECAR",
ForkInfo: MockForkInfo(),
SigningRoot: make([]byte, fieldparams.RootLength),
BlobSidecar: &v1.BlobSidecar{
BlockRoot: make([]byte, fieldparams.RootLength),
Index: fmt.Sprint(0),
Slot: fmt.Sprint(0),
BlockParentRoot: make([]byte, fieldparams.RootLength),
ProposerIndex: fmt.Sprint(0),
BlobRoot: blobRoot[:],
KzgCommitment: make([]byte, fieldparams.BLSPubkeyLength),
KzgProof: make([]byte, fieldparams.BLSPubkeyLength),
},
}
}
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -4,8 +4,10 @@ import (
"fmt"
"github.com/pkg/errors"
fieldparams "github.com/prysmaticlabs/prysm/v4/config/fieldparams"
"github.com/prysmaticlabs/prysm/v4/consensus-types/blocks"
"github.com/prysmaticlabs/prysm/v4/consensus-types/interfaces"
"github.com/prysmaticlabs/prysm/v4/encoding/ssz"
validatorpb "github.com/prysmaticlabs/prysm/v4/proto/prysm/v1alpha1/validator-client"
)
@@ -271,8 +273,8 @@ func GetSyncCommitteeContributionAndProofSignRequest(request *validatorpb.SignRe
}, nil
}
// GetBlockV2BlindedSignRequest maps the request for signing types
// Supports Bellatrix and Capella
// GetBlockV2BlindedSignRequest maps the request for signing types (GetBlockV2 id defined by the remote signer interface and not the beacon APIs)
// Supports Bellatrix, Capella, Deneb
func GetBlockV2BlindedSignRequest(request *validatorpb.SignRequest, genesisValidatorsRoot []byte) (*BlockV2BlindedSignRequest, error) {
if request == nil {
return nil, errors.New("nil sign request provided")
@@ -337,6 +339,34 @@ func GetBlockV2BlindedSignRequest(request *validatorpb.SignRequest, genesisValid
return nil, err
}
b = beaconBlock
case *validatorpb.SignRequest_BlockDeneb:
version = "DENEB"
blockDeneb, ok := request.Object.(*validatorpb.SignRequest_BlockDeneb)
if !ok {
return nil, errors.New("failed to cast request object to deneb block")
}
if blockDeneb == nil {
return nil, errors.New("invalid sign request: deneb block is nil")
}
beaconBlock, err := blocks.NewBeaconBlock(blockDeneb.BlockDeneb)
if err != nil {
return nil, err
}
b = beaconBlock
case *validatorpb.SignRequest_BlindedBlockDeneb:
version = "DENEB"
blindedBlockDeneb, ok := request.Object.(*validatorpb.SignRequest_BlindedBlockDeneb)
if !ok {
return nil, errors.New("failed to cast request object to blinded deneb block")
}
if blindedBlockDeneb == nil {
return nil, errors.New("invalid sign request: blinded deneb block is nil")
}
beaconBlock, err := blocks.NewBeaconBlock(blindedBlockDeneb.BlindedBlockDeneb)
if err != nil {
return nil, err
}
b = beaconBlock
default:
return nil, errors.New("invalid sign request - invalid object type")
}
@@ -386,3 +416,66 @@ func GetValidatorRegistrationSignRequest(request *validatorpb.SignRequest) (*Val
},
}, nil
}
// GetBlobSignRequest maps the request for signing type BLOB_SIDECAR
func GetBlobSignRequest(request *validatorpb.SignRequest, genesisValidatorsRoot []byte) (*BlobSidecarSignRequest, error) {
if request == nil {
return nil, errors.New("nil sign request provided")
}
fork, err := MapForkInfo(request.SigningSlot, genesisValidatorsRoot)
if err != nil {
return nil, err
}
var blobSidecar *BlobSidecar
switch request.Object.(type) {
case *validatorpb.SignRequest_Blob:
blob, ok := request.Object.(*validatorpb.SignRequest_Blob)
if !ok {
return nil, errors.New("failed to cast request object to blob sidecar")
}
if blob == nil || blob.Blob == nil {
return nil, errors.New("invalid sign request: blob sidecar is nil")
}
blobRoot, err := ssz.ByteSliceRoot(blob.Blob.Blob, fieldparams.BlobLength)
if err != nil {
return nil, errors.Wrap(err, "unable to get blob root from ssz roots")
}
blobSidecar = &BlobSidecar{
BlockRoot: blob.Blob.BlockRoot,
Index: fmt.Sprint(blob.Blob.Index),
Slot: fmt.Sprint(blob.Blob.Slot),
BlockParentRoot: blob.Blob.BlockParentRoot,
ProposerIndex: fmt.Sprint(blob.Blob.ProposerIndex),
BlobRoot: blobRoot[:],
KzgCommitment: blob.Blob.KzgCommitment,
KzgProof: blob.Blob.KzgProof,
}
case *validatorpb.SignRequest_BlindedBlob:
blindedBlob, ok := request.Object.(*validatorpb.SignRequest_BlindedBlob)
if !ok {
return nil, errors.New("failed to cast request object to blinded blob sidecar")
}
if blindedBlob == nil || blindedBlob.BlindedBlob == nil {
return nil, errors.New("invalid sign request: blinded blob sidecar is nil")
}
blobSidecar = &BlobSidecar{
BlockRoot: blindedBlob.BlindedBlob.BlockRoot,
Index: fmt.Sprint(blindedBlob.BlindedBlob.Index),
Slot: fmt.Sprint(blindedBlob.BlindedBlob.Slot),
BlockParentRoot: blindedBlob.BlindedBlob.BlockParentRoot,
ProposerIndex: fmt.Sprint(blindedBlob.BlindedBlob.ProposerIndex),
BlobRoot: blindedBlob.BlindedBlob.BlobRoot,
KzgCommitment: blindedBlob.BlindedBlob.KzgCommitment,
KzgProof: blindedBlob.BlindedBlob.KzgProof,
}
default:
return nil, errors.New("invalid sign request: invalid object type")
}
return &BlobSidecarSignRequest{
Type: "BLOB_SIDECAR",
ForkInfo: fork,
SigningRoot: request.SigningRoot,
BlobSidecar: blobSidecar,
}, nil
}

View File

@@ -424,6 +424,32 @@ func TestGetBlockV2BlindedSignRequest(t *testing.T) {
}(t), "CAPELLA"),
wantErr: false,
},
{
name: "Happy Path Test non blinded Deneb",
args: args{
request: mock.GetMockSignRequest("BLOCK_V2_DENEB"),
genesisValidatorsRoot: make([]byte, fieldparams.RootLength),
},
want: mock.MockBlockV2BlindedSignRequest(func(t *testing.T) []byte {
bytevalue, err := hexutil.Decode("0xbce73ee2c617851846af2b3ea2287e3b686098e18ae508c7271aaa06ab1d06cd")
require.NoError(t, err)
return bytevalue
}(t), "DENEB"),
wantErr: false,
},
{
name: "Happy Path Test blinded Deneb",
args: args{
request: mock.GetMockSignRequest("BLOCK_V2_BLINDED_DENEB"),
genesisValidatorsRoot: make([]byte, fieldparams.RootLength),
},
want: mock.MockBlockV2BlindedSignRequest(func(t *testing.T) []byte {
bytevalue, err := hexutil.Decode("0xfeb1f7e4f704e72544f4f097b36cb3f3af83043765ad9ad3c3a6cd7fac605055")
require.NoError(t, err)
return bytevalue
}(t), "DENEB"),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@@ -439,6 +465,47 @@ func TestGetBlockV2BlindedSignRequest(t *testing.T) {
}
}
func TestGetBlobSignRequest(t *testing.T) {
type args struct {
request *validatorpb.SignRequest
}
tests := []struct {
name string
args args
want *v1.BlobSidecarSignRequest
wantErr bool
}{
{
name: "Happy Path Test",
args: args{
request: mock.GetMockSignRequest("BLOB_SIDECAR"),
},
want: mock.MockBlobSidecarSignRequest(),
wantErr: false,
},
{
name: "Happy Path Test Blinded",
args: args{
request: mock.GetMockSignRequest("BLINDED_BLOB_SIDECAR"),
},
want: mock.MockBlobSidecarSignRequest(),
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := v1.GetBlobSignRequest(tt.args.request, make([]byte, fieldparams.RootLength))
if (err != nil) != tt.wantErr {
t.Errorf("GetBlobSignRequest() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetBlobSignRequest() got = %v, want %v", got, tt.want)
}
})
}
}
func TestGetValidatorRegistrationSignRequest(t *testing.T) {
type args struct {
request *validatorpb.SignRequest

View File

@@ -105,6 +105,14 @@ type ValidatorRegistrationSignRequest struct {
ValidatorRegistration *ValidatorRegistration `json:"validator_registration" validate:"required"`
}
// BlobSidecarSignRequest a request object for web3signer sign api.
type BlobSidecarSignRequest struct {
Type string `json:"type" validate:"required"`
ForkInfo *ForkInfo `json:"fork_info" validate:"required"`
SigningRoot hexutil.Bytes `json:"signingRoot"`
BlobSidecar *BlobSidecar `json:"blob_sidecar" validate:"required,dive"`
}
////////////////////////////////////////////////////////////////////////////////
// sub properties of Sign Requests /////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
@@ -337,6 +345,18 @@ type ValidatorRegistration struct {
Pubkey hexutil.Bytes `json:"pubkey" validate:"required"` /* bls hexadecimal string */
}
// BlobSidecar a sub property of BlobSidecarSignRequest
type BlobSidecar struct {
BlockRoot hexutil.Bytes `json:"block_root" validate:"required"` /* 32 hexadecimal string */
Index string `json:"index" validate:"required"` /* uint64 */
Slot string `json:"slot" validate:"required"` /* uint64 */
BlockParentRoot hexutil.Bytes `json:"block_parent_root" validate:"required"` /* 32 hexadecimal string */
ProposerIndex string `json:"proposer_index" validate:"required"` /* uint64 */
BlobRoot hexutil.Bytes `json:"blob_root" validate:"required"` /* 32 hexadecimal string */
KzgCommitment hexutil.Bytes `json:"kzg_commitment" validate:"required"` // pattern: "^0x[a-fA-F0-9]{96}$" ssz-size:"48"
KzgProof hexutil.Bytes `json:"kzg_proof" validate:"required"` // pattern: "^0x[a-fA-F0-9]{96}$" ssz-size:"48"
}
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////