Skip to content

Commit 01dda79

Browse files
committed
Add sync committee contribution and proof signing
1 parent 8819558 commit 01dda79

3 files changed

Lines changed: 233 additions & 12 deletions

File tree

pkg/dirksigner/dirk-signer.go

Lines changed: 153 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,19 @@ func (d *DirkSigner) getAccount(pubkey [48]byte) e2wt.AccountProtectingSigner {
132132
return nil
133133
}
134134

135+
func nilCheckFork(fork *api.Fork) *errors.SignerError {
136+
if fork.CurrentVersion == nil {
137+
return errors.ErrBadRequest
138+
}
139+
if fork.Epoch == nil {
140+
return errors.ErrBadRequest
141+
}
142+
if fork.PreviousVersion == nil {
143+
return errors.ErrBadRequest
144+
}
145+
return nil
146+
}
147+
135148
func (d *DirkSigner) AggregationSlotSigning(ctx context.Context, pubkey [48]byte, obj *api.AggregationSlotSigning) ([96]byte, *errors.SignerError) {
136149
return [96]byte{}, nil
137150
}
@@ -177,7 +190,134 @@ func (d *DirkSigner) SyncCommitteeSelectionProofSigning(ctx context.Context, pub
177190
}
178191

179192
func (d *DirkSigner) SyncCommitteeContributionAndProofSigning(ctx context.Context, pubkey [48]byte, obj *api.SyncCommitteeContributionAndProofSigning) ([96]byte, *errors.SignerError) {
180-
return [96]byte{}, nil
193+
// Sanity check field nilness
194+
if obj.ContributionAndProof.AggregatorIndex == nil {
195+
d.log.Warn("aggregator index is nil")
196+
return [96]byte{}, errors.ErrBadRequest
197+
}
198+
if obj.ContributionAndProof.Contribution == nil {
199+
d.log.Warn("contribution is nil")
200+
return [96]byte{}, errors.ErrBadRequest
201+
}
202+
if obj.ContributionAndProof.SelectionProof == nil {
203+
d.log.Warn("selection proof is nil")
204+
return [96]byte{}, errors.ErrBadRequest
205+
}
206+
207+
contribution := obj.ContributionAndProof.Contribution
208+
if contribution.AggregationBits == nil {
209+
d.log.Warn("aggregation bits is nil")
210+
return [96]byte{}, errors.ErrBadRequest
211+
}
212+
if contribution.BeaconBlockRoot == nil {
213+
d.log.Warn("beacon block root is nil")
214+
return [96]byte{}, errors.ErrBadRequest
215+
}
216+
if contribution.Signature == nil {
217+
d.log.Warn("signature is nil")
218+
return [96]byte{}, errors.ErrBadRequest
219+
}
220+
if contribution.Slot == nil {
221+
d.log.Warn("slot is nil")
222+
return [96]byte{}, errors.ErrBadRequest
223+
}
224+
if contribution.SubcommitteeIndex == nil {
225+
d.log.Warn("subcommittee index is nil")
226+
return [96]byte{}, errors.ErrBadRequest
227+
}
228+
229+
if nilCheckFork(&obj.ForkInfo.Fork) != nil {
230+
d.log.Warn("fork info is nil")
231+
return [96]byte{}, errors.ErrBadRequest
232+
}
233+
234+
aggregatorIndex, err := decodeValidatorIndex(*obj.ContributionAndProof.AggregatorIndex)
235+
if err != nil {
236+
d.log.Warn("failed to decode aggregator index", "error", err, "aggregator index", *obj.ContributionAndProof.AggregatorIndex)
237+
return [96]byte{}, err
238+
}
239+
240+
aggregationBits, err := decodeBitVector128(*contribution.AggregationBits)
241+
if err != nil {
242+
d.log.Warn("failed to decode aggregation bits", "error", err, "aggregation bits", *contribution.AggregationBits)
243+
return [96]byte{}, err
244+
}
245+
246+
beaconBlockRoot, err := decodeRoot(*contribution.BeaconBlockRoot)
247+
if err != nil {
248+
d.log.Warn("failed to decode beacon block root", "error", err, "beacon block root", *contribution.BeaconBlockRoot)
249+
return [96]byte{}, err
250+
}
251+
252+
contributionSignature, err := decodeSignature(*contribution.Signature)
253+
if err != nil {
254+
d.log.Warn("failed to decode signature", "error", err, "signature", *contribution.Signature)
255+
return [96]byte{}, err
256+
}
257+
258+
slot, err := decodeSlot(*contribution.Slot)
259+
if err != nil {
260+
d.log.Warn("failed to decode slot", "error", err, "slot", *contribution.Slot)
261+
return [96]byte{}, err
262+
}
263+
264+
subcommitteeIndex, err := decodeUint64(*contribution.SubcommitteeIndex)
265+
if err != nil {
266+
d.log.Warn("failed to decode subcommittee index", "error", err, "subcommittee index", *contribution.SubcommitteeIndex)
267+
return [96]byte{}, err
268+
}
269+
270+
selectionProof, err := decodeSignature(*obj.ContributionAndProof.SelectionProof)
271+
if err != nil {
272+
d.log.Warn("failed to decode selection proof", "error", err, "selection proof", *obj.ContributionAndProof.SelectionProof)
273+
return [96]byte{}, err
274+
}
275+
276+
genesisValidatorsRoot, err := decodeRoot(obj.ForkInfo.GenesisValidatorsRoot)
277+
if err != nil {
278+
d.log.Warn("failed to decode genesis validators root", "error", err, "genesis validators root", obj.ForkInfo.GenesisValidatorsRoot)
279+
return [96]byte{}, err
280+
}
281+
282+
// Parse everything into ethpb.SyncCommitteeContributionAndProof
283+
contributionAndProof := &ethpb.ContributionAndProof{
284+
AggregatorIndex: aggregatorIndex,
285+
Contribution: &ethpb.SyncCommitteeContribution{
286+
AggregationBits: aggregationBits,
287+
BlockRoot: beaconBlockRoot[:],
288+
Signature: contributionSignature[:],
289+
Slot: slot,
290+
SubcommitteeIndex: subcommitteeIndex,
291+
},
292+
SelectionProof: selectionProof[:],
293+
}
294+
295+
hashTreeRoot, nErr := contributionAndProof.HashTreeRoot()
296+
if nErr != nil {
297+
d.log.Warn("failed to compute hash tree root", "error", nErr)
298+
return [96]byte{}, errors.ErrInternalServerError
299+
}
300+
301+
// Compute the domain
302+
domain, nErr := d.domain(domains.DomainSyncContributionAndProof, genesisValidatorsRoot[:], &obj.ForkInfo.Fork)
303+
if nErr != nil {
304+
d.log.Warn("failed to compute domain", "error", nErr)
305+
return [96]byte{}, errors.ErrInternalServerError
306+
}
307+
308+
account := d.getAccount(pubkey)
309+
if account == nil {
310+
d.log.Warn("account not found in cache", "pubkey", hex.EncodeToString(pubkey[:]))
311+
return [96]byte{}, errors.ErrPublicKeyNotFound
312+
}
313+
314+
signature, nErr := account.SignGeneric(ctx, hashTreeRoot[:], domain[:])
315+
if nErr != nil {
316+
d.log.Warn("failed to sign sync committee contribution and proof", "error", nErr)
317+
return [96]byte{}, errors.ErrInternalServerError
318+
}
319+
d.log.Debug("signed sync committee contribution and proof", "pubkey", hex.EncodeToString(pubkey[:]))
320+
return returnSignature(signature)
181321
}
182322

183323
func (d *DirkSigner) ValidatorRegistrationSigning(ctx context.Context, pubkey [48]byte, obj *api.ValidatorRegistrationSigning) ([96]byte, *errors.SignerError) {
@@ -199,12 +339,6 @@ func (d *DirkSigner) ValidatorRegistrationSigning(ctx context.Context, pubkey [4
199339
return [96]byte{}, errors.ErrBadRequest
200340
}
201341

202-
account := d.getAccount(pubkey)
203-
if account == nil {
204-
d.log.Warn("account not found in cache", "pubkey", hex.EncodeToString(pubkey[:]))
205-
return [96]byte{}, errors.ErrPublicKeyNotFound
206-
}
207-
208342
feeRecipient, err := feeRecipient(*obj.ValidatorRegistration.FeeRecipient)
209343
if err != nil {
210344
d.log.Warn("failed to decode fee recipient", "error", err, "fee recipient", *obj.ValidatorRegistration.FeeRecipient)
@@ -246,8 +380,15 @@ func (d *DirkSigner) ValidatorRegistrationSigning(ctx context.Context, pubkey [4
246380
Pubkey: pubkey[:],
247381
}
248382

383+
hashTreeRoot, nErr := validatorRegistration.HashTreeRoot()
384+
if nErr != nil {
385+
d.log.Warn("failed to compute hash tree root", "error", nErr)
386+
return [96]byte{}, errors.ErrInternalServerError
387+
}
388+
249389
// Compute the domain
250-
// For validator registrations, only genesis for version is needed
390+
// For validator registrations, only genesis fork version is needed
391+
// genesis validators root must be nil
251392
domain, nErr := signing.ComputeDomain(
252393
domains.DomainApplicationBuilder,
253394
d.genesisForkVersion,
@@ -258,10 +399,10 @@ func (d *DirkSigner) ValidatorRegistrationSigning(ctx context.Context, pubkey [4
258399
return [96]byte{}, errors.ErrInternalServerError
259400
}
260401

261-
hashTreeRoot, nErr := validatorRegistration.HashTreeRoot()
262-
if nErr != nil {
263-
d.log.Warn("failed to compute hash tree root", "error", nErr)
264-
return [96]byte{}, errors.ErrInternalServerError
402+
account := d.getAccount(pubkey)
403+
if account == nil {
404+
d.log.Warn("account not found in cache", "pubkey", hex.EncodeToString(pubkey[:]))
405+
return [96]byte{}, errors.ErrPublicKeyNotFound
265406
}
266407

267408
signature, nErr := account.SignGeneric(ctx, hashTreeRoot[:], domain[:])

pkg/dirksigner/fork.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package dirksigner
2+
3+
import (
4+
"github.com/OffchainLabs/prysm/v7/beacon-chain/core/signing"
5+
"github.com/jshufro/remote-signer-dirk-interop/internal/api"
6+
"github.com/jshufro/remote-signer-dirk-interop/internal/domains"
7+
"github.com/jshufro/remote-signer-dirk-interop/internal/errors"
8+
)
9+
10+
func (d *DirkSigner) domain(domainType domains.DomainType, genesisValidatorsRoot []byte, fork *api.Fork) ([]byte, error) {
11+
forkVersion, err := decodeHex(*fork.CurrentVersion)
12+
if err != nil {
13+
return nil, errors.ErrBadRequest
14+
}
15+
if len(forkVersion) != 4 {
16+
return nil, errors.ErrBadRequest
17+
}
18+
19+
out, nErr := signing.ComputeDomain(
20+
domainType,
21+
forkVersion,
22+
genesisValidatorsRoot[:],
23+
)
24+
if nErr != nil {
25+
d.log.Warn("failed to compute domain", "error", nErr)
26+
return nil, errors.ErrInternalServerError
27+
}
28+
return out, nil
29+
}

pkg/dirksigner/type-conversion.go

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ import (
88
"strconv"
99
"strings"
1010

11+
"github.com/OffchainLabs/go-bitfield"
12+
"github.com/OffchainLabs/prysm/v7/consensus-types/primitives"
1113
"github.com/attestantio/go-eth2-client/spec/bellatrix"
1214
"github.com/jshufro/remote-signer-dirk-interop/internal/errors"
1315
"github.com/rs/zerolog"
@@ -30,6 +32,55 @@ func decodeUint64(uint64Str string) (uint64, *errors.SignerError) {
3032
return u64, nil
3133
}
3234

35+
func decodeValidatorIndex(validatorIndex string) (primitives.ValidatorIndex, *errors.SignerError) {
36+
u64, err := strconv.ParseUint(validatorIndex, 10, 64)
37+
if err != nil {
38+
return 0, errors.ErrBadRequest
39+
}
40+
return primitives.ValidatorIndex(u64), nil
41+
}
42+
43+
func decodeBitVector128(bitVector128 string) (bitfield.Bitvector128, *errors.SignerError) {
44+
bytes, err := decodeHex(bitVector128)
45+
if err != nil {
46+
return bitfield.Bitvector128{}, err
47+
}
48+
if len(bytes) != 16 {
49+
return bitfield.Bitvector128{}, errors.ErrBadRequest
50+
}
51+
return bitfield.Bitvector128(bytes), nil
52+
}
53+
54+
func decodeRoot(root string) ([32]byte, *errors.SignerError) {
55+
bytes, err := decodeHex(root)
56+
if err != nil {
57+
return [32]byte{}, err
58+
}
59+
if len(bytes) != 32 {
60+
return [32]byte{}, errors.ErrBadRequest
61+
}
62+
return [32]byte(bytes), nil
63+
}
64+
65+
func decodeSignature(signature string) ([96]byte, *errors.SignerError) {
66+
bytes, err := decodeHex(signature)
67+
if err != nil {
68+
return [96]byte{}, err
69+
}
70+
if len(bytes) != 96 {
71+
return [96]byte{}, errors.ErrBadRequest
72+
}
73+
return [96]byte(bytes), nil
74+
}
75+
76+
func decodeSlot(slot string) (primitives.Slot, *errors.SignerError) {
77+
u64, err := decodeUint64(slot)
78+
if err != nil {
79+
return primitives.Slot(0), err
80+
}
81+
return primitives.Slot(u64), nil
82+
}
83+
3384
func feeRecipient(feeRecipient string) (bellatrix.ExecutionAddress, *errors.SignerError) {
3485
out := bellatrix.ExecutionAddress{}
3586
bytes, err := decodeHex(feeRecipient)

0 commit comments

Comments
 (0)