Skip to content

Commit

Permalink
cmd: --partial-deposits flag
Browse files Browse the repository at this point in the history
  • Loading branch information
pinebit committed Jan 24, 2024
1 parent 4d66a10 commit 99686c7
Show file tree
Hide file tree
Showing 8 changed files with 184 additions and 1 deletion.
46 changes: 46 additions & 0 deletions cluster/deposit.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@

package cluster

import (
eth2p0 "github.com/attestantio/go-eth2-client/spec/phase0"

"github.com/obolnetwork/charon/app/errors"
"github.com/obolnetwork/charon/app/z"
)

// 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 {
Expand Down Expand Up @@ -45,3 +52,42 @@ func depositDataFromJSON(d depositDataJSON) DepositData {
Signature: d.Signature,
}
}

// VerifyDepositAmounts verifies various conditions about partial deposits rules.
func VerifyDepositAmounts(amounts []eth2p0.Gwei) error {
if len(amounts) == 0 {
return nil
}

const minSingleDepositAmountGwei = 1000000000
const maxTotalDepositAmountGwei = 32000000000

var sum eth2p0.Gwei
for _, amount := range amounts {
if amount < minSingleDepositAmountGwei {
return errors.New("each partial deposit amount must be greater than 1ETH", z.U64("amount", uint64(amount)))
}
sum += amount
}

if sum > eth2p0.Gwei(maxTotalDepositAmountGwei) {
return errors.New("sum of partial deposit amounts must sum up to 32ETH", z.U64("sum", uint64(sum)))
}

return nil
}

// DepositAmountsFromIntSlice converts amounts from []int to []eth2p0.Gwei.
// For verification, please see VerifyDepositAmounts().
func DepositAmountsFromIntSlice(amounts []int) []eth2p0.Gwei {
if amounts == nil {
return nil
}

gweiAmounts := make([]eth2p0.Gwei, len(amounts))
for i, amount := range amounts {
gweiAmounts[i] = eth2p0.Gwei(amount)
}

return gweiAmounts
}
61 changes: 61 additions & 0 deletions cluster/deposit_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,64 @@ func TestDepositJSON(t *testing.T) {

require.Equal(t, b1, b2)
}

func TestVerifyDepositAmounts(t *testing.T) {
t.Run("empty slice", func(t *testing.T) {
err := VerifyDepositAmounts(nil)

require.NoError(t, err)
})

t.Run("valid amounts", func(t *testing.T) {
amounts := []eth2p0.Gwei{
eth2p0.Gwei(16000000000),
eth2p0.Gwei(16000000000),
}

err := VerifyDepositAmounts(amounts)

require.NoError(t, err)
})

t.Run("each amount is greater than 1ETH", func(t *testing.T) {
amounts := []eth2p0.Gwei{
eth2p0.Gwei(500000000), // 0.5ETH
eth2p0.Gwei(31500000000), // 31.5ETH
}

err := VerifyDepositAmounts(amounts)

require.ErrorContains(t, err, "each partial deposit amount must be greater than 1ETH")
})

t.Run("total sum is 32ETH", func(t *testing.T) {
amounts := []eth2p0.Gwei{
eth2p0.Gwei(1000000000),
eth2p0.Gwei(32000000000),
}

err := VerifyDepositAmounts(amounts)

require.ErrorContains(t, err, "sum of partial deposit amounts must sum up to 32ETH")
})
}

func TestConvertIntAmountsToGwei(t *testing.T) {
t.Run("nil slice", func(t *testing.T) {
slice := DepositAmountsFromIntSlice(nil)

require.Nil(t, slice)
})

t.Run("values", func(t *testing.T) {
slice := DepositAmountsFromIntSlice([]int{
1000000000,
5000000000,
})

require.Equal(t, []eth2p0.Gwei{
eth2p0.Gwei(1000000000),
eth2p0.Gwei(5000000000),
}, slice)
})
}
15 changes: 14 additions & 1 deletion cmd/createcluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,8 @@ type clusterConfig struct {
Network string
NumDVs int

DepositAmounts []int

SplitKeys bool
SplitKeysDir string

Expand All @@ -86,7 +88,7 @@ func newCreateClusterCmd(runFunc func(context.Context, io.Writer, clusterConfig)
cmd := &cobra.Command{
Use: "cluster",
Short: "Create private keys and configuration files needed to run a distributed validator cluster locally",
Long: "Creates a local charon cluster configuration including validator keys, charon p2p keys, cluster-lock.json and a deposit-data.json. " +
Long: "Creates a local charon cluster configuration including validator keys, charon p2p keys, cluster-lock.json and deposit-data.json file(s). " +

Check warning on line 91 in cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/createcluster.go#L91

Added line #L91 was not covered by tests
"See flags for supported features.",
RunE: func(cmd *cobra.Command, args []string) error {
return runFunc(cmd.Context(), cmd.OutOrStdout(), conf)
Expand Down Expand Up @@ -119,6 +121,7 @@ func bindClusterFlags(flags *pflag.FlagSet, config *clusterConfig) {
flags.StringVar(&config.testnetConfig.GenesisForkVersionHex, "testnet-fork-version", "", "Genesis fork version of the custom test network (in hex).")
flags.Uint64Var(&config.testnetConfig.ChainID, "testnet-chain-id", 0, "Chain ID of the custom test network.")
flags.Int64Var(&config.testnetConfig.GenesisTimestamp, "testnet-genesis-timestamp", 0, "Genesis timestamp of the custom test network.")
flags.IntSliceVar(&config.DepositAmounts, "deposit-amounts", nil, "List of partial deposit amounts in gwei. Values must sum up to 32ETH.")

Check warning on line 124 in cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/createcluster.go#L124

Added line #L124 was not covered by tests
}

func bindInsecureFlags(flags *pflag.FlagSet, insecureKeys *bool) {
Expand Down Expand Up @@ -321,6 +324,16 @@ func validateCreateConfig(ctx context.Context, conf clusterConfig) error {
return errors.New("number of --keymanager-addresses do not match --keymanager-auth-tokens. Please fix configuration flags")
}

if len(conf.DepositAmounts) > 0 {
amounts := cluster.DepositAmountsFromIntSlice(conf.DepositAmounts)

if err := cluster.VerifyDepositAmounts(amounts); err != nil {
return err
}

Check warning on line 332 in cmd/createcluster.go

View check run for this annotation

Codecov / codecov/patch

cmd/createcluster.go#L331-L332

Added lines #L331 - L332 were not covered by tests

log.Warn(ctx, "Partial deposits feature is under development. The --deposit-amount flag has no effect yet.", nil)
}

for _, addr := range conf.KeymanagerAddrs {
keymanagerURL, err := url.Parse(addr)
if err != nil {
Expand Down
13 changes: 13 additions & 0 deletions cmd/createcluster_internal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,19 @@ func TestCreateCluster(t *testing.T) {
Network: eth2util.Goerli.Name,
},
},
{
Name: "partial deposits",
Config: clusterConfig{
NumNodes: 4,
Threshold: 3,
NumDVs: 1,
Network: eth2util.Goerli.Name,
DepositAmounts: []int{
31000000000,
1000000000,
},
},
},
{
Name: "splitkeys",
Config: clusterConfig{
Expand Down
5 changes: 5 additions & 0 deletions cmd/dkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ this command at the same time.`,
bindLogFlags(cmd.Flags(), &config.Log)
bindPublishFlags(cmd.Flags(), &config)
bindShutdownDelayFlag(cmd.Flags(), &config.ShutdownDelay)
bindPartialDepositsFlag(cmd.Flags(), &config.DepositAmounts)

Check warning on line 47 in cmd/dkg.go

View check run for this annotation

Codecov / codecov/patch

cmd/dkg.go#L47

Added line #L47 was not covered by tests

return cmd
}
Expand All @@ -69,3 +70,7 @@ func bindPublishFlags(flags *pflag.FlagSet, config *dkg.Config) {
func bindShutdownDelayFlag(flags *pflag.FlagSet, shutdownDelay *time.Duration) {
flags.DurationVar(shutdownDelay, "shutdown-delay", time.Second, "Graceful shutdown delay.")
}

func bindPartialDepositsFlag(flags *pflag.FlagSet, depositAmounts *[]int) {
flags.IntSliceVar(depositAmounts, "deposit-amounts", nil, "List of partial deposit amounts in gwei. Values must sum up to 32ETH.")

Check warning on line 75 in cmd/dkg.go

View check run for this annotation

Codecov / codecov/patch

cmd/dkg.go#L74-L75

Added lines #L74 - L75 were not covered by tests
}
22 changes: 22 additions & 0 deletions cmd/testdata/TestCreateCluster_partial_deposits_files.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
[
"node0",
"node1",
"node2",
"node3",
"node0/charon-enr-private-key",
"node0/cluster-lock.json",
"node0/deposit-data.json",
"node0/validator_keys",
"node1/charon-enr-private-key",
"node1/cluster-lock.json",
"node1/deposit-data.json",
"node1/validator_keys",
"node2/charon-enr-private-key",
"node2/cluster-lock.json",
"node2/deposit-data.json",
"node2/validator_keys",
"node3/charon-enr-private-key",
"node3/cluster-lock.json",
"node3/deposit-data.json",
"node3/validator_keys"
]
11 changes: 11 additions & 0 deletions cmd/testdata/TestCreateCluster_partial_deposits_output.golden
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Created charon cluster:
--split-existing-keys=false

charon/
├─ node[0-3]/ Directory for each node
│ ├─ charon-enr-private-key Charon networking private key for node authentication
│ ├─ cluster-lock.json Cluster lock defines the cluster lock file which is signed by all nodes
│ ├─ deposit-data.json Deposit data file is used to activate a Distributed Validator on DV Launchpad
│ ├─ validator_keys Validator keystores and password
│ │ ├─ keystore-*.json Validator private share key for duty signing
│ │ ├─ keystore-*.txt Keystore password files for keystore-*.json
12 changes: 12 additions & 0 deletions dkg/dkg.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ type Config struct {
KeymanagerAddr string
KeymanagerAuthToken string

DepositAmounts []int

PublishAddr string
Publish bool

Expand Down Expand Up @@ -113,6 +115,16 @@ func Run(ctx context.Context, conf Config) (err error) {
return errors.New("only v1.6.0 and v1.7.0 cluster definition version supported")
}

if len(conf.DepositAmounts) > 0 {
amounts := cluster.DepositAmountsFromIntSlice(conf.DepositAmounts)

if err := cluster.VerifyDepositAmounts(amounts); err != nil {
return err
}

Check warning on line 123 in dkg/dkg.go

View check run for this annotation

Codecov / codecov/patch

dkg/dkg.go#L119-L123

Added lines #L119 - L123 were not covered by tests

log.Warn(ctx, "Partial deposits feature is under development. The --deposit-amount flag has no effect yet.", nil)

Check warning on line 125 in dkg/dkg.go

View check run for this annotation

Codecov / codecov/patch

dkg/dkg.go#L125

Added line #L125 was not covered by tests
}

if err := validateKeymanagerFlags(ctx, conf.KeymanagerAddr, conf.KeymanagerAuthToken); err != nil {
return err
}
Expand Down

0 comments on commit 99686c7

Please sign in to comment.