Skip to content

Commit

Permalink
Merge pull request #7 from rgeraldes24/fix/contract-deposit
Browse files Browse the repository at this point in the history
Add 'submit' cmd to the deposit cli
  • Loading branch information
cyyber committed Jan 10, 2024
2 parents bb9f8a3 + a90516d commit 584bd7a
Show file tree
Hide file tree
Showing 8 changed files with 335 additions and 1 deletion.
2 changes: 1 addition & 1 deletion cmd/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
"github.com/urfave/cli/v2"
)

var log = logrus.WithField("prefix", "node")
var log = logrus.WithField("prefix", "cmd")

// ConfirmAction uses the passed in actionText as the confirmation text displayed in the terminal.
// The user must enter Y or N to indicate whether they confirm the action detailed in the warning text.
Expand Down
1 change: 1 addition & 0 deletions cmd/staking-deposit-cli/deposit/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ go_library(
"//cmd/staking-deposit-cli/deposit/existingseed:go_default_library",
"//cmd/staking-deposit-cli/deposit/generatedilithiumtoexecutionchange:go_default_library",
"//cmd/staking-deposit-cli/deposit/newseed:go_default_library",
"//cmd/staking-deposit-cli/deposit/submit:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
Expand Down
12 changes: 12 additions & 0 deletions cmd/staking-deposit-cli/deposit/flags/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
load("@qrysm//tools/go:def.bzl", "go_library")

go_library(
name = "go_default_library",
srcs = ["flags.go"],
importpath = "github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/flags",
visibility = ["//visibility:public"],
deps = [
"//config/params:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)
50 changes: 50 additions & 0 deletions cmd/staking-deposit-cli/deposit/flags/flags.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package flags

import (
"github.com/theQRL/qrysm/v4/config/params"
"github.com/urfave/cli/v2"
)

// ValidatorKeysDefaultDirName for validator_keys.
const ValidatorKeysDefaultDirName = "validator_keys"

var (
// ValidatorKeysDir defines the path to the validator keys directory.
ValidatorKeysDirFlag = &cli.StringFlag{
Name: "validator-keys-dir",
Usage: "Path to a wallet directory on-disk for validator keys",
// Value: filepath.Join(DefaultValidatorKeysDir(), ValidatorKeysDefaultDirName),
Value: ValidatorKeysDefaultDirName,
}
// ZondSeedFileFlag for transaction signing.
ZondSeedFileFlag = &cli.StringFlag{
Name: "zond-seed-file",
Usage: "File containing a seed for sending deposit transactions from zond",
Value: "",
Required: true,
}
// HTTPWeb3ProviderFlag provides an HTTP access endpoint to a ZOND RPC.
HTTPWeb3ProviderFlag = &cli.StringFlag{
Name: "http-web3provider",
Usage: "A zond web3 provider string http endpoint",
Value: "http://localhost:8545",
}
// DepositContractAddressFlag for the validator deposit contract on zond.
DepositContractAddressFlag = &cli.StringFlag{
Name: "deposit-contract",
Usage: "Address of the deposit contract",
Value: params.MainnetConfig().DepositContractAddress,
}
// SkipDepositConfirmationFlag skips the y/n confirmation prompt for sending a deposit to the deposit contract.
SkipDepositConfirmationFlag = &cli.BoolFlag{
Name: "skip-deposit-confirmation",
Usage: "Skips the y/n confirmation prompt for sending a deposit to the deposit contract",
Value: false,
}
// DepositDelaySecondsFlag to delay sending deposit transactions by a fixed interval.
DepositDelaySecondsFlag = &cli.Int64Flag{
Name: "deposit-delay-seconds",
Usage: "The time delay between sending the deposits to the contract (in seconds)",
Value: 5,
}
)
2 changes: 2 additions & 0 deletions cmd/staking-deposit-cli/deposit/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/existingseed"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/generatedilithiumtoexecutionchange"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/newseed"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/submit"
"github.com/urfave/cli/v2"
)

Expand All @@ -26,4 +27,5 @@ func init() {
depositCommands = append(depositCommands, existingseed.Commands...)
depositCommands = append(depositCommands, newseed.Commands...)
depositCommands = append(depositCommands, generatedilithiumtoexecutionchange.Commands...)
depositCommands = append(depositCommands, submit.Command)
}
31 changes: 31 additions & 0 deletions cmd/staking-deposit-cli/deposit/submit/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
load("@qrysm//tools/go:def.bzl", "go_library", "go_test")

go_library(
name = "go_default_library",
srcs = [
"cmd.go",
"deposit.go",
],
importpath = "github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/submit",
visibility = ["//visibility:public"],
deps = [
"//cmd/staking-deposit-cli/deposit/flags:go_default_library",
"//cmd/staking-deposit-cli/misc:go_default_library",
"//cmd/staking-deposit-cli/stakingdeposit:go_default_library",
"//contracts/deposit:go_default_library",
"//encoding/bytesutil:go_default_library",
"@com_github_sirupsen_logrus//:go_default_library",
"@com_github_theqrl_go_qrllib//dilithium:go_default_library",
"@com_github_theqrl_go_zond//accounts/abi/bind:go_default_library",
"@com_github_theqrl_go_zond//common:go_default_library",
"@com_github_theqrl_go_zond//core/types:go_default_library",
"@com_github_theqrl_go_zond//rpc:go_default_library",
"@com_github_theqrl_go_zond//zondclient:go_default_library",
"@com_github_urfave_cli_v2//:go_default_library",
],
)

go_test(
name = "go_default_test",
srcs = ["deposit_test.go"],
)
27 changes: 27 additions & 0 deletions cmd/staking-deposit-cli/deposit/submit/cmd.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package submit

import (
"github.com/sirupsen/logrus"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/flags"
"github.com/urfave/cli/v2"
)

var log = logrus.WithField("prefix", "deposit")

var Command = &cli.Command{
Name: "submit",
Description: "Submits deposits to the zond deposit contract for a set of validators by connecting " +
"to a zond endpoint to submit the transactions. Requires signing the transactions with a zond private key",
Usage: "",
Action: func(cliCtx *cli.Context) error {
return submitDeposits(cliCtx)
},
Flags: []cli.Flag{
flags.ValidatorKeysDirFlag,
flags.ZondSeedFileFlag,
flags.DepositContractAddressFlag,
flags.HTTPWeb3ProviderFlag,
flags.DepositDelaySecondsFlag,
flags.SkipDepositConfirmationFlag,
},
}
211 changes: 211 additions & 0 deletions cmd/staking-deposit-cli/deposit/submit/deposit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
package submit

import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"os"
"path/filepath"
"strings"
"time"

"github.com/k0kubun/go-ansi"
"github.com/schollz/progressbar/v3"
"github.com/sirupsen/logrus"
dilithiumlib "github.com/theQRL/go-qrllib/dilithium"
"github.com/theQRL/go-zond/accounts/abi/bind"
"github.com/theQRL/go-zond/common"
"github.com/theQRL/go-zond/rpc"
"github.com/theQRL/go-zond/zondclient"
"github.com/theQRL/qrysm/v4/cmd"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/deposit/flags"
"github.com/theQRL/qrysm/v4/cmd/staking-deposit-cli/stakingdeposit"
"github.com/theQRL/qrysm/v4/config/params"
"github.com/theQRL/qrysm/v4/contracts/deposit"
"github.com/theQRL/qrysm/v4/encoding/bytesutil"
"github.com/urfave/cli/v2"
)

const depositDataFilePrefix = "deposit_data-"

func submitDeposits(cliCtx *cli.Context) error {
validatorKeysDir := cliCtx.String(flags.ValidatorKeysDirFlag.Name)
depositDataList, err := importDepositDataJSON(validatorKeysDir)
if err != nil {
return fmt.Errorf("failed to read deposit data. reason: %v", err)
}

contractAddr := cliCtx.String(flags.DepositContractAddressFlag.Name)
if !cliCtx.Bool(flags.SkipDepositConfirmationFlag.Name) {
qrlDepositTotal := uint64(len(depositDataList)) * params.BeaconConfig().MaxEffectiveBalance / params.BeaconConfig().GweiPerEth
actionText := "This will submit the deposits stored in your deposit data directory. " +
fmt.Sprintf("A total of %d QRL will be sent to contract address %s for %d validator accounts. ", qrlDepositTotal, contractAddr, len(depositDataList)) +
"Do you want to proceed? (Y/N)"
deniedText := "Deposits will not be submitted. No changes have been made."
submitConfirmed, err := cmd.ConfirmAction(actionText, deniedText)
if err != nil {
return err
}
if !submitConfirmed {
return nil
}
}

web3Provider := cliCtx.String(flags.HTTPWeb3ProviderFlag.Name)
rpcClient, err := rpc.Dial(web3Provider)
if err != nil {
return fmt.Errorf("failed to connect to the zond provider. reason: %v", err)
}
zondCli := zondclient.NewClient(rpcClient)
chainID, err := zondCli.ChainID(cliCtx.Context)
if err != nil {
return fmt.Errorf("failed to retrieve the chain ID. reason: %v", err)
}
contract, err := deposit.NewDepositContract(common.HexToAddress(contractAddr), zondCli)
if err != nil {
return fmt.Errorf("failed to create a new instance of the deposit contract. reason: %v", err)
}

signingSeedFile := cliCtx.String(flags.ZondSeedFileFlag.Name)
signingSeedHex, err := os.ReadFile(signingSeedFile)
if err != nil {
return fmt.Errorf("failed to read seed file. reason: %v", err)
}
signingSeed := make([]byte, hex.DecodedLen(len(signingSeedHex)))
_, err = hex.Decode(signingSeed, signingSeedHex)
if err != nil {
return fmt.Errorf("failed to read seed. reason: %v", err)
}

depositKey, err := dilithiumlib.NewDilithiumFromSeed(bytesutil.ToBytes48(signingSeed))
if err != nil {
return fmt.Errorf("failed to generate the deposit key from the signing seed. reason: %v", err)
}

gasTip, err := zondCli.SuggestGasTipCap(cliCtx.Context)
if err != nil {
return fmt.Errorf("failed to get gas tip suggestion. reason: %v", err)
}

txOpts, err := bind.NewKeyedTransactorWithChainID(depositKey, chainID)
if err != nil {
return err
}
txOpts.GasLimit = 500000
txOpts.GasFeeCap = nil
txOpts.GasTipCap = gasTip

depositDelaySeconds := cliCtx.Int(flags.DepositDelaySecondsFlag.Name)
depositDelay := time.Duration(depositDelaySeconds) * time.Second
bar := initializeProgressBar(len(depositDataList), "Sending deposit transactions...")
for i, depositData := range depositDataList {
txOpts.Value = new(big.Int).Mul(new(big.Int).SetUint64(depositData.Amount), big.NewInt(1e9)) // value in wei

if err := sendDepositTx(contract, depositData, chainID, txOpts); err != nil {
log.Errorf("Unable to send transaction to contract: %v | deposit data index: %d", err, i)
continue
}

log.Infof("Waiting for a short delay of %v seconds...", depositDelaySeconds)
if err := bar.Add(1); err != nil {
log.Errorf("Could not increase progress bar percentage: %v", err)
}
time.Sleep(depositDelay)
}

log.Infof("Successfully sent all validator deposits!")

return nil
}

func sendDepositTx(
contract *deposit.DepositContract,
data *stakingdeposit.DepositData,
chainID *big.Int,
txOpts *bind.TransactOpts,
) error {
pubKeyBytes, err := hex.DecodeString(data.PubKey)
if err != nil {
return err
}
credsBytes, err := hex.DecodeString(data.WithdrawalCredentials)
if err != nil {
return err
}
sigBytes, err := hex.DecodeString(data.Signature)
if err != nil {
return err
}
depDataRootBytes, err := hex.DecodeString(data.DepositDataRoot)
if err != nil {
return err
}

tx, err := contract.Deposit(
txOpts,
pubKeyBytes,
credsBytes,
sigBytes,
bytesutil.ToBytes32(depDataRootBytes),
)
if err != nil {
return err
}

log.WithFields(logrus.Fields{
"Transaction Hash": fmt.Sprintf("%#x", tx.Hash()),
}).Info("Deposit sent for validator")

return nil
}

func importDepositDataJSON(folder string) ([]*stakingdeposit.DepositData, error) {
entries, err := os.ReadDir(folder)
if err != nil {
return nil, err
}

var file string
for _, entry := range entries {
if !entry.IsDir() && strings.HasPrefix(entry.Name(), depositDataFilePrefix) {
file = entry.Name()
break
}
}

if file == "" {
return nil, fmt.Errorf("deposit data file not found. dir: %s", folder)
}

fileFolder := filepath.Join(folder, file)
data, err := os.ReadFile(fileFolder)
if err != nil {
return nil, err
}

var depositDataList []*stakingdeposit.DepositData
if err := json.Unmarshal(data, &depositDataList); err != nil {
return nil, fmt.Errorf("failed to read deposit data list. reason: %v", err)
}

return depositDataList, nil
}

func initializeProgressBar(numItems int, msg string) *progressbar.ProgressBar {
return progressbar.NewOptions(
numItems,
progressbar.OptionFullWidth(),
progressbar.OptionSetWriter(ansi.NewAnsiStdout()),
progressbar.OptionEnableColorCodes(true),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "[green]=[reset]",
SaucerHead: "[green]>[reset]",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}),
progressbar.OptionOnCompletion(func() { fmt.Println() }),
progressbar.OptionSetDescription(msg),
)
}

0 comments on commit 584bd7a

Please sign in to comment.