Skip to content

Commit

Permalink
core: add message root to signed data interface (#1349)
Browse files Browse the repository at this point in the history
Add explicit unsigned data message root function to signed data as a better way to ensure consistent unsigned data in parsigdb. Relying on json serialisation to check for consistent signed data is brittle.

category: refactor
ticket: #1347
  • Loading branch information
corverroos committed Oct 25, 2022
1 parent 830613d commit c1981a5
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 10 deletions.
85 changes: 75 additions & 10 deletions core/signeddata.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ func NewPartialSignature(sig Signature, shareIdx int) ParSignedData {
// Signature is a BLS12-381 Signature. It implements SignedData.
type Signature []byte

func (Signature) MessageRoot() ([32]byte, error) {
return [32]byte{}, errors.New("unsigned data root not supported by signature type")
}

func (s Signature) Clone() (SignedData, error) {
return s.clone(), nil
}
Expand Down Expand Up @@ -153,6 +157,20 @@ type VersionedSignedBeaconBlock struct {
spec.VersionedSignedBeaconBlock // Could subtype instead of embed, but aligning with Attestation that cannot subtype.
}

func (b VersionedSignedBeaconBlock) MessageRoot() ([32]byte, error) {
switch b.Version {
// No block nil checks since `NewVersionedSignedBeaconBlock` assumed.
case spec.DataVersionPhase0:
return b.Phase0.Message.HashTreeRoot()
case spec.DataVersionAltair:
return b.Altair.Message.HashTreeRoot()
case spec.DataVersionBellatrix:
return b.Bellatrix.Message.HashTreeRoot()
default:
panic("unknown version") // Note this is avoided by using `NewVersionedSignedBeaconBlock`.
}
}

func (b VersionedSignedBeaconBlock) Clone() (SignedData, error) {
return b.clone()
}
Expand Down Expand Up @@ -270,11 +288,6 @@ func (b *VersionedSignedBeaconBlock) UnmarshalJSON(input []byte) error {
return nil
}

// 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 {
Expand Down Expand Up @@ -302,6 +315,21 @@ func NewPartialVersionedSignedBlindedBeaconBlock(block *eth2api.VersionedSignedB
}, nil
}

// 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.
}

func (b VersionedSignedBlindedBeaconBlock) MessageRoot() ([32]byte, error) {
switch b.Version {
// No block nil checks since `NewVersionedSignedBlindedBeaconBlock` assumed.
case spec.DataVersionBellatrix:
return b.Bellatrix.Message.HashTreeRoot()
default:
panic("unknown version") // Note this is avoided by using `NewVersionedSignedBlindedBeaconBlock`.
}
}

func (b VersionedSignedBlindedBeaconBlock) Clone() (SignedData, error) {
return b.clone()
}
Expand Down Expand Up @@ -419,6 +447,10 @@ type Attestation struct {
eth2p0.Attestation
}

func (a Attestation) MessageRoot() ([32]byte, error) {
return a.Data.HashTreeRoot()
}

func (a Attestation) Clone() (SignedData, error) {
return a.clone()
}
Expand Down Expand Up @@ -476,6 +508,10 @@ type SignedVoluntaryExit struct {
eth2p0.SignedVoluntaryExit
}

func (e SignedVoluntaryExit) MessageRoot() ([32]byte, error) {
return e.Message.HashTreeRoot()
}

func (e SignedVoluntaryExit) Clone() (SignedData, error) {
return e.clone()
}
Expand Down Expand Up @@ -516,11 +552,6 @@ func (e *SignedVoluntaryExit) UnmarshalJSON(b []byte) error {
return e.SignedVoluntaryExit.UnmarshalJSON(b)
}

// VersionedSignedValidatorRegistration is a signed versioned validator (builder) registration and implements SignedData.
type VersionedSignedValidatorRegistration struct {
eth2api.VersionedSignedValidatorRegistration
}

// versionedRawValidatorRegistrationJSON is a custom VersionedSignedValidator serialiser.
type versionedRawValidatorRegistrationJSON struct {
Version int `json:"version"`
Expand Down Expand Up @@ -554,6 +585,20 @@ func NewPartialVersionedSignedValidatorRegistration(registration *eth2api.Versio
}, nil
}

// VersionedSignedValidatorRegistration is a signed versioned validator (builder) registration and implements SignedData.
type VersionedSignedValidatorRegistration struct {
eth2api.VersionedSignedValidatorRegistration
}

func (r VersionedSignedValidatorRegistration) MessageRoot() ([32]byte, error) {
switch r.Version {
case spec.BuilderVersionV1:
return r.V1.Message.HashTreeRoot()
default:
panic("unknown version")
}
}

func (r VersionedSignedValidatorRegistration) Clone() (SignedData, error) {
return r.clone()
}
Expand Down Expand Up @@ -660,6 +705,10 @@ type SignedRandao struct {
eth2util.SignedEpoch
}

func (s SignedRandao) MessageRoot() ([32]byte, error) {
return s.SignedEpoch.HashTreeRoot()
}

func (s SignedRandao) Signature() Signature {
return SigFromETH2(s.SignedEpoch.Signature)
}
Expand Down Expand Up @@ -717,6 +766,10 @@ type BeaconCommitteeSelection struct {
eth2exp.BeaconCommitteeSelection
}

func (s BeaconCommitteeSelection) MessageRoot() ([32]byte, error) {
return eth2util.SlotHashRoot(s.Slot)
}

func (s BeaconCommitteeSelection) Signature() Signature {
return SigFromETH2(s.SelectionProof)
}
Expand Down Expand Up @@ -772,6 +825,10 @@ type SignedAggregateAndProof struct {
eth2p0.SignedAggregateAndProof
}

func (s SignedAggregateAndProof) MessageRoot() ([32]byte, error) {
return s.Message.HashTreeRoot()
}

func (s SignedAggregateAndProof) Signature() Signature {
return SigFromETH2(s.SignedAggregateAndProof.Signature)
}
Expand Down Expand Up @@ -827,6 +884,10 @@ type SignedSyncMessage struct {
altair.SyncCommitteeMessage
}

func (s SignedSyncMessage) MessageRoot() ([32]byte, error) {
return s.BeaconBlockRoot, nil
}

func (s SignedSyncMessage) Signature() Signature {
return SigFromETH2(s.SyncCommitteeMessage.Signature)
}
Expand Down Expand Up @@ -869,6 +930,10 @@ type SignedSyncContributionAndProof struct {
altair.SignedContributionAndProof
}

func (s SignedSyncContributionAndProof) MessageRoot() ([32]byte, error) {
return s.Message.HashTreeRoot()
}

func (s SignedSyncContributionAndProof) Signature() Signature {
return SigFromETH2(s.SignedContributionAndProof.Signature)
}
Expand Down
6 changes: 6 additions & 0 deletions core/signeddata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,12 @@ func TestSignedDataSetSignature(t *testing.T) {
require.NoError(t, err)
require.NotEqual(t, clone.Signature(), test.data.Signature())
require.NotEmpty(t, clone.Signature())

msgRoot, err := test.data.MessageRoot()
require.NoError(t, err)
cloneRoot, err := test.data.MessageRoot()
require.NoError(t, err)
require.Equal(t, msgRoot, cloneRoot)
})
}
}
Expand Down
2 changes: 2 additions & 0 deletions core/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -371,6 +371,8 @@ type SignedData interface {
Signature() Signature
// SetSignature returns a copy of signed duty data with the signature replaced.
SetSignature(Signature) (SignedData, error)
// MessageRoot returns the unsigned data message root.
MessageRoot() ([32]byte, error)
// Clone returns a cloned copy of the SignedData. For an immutable core workflow architecture,
// remember to clone data when it leaves the current scope (sharing, storing, returning, etc).
Clone() (SignedData, error)
Expand Down

0 comments on commit c1981a5

Please sign in to comment.