Skip to content

Commit

Permalink
cluster: draft v1.6 lock with deposit data (#1813)
Browse files Browse the repository at this point in the history
Adds a new draft lock version v1.6 which includes deposit data in the lock's distributed validators.

category: feature
ticket: #1775
  • Loading branch information
corverroos authored Feb 16, 2023
1 parent d56cfd5 commit 14d6b7c
Show file tree
Hide file tree
Showing 18 changed files with 465 additions and 98 deletions.
9 changes: 9 additions & 0 deletions cluster/cluster_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -265,3 +265,12 @@ func RandomValidatorAddresses(n int) []ValidatorAddresses {

return resp
}

func RandomDepositData() DepositData {
return DepositData{
PubKey: testutil.RandomBytes48(),
WithdrawalCredentials: testutil.RandomBytes32(),
Amount: rand.Int(),
Signature: testutil.RandomBytes96(),
}
}
29 changes: 25 additions & 4 deletions cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ import (
//go:generate go test . -v -update -clean

const (
v1_6 = "v1.6.0"
v1_5 = "v1.5.0"
v1_4 = "v1.4.0"
v1_3 = "v1.3.0"
v1_2 = "v1.2.0"
Expand All @@ -60,7 +62,7 @@ func TestEncode(t *testing.T) {
},
}
// Add multiple validator address from v1.5 and later.
if version != v1_0 && version != v1_1 && version != v1_2 && version != v1_3 && version != v1_4 {
if !isAnyVersion(version, v1_0, v1_1, v1_2, v1_3, v1_4) {
opts = append(opts, cluster.WithMultiVAddrs(cluster.RandomValidatorAddresses(numVals)))
}

Expand Down Expand Up @@ -92,10 +94,10 @@ func TestEncode(t *testing.T) {
rand.New(rand.NewSource(0)),
opts...,
)
require.NoError(t, err)
testutil.RequireNoError(t, err)

// Definition version prior to v1.3.0 don't support EIP712 signatures.
if version == v1_0 || version == v1_1 || version == v1_2 {
if isAnyVersion(version, v1_0, v1_1, v1_2) {
for i := range definition.Operators {
// Set to empty values instead of nil to align with unmarshalled json.
definition.Operators[i].ConfigSignature = []byte{}
Expand All @@ -104,7 +106,7 @@ func TestEncode(t *testing.T) {
}

// Definition version prior to v1.4.0 don't support creator.
if version == v1_0 || version == v1_1 || version == v1_2 || version == v1_3 {
if isAnyVersion(version, v1_0, v1_1, v1_2, v1_3) {
definition.Creator = cluster.Creator{}
}

Expand Down Expand Up @@ -139,16 +141,25 @@ func TestEncode(t *testing.T) {
testutil.RandomBytes48(),
testutil.RandomBytes48(),
},
DepositData: cluster.RandomDepositData(),
}, {
PubKey: testutil.RandomBytes48(),
PubShares: [][]byte{
testutil.RandomBytes48(),
testutil.RandomBytes48(),
},
DepositData: cluster.RandomDepositData(),
},
},
}

// Lock version prior to v1.6.0 don't support DepositData.
if isAnyVersion(version, v1_0, v1_1, v1_2, v1_3, v1_4, v1_5) {
for i := range lock.Validators {
lock.Validators[i].DepositData = cluster.DepositData{}
}
}

t.Run("lock_json_"+vStr, func(t *testing.T) {
testutil.RequireGoldenJSON(t, lock,
testutil.WithFilename("cluster_lock_"+vStr+".json"))
Expand Down Expand Up @@ -232,3 +243,13 @@ func TestDefinitionPeers(t *testing.T) {
require.Equal(t, names[i], peer.Name)
}
}

func isAnyVersion(version string, list ...string) bool {
for _, v := range list {
if version == v {
return true
}
}

return false
}
18 changes: 9 additions & 9 deletions cluster/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -350,14 +350,14 @@ func (d Definition) MarshalJSON() ([]byte, error) {
}

switch {
case isV1x0(d2.Version) || isV1x1(d2.Version):
case isAnyVersion(d2.Version, v1_0, v1_1):
return marshalDefinitionV1x0or1(d2)
case isV1x2(d2.Version) || isV1x3(d2.Version):
case isAnyVersion(d2.Version, v1_2, v1_3):
// v1.2 and v1.3 has the same json format.
return marshalDefinitionV1x2or3(d2)
case isV1x4(d2.Version):
case isAnyVersion(d2.Version, v1_4):
return marshalDefinitionV1x4(d2)
case isV1x5(d2.Version):
case isAnyVersion(d2.Version, v1_5, v1_6):
return marshalDefinitionV1x5(d2)
default:
return nil, errors.New("unsupported version")
Expand All @@ -383,22 +383,22 @@ func (d *Definition) UnmarshalJSON(data []byte) error {
err error
)
switch {
case isV1x0(version.Version) || isV1x1(version.Version):
case isAnyVersion(version.Version, v1_0, v1_1):
def, err = unmarshalDefinitionV1x0or1(data)
if err != nil {
return err
}
case isV1x2(version.Version) || isV1x3(version.Version):
case isAnyVersion(version.Version, v1_2, v1_3):
def, err = unmarshalDefinitionV1x2or3(data)
if err != nil {
return err
}
case isV1x4(version.Version):
case isAnyVersion(version.Version, v1_4):
def, err = unmarshalDefinitionV1x4(data)
if err != nil {
return err
}
case isV1x5(version.Version):
case isAnyVersion(version.Version, v1_5, v1_6):
def, err = unmarshalDefinitionV1x5(data)
if err != nil {
return err
Expand Down Expand Up @@ -681,7 +681,7 @@ func unmarshalDefinitionV1x5(data []byte) (def Definition, err error) {
// supportEIP712Sigs returns true if the provided definition version supports EIP712 signatures.
// Note that Definition versions prior to v1.3.0 don't support EIP712 signatures.
func supportEIP712Sigs(version string) bool {
return !(isV1x0(version) || isV1x1(version) || isV1x2(version))
return !isAnyVersion(version, v1_0, v1_1, v1_2)
}

func eip712SigsPresent(operators []Operator) bool {
Expand Down
60 changes: 60 additions & 0 deletions cluster/deposit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright © 2022 Obol Labs Inc.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.

package cluster

// DepositData defines the deposit data to activate a validator.
// https://github.com/ethereum/consensus-specs/blob/dev/specs/phase0/beacon-chain.md#depositdata
type DepositData struct {
// PubKey is the validator public key.
PubKey []byte `json:"pubkey" ssz:"Bytes48" lock_hash:"0"`

// WithdrawalCredentials included in the deposit.
WithdrawalCredentials []byte `json:"withdrawal_credentials" ssz:"Bytes32" lock_hash:"1"`

// Amount is the amount in Gwei to be deposited.
Amount int `json:"amount" ssz:"uint64" lock_hash:"2"`

// Signature is the BLS signature of the deposit message (above three fields).
Signature []byte `json:"signature" ssz:"Bytes96" lock_hash:"3"`
}

// depositDataJSON is the json formatter of DepositData.
type depositDataJSON struct {
PubKey ethHex `json:"pubkey"`
WithdrawalCredentials ethHex `json:"withdrawal_credentials"`
Amount int `json:"amount,string"`
Signature ethHex `json:"signature"`
}

// depositDataToJSON converts DepositData to depositDataJSON.
func depositDataToJSON(d DepositData) depositDataJSON {
return depositDataJSON{
PubKey: d.PubKey,
WithdrawalCredentials: d.WithdrawalCredentials,
Amount: d.Amount,
Signature: d.Signature,
}
}

// depositDataFromJSON converts depositDataJSON to DepositData.
func depositDataFromJSON(d depositDataJSON) DepositData {
return DepositData{
PubKey: d.PubKey,
WithdrawalCredentials: d.WithdrawalCredentials,
Amount: d.Amount,
Signature: d.Signature,
}
}
43 changes: 43 additions & 0 deletions cluster/deposit_internal_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// Copyright © 2022 Obol Labs Inc.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the Free
// Software Foundation, either version 3 of the License, or (at your option)
// any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program. If not, see <http://www.gnu.org/licenses/>.

package cluster

import (
"encoding/json"
"testing"

eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"
"github.com/stretchr/testify/require"
)

func TestDepositJSON(t *testing.T) {
deposit := RandomDepositData()
depositJSON := depositDataToJSON(deposit)

eth2Deposit := &eth2p0.DepositData{
PublicKey: *(*eth2p0.BLSPubKey)(deposit.PubKey),
WithdrawalCredentials: deposit.WithdrawalCredentials,
Amount: eth2p0.Gwei(deposit.Amount),
Signature: *(*eth2p0.BLSSignature)(deposit.Signature),
}

b1, err := json.MarshalIndent(depositJSON, "", " ")
require.NoError(t, err)
b2, err := json.MarshalIndent(eth2Deposit, "", " ")
require.NoError(t, err)

require.Equal(t, b1, b2)
}
57 changes: 51 additions & 6 deletions cluster/distvalidator.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@ type DistValidator struct {
// PubShares are the public keys corresponding to each node's secret key share.
// It can be used to verify a partial signature created by any node in the cluster.
PubShares [][]byte `json:"public_shares,omitempty" ssz:"CompositeList[256],Bytes48" lock_hash:"1"`

// DepositData is the validator deposit data.
DepositData DepositData `json:"deposit_data,omitempty" ssz:"Composite" lock_hash:"2"`
}

// PublicKey returns the validator BLS group public key.
Expand All @@ -52,13 +55,20 @@ type distValidatorJSONv1x1 struct {
FeeRecipientAddress ethHex `json:"fee_recipient_address,omitempty"`
}

// distValidatorJSONv1x2 is the json formatter of DistValidator for versions v1.2.0 and later.
type distValidatorJSONv1x2 struct {
// distValidatorJSONv1x2to5 is the json formatter of DistValidator for versions v1.2.0 to v1.5.0.
type distValidatorJSONv1x2to5 struct {
PubKey ethHex `json:"distributed_public_key"`
PubShares []ethHex `json:"public_shares,omitempty"`
FeeRecipientAddress ethHex `json:"fee_recipient_address,omitempty"`
}

// distValidatorJSONv1x6 is the json formatter of DistValidator for versions v1.6.0 or later.
type distValidatorJSONv1x6 struct {
PubKey ethHex `json:"distributed_public_key"`
PubShares []ethHex `json:"public_shares,omitempty"`
DepositData depositDataJSON `json:"deposit_data,omitempty"`
}

func distValidatorsFromV1x1(distValidators []distValidatorJSONv1x1) []DistValidator {
var resp []DistValidator
for _, dv := range distValidators {
Expand All @@ -83,7 +93,7 @@ func distValidatorsToV1x1(distValidators []DistValidator) []distValidatorJSONv1x
return resp
}

func distValidatorsFromV1x2orLater(distValidators []distValidatorJSONv1x2) []DistValidator {
func distValidatorsFromV1x2to5(distValidators []distValidatorJSONv1x2to5) []DistValidator {
var resp []DistValidator
for _, dv := range distValidators {
var shares [][]byte
Expand All @@ -99,19 +109,54 @@ func distValidatorsFromV1x2orLater(distValidators []distValidatorJSONv1x2) []Dis
return resp
}

func distValidatorsToV1x2orLater(distValidators []DistValidator) []distValidatorJSONv1x2 {
var resp []distValidatorJSONv1x2
func distValidatorsToV1x2to5(distValidators []DistValidator) []distValidatorJSONv1x2to5 {
var resp []distValidatorJSONv1x2to5
for _, dv := range distValidators {
var shares []ethHex
for _, share := range dv.PubShares {
shares = append(shares, share)
}

resp = append(resp, distValidatorJSONv1x2{
resp = append(resp, distValidatorJSONv1x2to5{
PubKey: dv.PubKey,
PubShares: shares,
})
}

return resp
}

func distValidatorsFromV1x6orLater(distValidators []distValidatorJSONv1x6) []DistValidator {
var resp []DistValidator
for _, dv := range distValidators {
var shares [][]byte
for _, share := range dv.PubShares {
shares = append(shares, share)
}
resp = append(resp, DistValidator{
PubKey: dv.PubKey,
PubShares: shares,
DepositData: depositDataFromJSON(dv.DepositData),
})
}

return resp
}

func distValidatorsToV1x6OrLater(distValidators []DistValidator) []distValidatorJSONv1x6 {
var resp []distValidatorJSONv1x6
for _, dv := range distValidators {
var shares []ethHex
for _, share := range dv.PubShares {
shares = append(shares, share)
}

resp = append(resp, distValidatorJSONv1x6{
PubKey: dv.PubKey,
PubShares: shares,
DepositData: depositDataToJSON(dv.DepositData),
})
}

return resp
}
Loading

0 comments on commit 14d6b7c

Please sign in to comment.