Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cluster: create draft 1.5 definition #1674

Merged
merged 8 commits into from
Jan 24, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cluster/cluster_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ func TestDefinitionVerify(t *testing.T) {
secret1, op1 := randomOperator(t)
secret3, creator := randomCreator(t)

t.Run("verify definition v1.4", func(t *testing.T) {
t.Run("verify definition v1.4 and v1.5", func(t *testing.T) {
definition := randomDefinition(t, creator, op0, op1)

definition, err = signCreator(secret3, definition)
Expand Down
16 changes: 16 additions & 0 deletions cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,22 @@ func TestEncode(t *testing.T) {
func(d *cluster.Definition) {
d.Version = version
d.Timestamp = "2022-07-19T18:19:58+02:00" // Make deterministic

if version == "v1.5.0" {
d.Validators = []cluster.Validator{
{
FeeRecipientAddress: testutil.RandomETHAddress(),
WithdrawalAddress: testutil.RandomETHAddress(),
},
{
FeeRecipientAddress: testutil.RandomETHAddress(),
WithdrawalAddress: testutil.RandomETHAddress(),
},
}

d.FeeRecipientAddress = ""
d.WithdrawalAddress = ""
}
},
)
require.NoError(t, err)
Expand Down
97 changes: 94 additions & 3 deletions cluster/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ type Definition struct {
// Threshold required for signature reconstruction. Defaults to safe value for number of nodes/peers.
Threshold int `json:"threshold" ssz:"uint64" config_hash:"5" definition_hash:"5"`

// FeeRecipientAddress 20 byte Ethereum address.
// FeeRecipientAddress 20 byte Ethereum address. Deprecated: use Validators instead.
FeeRecipientAddress string `json:"fee_recipient_address,0xhex" ssz:"Bytes20" config_hash:"6" definition_hash:"6"`

// WithdrawalAddress 20 byte Ethereum address.
// WithdrawalAddress 20 byte Ethereum address. Deprecated: use Validators instead.
WithdrawalAddress string `json:"withdrawal_address,0xhex" ssz:"Bytes20" config_hash:"7" definition_hash:"7"`

// DKGAlgorithm to use for key generation. Max 32 chars.
Expand All @@ -125,13 +125,24 @@ type Definition struct {
// Creator identifies the creator of a cluster definition. They may also be an operator.
Creator Creator `json:"creator" ssz:"Composite" config_hash:"11" definition_hash:"11"`

// Validators define the configuration required by each cluster validator.
Validators []Validator `json:"validators" ssz:"CompositeList[65536]" config_hash:"12" definition_hash:"12"`

// ConfigHash uniquely identifies a cluster definition excluding operator ENRs and signatures.
ConfigHash []byte `json:"config_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"12"`
ConfigHash []byte `json:"config_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"13"`

// DefinitionHash uniquely identifies a cluster definition including operator ENRs and signatures.
DefinitionHash []byte `json:"definition_hash,0xhex" ssz:"Bytes32" config_hash:"-" definition_hash:"-"`
}

type Validator struct {
LukeHackett12 marked this conversation as resolved.
Show resolved Hide resolved
// FeeRecipientAddress 20 byte Ethereum address.
FeeRecipientAddress string `json:"fee_recipient_address,0xhex" ssz:"Bytes20" config_hash:"0" definition_hash:"0"`

// WithdrawalAddress 20 byte Ethereum address.
WithdrawalAddress string `json:"withdrawal_address,0xhex" ssz:"Bytes20" config_hash:"1" definition_hash:"1"`
}

// NodeIdx returns the node index for the peer.
func (d Definition) NodeIdx(pID peer.ID) (NodeIdx, error) {
peers, err := d.Peers()
Expand Down Expand Up @@ -312,6 +323,8 @@ func (d Definition) MarshalJSON() ([]byte, error) {
return marshalDefinitionV1x2or3(d)
case isV1x4(d.Version):
return marshalDefinitionV1x4(d)
case isV1x5(d.Version):
return marshalDefinitionV1x5(d)
default:
return nil, errors.New("unsupported version")
}
Expand Down Expand Up @@ -351,6 +364,11 @@ func (d *Definition) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
case isV1x5(version.Version):
def, err = unmarshalDefinitionV1x5(data)
if err != nil {
return err
}
default:
return errors.New("unsupported version")
}
Expand Down Expand Up @@ -457,6 +475,32 @@ func marshalDefinitionV1x4(def Definition) ([]byte, error) {
return resp, nil
}

func marshalDefinitionV1x5(def Definition) ([]byte, error) {
resp, err := json.Marshal(definitionJSONv1x5{
Name: def.Name,
UUID: def.UUID,
Version: def.Version,
Timestamp: def.Timestamp,
NumValidators: def.NumValidators,
Threshold: def.Threshold,
DKGAlgorithm: def.DKGAlgorithm,
Validators: def.Validators,
ForkVersion: def.ForkVersion,
ConfigHash: def.ConfigHash,
DefinitionHash: def.DefinitionHash,
Operators: operatorsToV1x2orLater(def.Operators),
Creator: creatorJSON{
Address: def.Creator.Address,
ConfigSignature: def.Creator.ConfigSignature,
},
})
if err != nil {
return nil, errors.Wrap(err, "marshal definition")
}

return resp, nil
}

func unmarshalDefinitionV1x0or1(data []byte) (def Definition, err error) {
var defJSON definitionJSONv1x0or1
if err := json.Unmarshal(data, &defJSON); err != nil {
Expand Down Expand Up @@ -543,6 +587,36 @@ func unmarshalDefinitionV1x4(data []byte) (def Definition, err error) {
}, nil
}

func unmarshalDefinitionV1x5(data []byte) (def Definition, err error) {
var defJSON definitionJSONv1x5
if err := json.Unmarshal(data, &defJSON); err != nil {
return Definition{}, errors.Wrap(err, "unmarshal definition v1v2")
LukeHackett12 marked this conversation as resolved.
Show resolved Hide resolved
}

if len(defJSON.Validators) != defJSON.NumValidators {
return Definition{}, errors.Wrap(err, "insufficient validators")
}

return Definition{
Name: defJSON.Name,
UUID: defJSON.UUID,
Version: defJSON.Version,
Timestamp: defJSON.Timestamp,
NumValidators: defJSON.NumValidators,
Threshold: defJSON.Threshold,
Validators: defJSON.Validators,
DKGAlgorithm: defJSON.DKGAlgorithm,
ForkVersion: defJSON.ForkVersion,
ConfigHash: defJSON.ConfigHash,
DefinitionHash: defJSON.DefinitionHash,
Operators: operatorsFromV1x2orLater(defJSON.Operators),
Creator: Creator{
Address: defJSON.Creator.Address,
ConfigSignature: defJSON.Creator.ConfigSignature,
},
}, nil
}

// 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 {
Expand Down Expand Up @@ -611,6 +685,23 @@ type definitionJSONv1x4 struct {
DefinitionHash ethHex `json:"definition_hash"`
}

// definitionJSONv1x5 is the json formatter of Definition for versions v1.5.
type definitionJSONv1x5 struct {
Name string `json:"name,omitempty"`
Creator creatorJSON `json:"creator"`
Operators []operatorJSONv1x2orLater `json:"operators"`
UUID string `json:"uuid"`
Version string `json:"version"`
Timestamp string `json:"timestamp,omitempty"`
NumValidators int `json:"num_validators"`
Threshold int `json:"threshold"`
Validators []Validator `json:"validators"`
DKGAlgorithm string `json:"dkg_algorithm"`
ForkVersion ethHex `json:"fork_version"`
ConfigHash ethHex `json:"config_hash"`
DefinitionHash ethHex `json:"definition_hash"`
}

// Creator identifies the creator of a cluster definition.
// Note the following struct tag meanings:
// - json: json field name. Suffix 0xhex indicates bytes are formatted as 0x prefixed hex strings.
Expand Down
23 changes: 22 additions & 1 deletion cluster/lock.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func (l Lock) MarshalJSON() ([]byte, error) {
switch {
case isV1x0(l.Version) || isV1x1(l.Version):
return marshalLockV1x0or1(l, lockHash)
case isV1x2(l.Version) || isV1x3(l.Version) || isV1x4(l.Version):
case isV1x2(l.Version) || isV1x3(l.Version) || isV1x4(l.Version) || isV1x5(l.Version):
return marshalLockV1x2orLater(l, lockHash)
default:
return nil, errors.New("unsupported version")
Expand Down Expand Up @@ -93,6 +93,11 @@ func (l *Lock) UnmarshalJSON(data []byte) error {
if err != nil {
return err
}
case isAnyVersion(version.Definition.Version, v1_5):
LukeHackett12 marked this conversation as resolved.
Show resolved Hide resolved
lock, err = unmarshalLockV1x5orLater(data)
if err != nil {
return err
}
default:
return errors.New("unsupported version")
}
Expand Down Expand Up @@ -238,6 +243,22 @@ func unmarshalLockV1x2orLater(data []byte) (lock Lock, err error) {
return lock, nil
}

func unmarshalLockV1x5orLater(data []byte) (lock Lock, err error) {
var lockJSON lockJSONv1x2orLater
if err := json.Unmarshal(data, &lockJSON); err != nil {
return Lock{}, errors.Wrap(err, "unmarshal definition")
}

lock = Lock{
Definition: lockJSON.Definition,
Validators: distValidatorsFromV1x2orLater(lockJSON.Validators),
SignatureAggregate: lockJSON.SignatureAggregate,
LockHash: lockJSON.LockHash,
}

return lock, nil
}

// lockJSONv1x0or1 is the json formatter of Lock for versions v1.0.0 and v1.1.0.
type lockJSONv1x0or1 struct {
Definition Definition `json:"cluster_definition"`
Expand Down
Loading