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: refactor base64 fields to base58 #851

Merged
merged 8 commits into from
Jul 26, 2022
Merged
Show file tree
Hide file tree
Changes from 4 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
3 changes: 3 additions & 0 deletions cluster/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,9 @@ func TestBackwardsCompatability(t *testing.T) {
{
version: "v1.1.0",
},
{
version: "v1.2.0",
},
// Note: Add testdata files for newer versions when bumped.
}
for _, test := range tests {
Expand Down
253 changes: 181 additions & 72 deletions cluster/definition.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,18 @@ import (
)

const (
definitionVersion = "v1.1.0"
dkgAlgo = "default"
currentVersion = v1_1
dkgAlgo = "default"

v1_2 = "v1.2.0" // WIP
v1_1 = "v1.1.0"
v1_0 = "v1.0.0"
Copy link
Contributor

@dB2510 dB2510 Jul 26, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add enum for these versions, which would work as order of priority of versions at the time of comparison, which can be generated by go generate like we have in tracker as component enum

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will also make it easy to add versions in future with enum approach

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, good idea

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually not sure this is going to work, since we need to do string comparison, which doesn't work nice with enums.

)

var supportedDefVersions = map[string]bool{
definitionVersion: true,
"v1.0.0": true,
var supportedVersions = map[string]bool{
v1_2: true,
v1_1: true,
v1_0: true,
}

// NodeIdx represents the index of a node/peer/share in the cluster as operator order in cluster definition.
Expand All @@ -59,7 +64,7 @@ func NewDefinition(
random io.Reader,
) Definition {
s := Definition{
Version: definitionVersion,
Version: currentVersion,
Name: name,
UUID: uuid(random),
Timestamp: time.Now().Format(time.RFC3339),
Expand Down Expand Up @@ -87,7 +92,7 @@ type Definition struct {
Version string

// Timestamp is the human readable timestamp of this definition.
// Note this was added in v1.0.1, so may be empty for older versions.
// Note this was added in v1.1.0, so may be empty for older versions.
corverroos marked this conversation as resolved.
Show resolved Hide resolved
Timestamp string

// NumValidators is the number of DVs (n*32ETH) to be created in the cluster lock file.
Expand Down Expand Up @@ -223,8 +228,8 @@ func (d Definition) HashTreeRootWith(hh *ssz.Hasher) error {
hh.MerkleizeWithMixin(subIndx, num, num)
}

// Field (10) 'timestamp' (optional for backwards compatibility)
if d.Timestamp != "" {
// Field (10) 'timestamp' (optional only added from v1.1.0)
if d.Version != v1_0 {
hh.PutBytes([]byte(d.Timestamp))
}

Expand All @@ -233,6 +238,40 @@ func (d Definition) HashTreeRootWith(hh *ssz.Hasher) error {
return nil
}

// Peers returns the operators as a slice of p2p peers.
func (d Definition) Peers() ([]p2p.Peer, error) {
var resp []p2p.Peer
for i, operator := range d.Operators {
record, err := p2p.DecodeENR(operator.ENR)
if err != nil {
return nil, err
}

p, err := p2p.NewPeer(record, i)
if err != nil {
return nil, err
}

resp = append(resp, p)
}

return resp, nil
}

// PeerIDs is a convenience function that returns the operators p2p peer IDs.
func (d Definition) PeerIDs() ([]peer.ID, error) {
peers, err := d.Peers()
if err != nil {
return nil, err
}
var resp []peer.ID
for _, p := range peers {
resp = append(resp, p.ID)
}

return resp, nil
}

func (d Definition) MarshalJSON() ([]byte, error) {
// Marshal config hash
configHash, err := d.ConfigHash()
Expand All @@ -246,27 +285,51 @@ func (d Definition) MarshalJSON() ([]byte, error) {
return nil, errors.Wrap(err, "definition hash")
}

// Marshal json version of lock
resp, err := json.Marshal(definitionJSON{
Name: d.Name,
UUID: d.UUID,
Version: d.Version,
Timestamp: d.Timestamp,
NumValidators: d.NumValidators,
Threshold: d.Threshold,
FeeRecipientAddress: d.FeeRecipientAddress,
WithdrawalAddress: d.WithdrawalAddress,
DKGAlgorithm: d.DKGAlgorithm,
ForkVersion: d.ForkVersion,
Operators: d.Operators,
ConfigHash: configHash[:],
DefinitionHash: defHash[:],
})
if err != nil {
return nil, errors.Wrap(err, "marshal lock")
}
if isJSONv1x1(d.Version) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

switch case would be more cleaner + readable imo

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fair

resp, err := json.Marshal(definitionJSONv1x1{
Name: d.Name,
UUID: d.UUID,
Version: d.Version,
Timestamp: d.Timestamp,
NumValidators: d.NumValidators,
Threshold: d.Threshold,
FeeRecipientAddress: d.FeeRecipientAddress,
WithdrawalAddress: d.WithdrawalAddress,
DKGAlgorithm: d.DKGAlgorithm,
ForkVersion: d.ForkVersion,
Operators: operatorsToV1x1(d.Operators),
ConfigHash: configHash[:],
DefinitionHash: defHash[:],
})
if err != nil {
return nil, errors.Wrap(err, "marshal definition")
}

return resp, nil
return resp, nil
} else if isJSONv1x2(d.Version) {
resp, err := json.Marshal(definitionJSONv1x2{
Name: d.Name,
UUID: d.UUID,
Version: d.Version,
Timestamp: d.Timestamp,
NumValidators: d.NumValidators,
Threshold: d.Threshold,
FeeRecipientAddress: d.FeeRecipientAddress,
WithdrawalAddress: d.WithdrawalAddress,
DKGAlgorithm: d.DKGAlgorithm,
ForkVersion: d.ForkVersion,
Operators: operatorsToV1x2(d.Operators),
ConfigHash: configHash[:],
DefinitionHash: defHash[:],
})
if err != nil {
return nil, errors.Wrap(err, "marshal definition")
}

return resp, nil
} else {
return nil, errors.New("unsupported version")
}
}

func (d *Definition) UnmarshalJSON(data []byte) error {
Expand All @@ -276,16 +339,26 @@ func (d *Definition) UnmarshalJSON(data []byte) error {
}{}
if err := json.Unmarshal(data, &version); err != nil {
return errors.Wrap(err, "unmarshal version")
} else if !supportedDefVersions[version.Version] {
} else if !supportedVersions[version.Version] {
return errors.New("unsupported definition version",
z.Str("version", version.Version),
z.Any("supported", supportedDefVersions),
z.Any("supported", supportedVersions),
)
}

var defJSON definitionJSON
if isJSONv1x1(version.Version) {
return d.unmarshalV1x1(data)
} else if isJSONv1x2(version.Version) {
return d.unmarshalV1x2(data)
} else {
return errors.New("unsupported version")
}
}

func (d *Definition) unmarshalV1x1(data []byte) error {
var defJSON definitionJSONv1x1
if err := json.Unmarshal(data, &defJSON); err != nil {
return errors.Wrap(err, "unmarshal definition")
return errors.Wrap(err, "unmarshal definition v1_1")
}

def := Definition{
Expand All @@ -299,7 +372,7 @@ func (d *Definition) UnmarshalJSON(data []byte) error {
WithdrawalAddress: defJSON.WithdrawalAddress,
DKGAlgorithm: defJSON.DKGAlgorithm,
ForkVersion: defJSON.ForkVersion,
Operators: defJSON.Operators,
Operators: operatorsFromV1x1(defJSON.Operators),
}

// Verify config_hash
Expand Down Expand Up @@ -327,53 +400,89 @@ func (d *Definition) UnmarshalJSON(data []byte) error {
return nil
}

// Peers returns the operators as a slice of p2p peers.
func (d Definition) Peers() ([]p2p.Peer, error) {
var resp []p2p.Peer
for i, operator := range d.Operators {
record, err := p2p.DecodeENR(operator.ENR)
if err != nil {
return nil, err
}
func (d *Definition) unmarshalV1x2(data []byte) error {
var defJSON definitionJSONv1x2
if err := json.Unmarshal(data, &defJSON); err != nil {
return errors.Wrap(err, "unmarshal definition v1v2")
}

p, err := p2p.NewPeer(record, i)
if err != nil {
return nil, err
}
def := Definition{
Name: defJSON.Name,
UUID: defJSON.UUID,
Version: defJSON.Version,
Timestamp: defJSON.Timestamp,
NumValidators: defJSON.NumValidators,
Threshold: defJSON.Threshold,
FeeRecipientAddress: defJSON.FeeRecipientAddress,
WithdrawalAddress: defJSON.WithdrawalAddress,
DKGAlgorithm: defJSON.DKGAlgorithm,
ForkVersion: defJSON.ForkVersion,
Operators: operatorsFromV1x2(defJSON.Operators),
}

resp = append(resp, p)
// Verify config_hash
configHash, err := def.ConfigHash()
if err != nil {
return errors.Wrap(err, "config hash")
}

return resp, nil
}
if !bytes.Equal(defJSON.ConfigHash, configHash[:]) {
return errors.New("invalid config hash")
}

// PeerIDs is a convenience function that returns the operators p2p peer IDs.
func (d Definition) PeerIDs() ([]peer.ID, error) {
peers, err := d.Peers()
// Verify definition_hash
defHash, err := def.HashTreeRoot()
if err != nil {
return nil, err
return errors.Wrap(err, "definition hash")
}
var resp []peer.ID
for _, p := range peers {
resp = append(resp, p.ID)

if !bytes.Equal(defJSON.DefinitionHash, defHash[:]) {
return errors.New("invalid definition hash")
}

return resp, nil
*d = def

return nil
}

// definitionJSONv1x1 is the json formatter of Definition for versions v1.0.0 and v1.1.1.
type definitionJSONv1x1 struct {
Name string `json:"name,omitempty"`
Operators []operatorJSONv1x1 `json:"operators"`
UUID string `json:"uuid"`
Version string `json:"version"`
Timestamp string `json:"timestamp,omitempty"`
NumValidators int `json:"num_validators"`
Threshold int `json:"threshold"`
FeeRecipientAddress string `json:"fee_recipient_address,omitempty"`
WithdrawalAddress string `json:"withdrawal_address,omitempty"`
DKGAlgorithm string `json:"dkg_algorithm"`
ForkVersion string `json:"fork_version"`
ConfigHash []byte `json:"config_hash"`
DefinitionHash []byte `json:"definition_hash"`
}

// definitionJSONv1x2 is the json formatter of Definition for versions v1.2.0 and later.
type definitionJSONv1x2 struct {
Name string `json:"name,omitempty"`
Operators []operatorJSONv1x2 `json:"operators"`
UUID string `json:"uuid"`
Version string `json:"version"`
Timestamp string `json:"timestamp,omitempty"`
NumValidators int `json:"num_validators"`
Threshold int `json:"threshold"`
FeeRecipientAddress string `json:"fee_recipient_address,omitempty"`
WithdrawalAddress string `json:"withdrawal_address,omitempty"`
DKGAlgorithm string `json:"dkg_algorithm"`
ForkVersion string `json:"fork_version"`
ConfigHash base58 `json:"config_hash"`
DefinitionHash base58 `json:"definition_hash"`
}

func isJSONv1x1(version string) bool {
return version == v1_0 || version == v1_1
}

// definitionJSON is the json formatter of Definition.
type definitionJSON struct {
Name string `json:"name,omitempty"`
Operators []Operator `json:"operators"`
UUID string `json:"uuid"`
Version string `json:"version"`
Timestamp string `json:"timestamp"`
NumValidators int `json:"num_validators"`
Threshold int `json:"threshold"`
FeeRecipientAddress string `json:"fee_recipient_address,omitempty"`
WithdrawalAddress string `json:"withdrawal_address,omitempty"`
DKGAlgorithm string `json:"dkg_algorithm"`
ForkVersion string `json:"fork_version"`
ConfigHash []byte `json:"config_hash"`
DefinitionHash []byte `json:"definition_hash"`
func isJSONv1x2(version string) bool {
return version == v1_2
}
Loading