Skip to content

Commit

Permalink
core: add changes for signed data for builder proposer (#820)
Browse files Browse the repository at this point in the history
- signed data implementation of blinded blocker proposer
- implementation of sigagg test

An issue with pre commit check specifically golangci-lint "dupl", appeared to be giving a false positive

`core/sigagg/sigagg_test.go:346: 346-400 lines are duplicate of `core/sigagg/sigagg_test.go:261-315` (dupl)`

This issue golangci/golangci-lint#1372 discussed updating the dupl threshold in the config which I've done. Not sure if I'm missing something and this is a legitimate dupl or if this has happened to you guys before and you have a better solution. 

category: feature
ticket: #809
  • Loading branch information
ciaranmcveigh5 authored Jul 20, 2022
1 parent 97add9c commit abea4c0
Show file tree
Hide file tree
Showing 3 changed files with 219 additions and 1 deletion.
3 changes: 3 additions & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ linters-settings:
cyclop:
max-complexity: 15
skip-tests: true
dupl:
# tokens count to trigger issue, 150 by default
threshold: 350
exhaustive:
default-signifies-exhaustive: true
forbidigo:
Expand Down
87 changes: 87 additions & 0 deletions core/sigagg/sigagg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"crypto/rand"
"testing"

eth2api "github.com/attestantio/go-eth2-client/api"
eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
Expand Down Expand Up @@ -312,3 +314,88 @@ func TestSigAgg_DutyProposer(t *testing.T) {
})
}
}

func TestSigAgg_DutyBuilderProposer(t *testing.T) {
ctx := context.Background()

const (
threshold = 3
peers = 4
)

// Generate private shares
tss, secrets, err := tbls.GenerateTSS(threshold, peers, rand.Reader)
require.NoError(t, err)

tests := []struct {
name string
block *eth2api.VersionedSignedBlindedBeaconBlock
}{
{
name: "bellatrix block",
block: &eth2api.VersionedSignedBlindedBeaconBlock{
Version: spec.DataVersionBellatrix,
Bellatrix: &eth2v1.SignedBlindedBeaconBlock{
Message: testutil.RandomBellatrixBlindedBeaconBlock(t),
Signature: testutil.RandomEth2Signature(),
},
},
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
// Ignoring Domain for this test
msg, err := test.block.Root()
require.NoError(t, err)

// Create partial signatures (in two formats)
var (
parsigs []core.ParSignedData
psigs []*bls_sig.PartialSignature
)
for _, secret := range secrets {
psig, err := tbls.PartialSign(secret, msg[:])
require.NoError(t, err)

block, err := core.NewVersionedSignedBlindedBeaconBlock(test.block)
require.NoError(t, err)

sig := tblsconv.SigToCore(tblsconv.SigFromPartial(psig))
signed, err := block.SetSignature(sig)
require.NoError(t, err)
require.Equal(t, sig, signed.Signature())

psigs = append(psigs, psig)
parsigs = append(parsigs, core.ParSignedData{
SignedData: signed,
ShareIdx: int(psig.Identifier),
})
}

// Create expected aggregated signature
aggSig, err := tbls.Aggregate(psigs)
require.NoError(t, err)
expect := tblsconv.SigToCore(aggSig)

agg := sigagg.New(threshold)

// Assert output
agg.Subscribe(func(_ context.Context, _ core.Duty, _ core.PubKey, aggData core.SignedData) error {
require.Equal(t, expect, aggData.Signature())
sig, err := tblsconv.SigFromCore(aggData.Signature())
require.NoError(t, err)

ok, err := tbls.Verify(tss.PublicKey(), msg[:], sig)
require.NoError(t, err)
require.True(t, ok)

return nil
})

// Run aggregation
err = agg.Aggregate(ctx, core.Duty{Type: core.DutyProposer}, "", parsigs)
require.NoError(t, err)
})
}
}
130 changes: 129 additions & 1 deletion core/signeddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ package core
import (
"encoding/json"

eth2api "github.com/attestantio/go-eth2-client/api"
eth2v1 "github.com/attestantio/go-eth2-client/api/v1"
"github.com/attestantio/go-eth2-client/spec"
"github.com/attestantio/go-eth2-client/spec/altair"
"github.com/attestantio/go-eth2-client/spec/bellatrix"
Expand All @@ -31,6 +33,7 @@ var (
_ SignedData = Attestation{}
_ SignedData = Signature{}
_ SignedData = SignedVoluntaryExit{}
_ SignedData = VersionedSignedBlindedBeaconBlock{}
)

// SigFromETH2 returns a new signature from eth2 phase0 BLSSignature.
Expand Down Expand Up @@ -259,7 +262,132 @@ func (b *VersionedSignedBeaconBlock) UnmarshalJSON(input []byte) error {
return nil
}

// versionedRawBlockJSON is a custom VersionedSignedBeaconBlock serialiser.
// VersionedSignedBlindedBeaconBlock is a signed versioned blinded beacon block and implements SignedData.
type VersionedSignedBlindedBeaconBlock struct {
eth2api.VersionedSignedBlindedBeaconBlock // Could subtype instead of embed, but aligning with Attestation that cannot subtype.
}

// NewVersionedSignedBlindedBeaconBlock validates and returns a new wrapped VersionedSignedBlindedBeaconBlock.
func NewVersionedSignedBlindedBeaconBlock(block *eth2api.VersionedSignedBlindedBeaconBlock) (VersionedSignedBlindedBeaconBlock, error) {
switch block.Version {
case spec.DataVersionBellatrix:
if block.Bellatrix == nil {
return VersionedSignedBlindedBeaconBlock{}, errors.New("no bellatrix block")
}
default:
return VersionedSignedBlindedBeaconBlock{}, errors.New("unknown version")
}

return VersionedSignedBlindedBeaconBlock{VersionedSignedBlindedBeaconBlock: *block}, nil
}

// NewPartialVersionedSignedBlindedBeaconBlock is a convenience function that returns a new partial signed block.
func NewPartialVersionedSignedBlindedBeaconBlock(block *eth2api.VersionedSignedBlindedBeaconBlock, shareIdx int) (ParSignedData, error) {
wrap, err := NewVersionedSignedBlindedBeaconBlock(block)
if err != nil {
return ParSignedData{}, err
}

return ParSignedData{
SignedData: wrap,
ShareIdx: shareIdx,
}, nil
}

func (b VersionedSignedBlindedBeaconBlock) Clone() (SignedData, error) {
return b.clone()
}

// clone returns a copy of the VersionedSignedBlindedBeaconBlock.
// It is similar to Clone that returns the SignedData interface.
//nolint:revive // similar method names.
func (b VersionedSignedBlindedBeaconBlock) clone() (VersionedSignedBlindedBeaconBlock, error) {
var resp VersionedSignedBlindedBeaconBlock
err := cloneJSONMarshaler(b, &resp)
if err != nil {
return VersionedSignedBlindedBeaconBlock{}, errors.Wrap(err, "clone block")
}

return resp, nil
}

func (b VersionedSignedBlindedBeaconBlock) Signature() Signature {
switch b.Version {
// No block nil checks since `NewVersionedSignedBlindedBeaconBlock` assumed.
case spec.DataVersionBellatrix:
return SigFromETH2(b.Bellatrix.Signature)
default:
panic("unknown version") // Note this is avoided by using `NewVersionedSignedBlindedBeaconBlock`.
}
}

func (b VersionedSignedBlindedBeaconBlock) SetSignature(sig Signature) (SignedData, error) {
resp, err := b.clone()
if err != nil {
return nil, err
}

switch resp.Version {
// No block nil checks since `NewVersionedSignedBlindedBeaconBlock` assumed.
case spec.DataVersionBellatrix:
resp.Bellatrix.Signature = sig.ToETH2()
default:
return nil, errors.New("unknown type")
}

return resp, nil
}

func (b VersionedSignedBlindedBeaconBlock) MarshalJSON() ([]byte, error) {
var marshaller json.Marshaler
switch b.Version {
// No block nil checks since `NewVersionedSignedBlindedBeaconBlock` assumed.
case spec.DataVersionBellatrix:
marshaller = b.VersionedSignedBlindedBeaconBlock.Bellatrix
default:
return nil, errors.New("unknown version")
}

block, err := marshaller.MarshalJSON()
if err != nil {
return nil, errors.Wrap(err, "marshal block")
}

resp, err := json.Marshal(versionedRawBlockJSON{
Version: int(b.Version),
Block: block,
})
if err != nil {
return nil, errors.Wrap(err, "marshal wrapper")
}

return resp, nil
}

func (b *VersionedSignedBlindedBeaconBlock) UnmarshalJSON(input []byte) error {
var raw versionedRawBlockJSON
if err := json.Unmarshal(input, &raw); err != nil {
return errors.Wrap(err, "unmarshal block")
}

resp := eth2api.VersionedSignedBlindedBeaconBlock{Version: spec.DataVersion(raw.Version)}
switch resp.Version {
case spec.DataVersionBellatrix:
block := new(eth2v1.SignedBlindedBeaconBlock)
if err := json.Unmarshal(raw.Block, &block); err != nil {
return errors.Wrap(err, "unmarshal bellatrix")
}
resp.Bellatrix = block
default:
return errors.New("unknown version")
}

b.VersionedSignedBlindedBeaconBlock = resp

return nil
}

// versionedRawBlockJSON is a custom VersionedSignedBeaconBlock or VersionedSignedBlindedBeaconBlock serialiser.
type versionedRawBlockJSON struct {
Version int `json:"version"`
Block json.RawMessage `json:"block"`
Expand Down

0 comments on commit abea4c0

Please sign in to comment.