mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-25 21:07:18 +00:00
169 lines
5.7 KiB
Go
169 lines
5.7 KiB
Go
|
package derived
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"math/big"
|
||
|
"strings"
|
||
|
"time"
|
||
|
|
||
|
"github.com/ethereum/go-ethereum/accounts/abi/bind"
|
||
|
"github.com/ethereum/go-ethereum/accounts/keystore"
|
||
|
"github.com/ethereum/go-ethereum/common"
|
||
|
"github.com/ethereum/go-ethereum/crypto"
|
||
|
"github.com/ethereum/go-ethereum/ethclient"
|
||
|
"github.com/ethereum/go-ethereum/rpc"
|
||
|
"github.com/k0kubun/go-ansi"
|
||
|
"github.com/pkg/errors"
|
||
|
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
|
||
|
"github.com/prysmaticlabs/prysm/shared/bls"
|
||
|
"github.com/prysmaticlabs/prysm/shared/bytesutil"
|
||
|
"github.com/prysmaticlabs/prysm/shared/depositutil"
|
||
|
"github.com/prysmaticlabs/prysm/shared/fileutil"
|
||
|
"github.com/prysmaticlabs/prysm/shared/params"
|
||
|
"github.com/schollz/progressbar/v3"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
util "github.com/wealdtech/go-eth2-util"
|
||
|
)
|
||
|
|
||
|
// SendDepositConfig contains all the required information for
|
||
|
// the derived keymanager to submit a 32 ETH deposit from the user's
|
||
|
// eth1 wallet to an eth1 RPC endpoint.
|
||
|
type SendDepositConfig struct {
|
||
|
DepositContractAddress string
|
||
|
DepositDelaySeconds time.Duration
|
||
|
DepositPublicKeys []bls.PublicKey
|
||
|
Eth1KeystoreUTCFile string
|
||
|
Eth1KeystorePasswordFile string
|
||
|
Eth1PrivateKey string
|
||
|
Web3Provider string
|
||
|
}
|
||
|
|
||
|
// SendDepositTx to the validator deposit contract on the eth1 chain
|
||
|
// using a defined configuration by first unlocking the user's eth1 wallet,
|
||
|
// then generating the deposit data for a desired validator account, finally
|
||
|
// submitting the transaction via an eth1 web3 endpoint.
|
||
|
func (dr *Keymanager) SendDepositTx(conf *SendDepositConfig) error {
|
||
|
var txOps *bind.TransactOpts
|
||
|
rpcClient, err := rpc.Dial(conf.Web3Provider)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
client := ethclient.NewClient(rpcClient)
|
||
|
depositAmountInGwei := params.BeaconConfig().MaxEffectiveBalance
|
||
|
|
||
|
if conf.Eth1PrivateKey != "" {
|
||
|
// User inputs private key, sign tx with private key
|
||
|
privKey, err := crypto.HexToECDSA(conf.Eth1PrivateKey)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
txOps = bind.NewKeyedTransactor(privKey)
|
||
|
txOps.Value = new(big.Int).Mul(big.NewInt(int64(depositAmountInGwei)), big.NewInt(1e9))
|
||
|
} else {
|
||
|
// User inputs keystore json file, sign tx with keystore json
|
||
|
password, err := fileutil.ReadFileAsBytes(conf.Eth1KeystorePasswordFile)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
// #nosec - Inclusion of file via variable is OK for this tool.
|
||
|
keyJSON, err := fileutil.ReadFileAsBytes(conf.Eth1KeystoreUTCFile)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
privKey, err := keystore.DecryptKey(keyJSON, strings.TrimRight(string(password), "\r\n"))
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
|
||
|
txOps = bind.NewKeyedTransactor(privKey.PrivateKey)
|
||
|
txOps.Value = new(big.Int).Mul(big.NewInt(int64(depositAmountInGwei)), big.NewInt(1e9))
|
||
|
txOps.GasLimit = 500000
|
||
|
}
|
||
|
|
||
|
depositContract, err := contracts.NewDepositContract(common.HexToAddress(conf.DepositContractAddress), client)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
wantedPubKeys := make(map[[48]byte]bool, len(conf.DepositPublicKeys))
|
||
|
for _, pk := range conf.DepositPublicKeys {
|
||
|
wantedPubKeys[bytesutil.ToBytes48(pk.Marshal())] = true
|
||
|
}
|
||
|
bar := initializeProgressBar(int(dr.seedCfg.NextAccount), "Sending deposit transactions...")
|
||
|
for i := uint64(0); i < dr.seedCfg.NextAccount; i++ {
|
||
|
validatingKeyPath := fmt.Sprintf(ValidatingKeyDerivationPathTemplate, i)
|
||
|
validatingKey, err := util.PrivateKeyFromSeedAndPath(dr.seed, validatingKeyPath)
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "failed to read validating key for account %d", i)
|
||
|
}
|
||
|
if ok := wantedPubKeys[bytesutil.ToBytes48(validatingKey.PublicKey().Marshal())]; !ok {
|
||
|
continue
|
||
|
}
|
||
|
withdrawalKeyPath := fmt.Sprintf(WithdrawalKeyDerivationPathTemplate, i)
|
||
|
withdrawalKey, err := util.PrivateKeyFromSeedAndPath(dr.seed, withdrawalKeyPath)
|
||
|
if err != nil {
|
||
|
return errors.Wrapf(err, "failed to read withdrawal key for account %d", i)
|
||
|
}
|
||
|
validatingKeyBLS, err := bls.SecretKeyFromBytes(validatingKey.Marshal())
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
withdrawalKeyBLS, err := bls.SecretKeyFromBytes(withdrawalKey.Marshal())
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
data, depositRoot, err := depositutil.DepositInput(validatingKeyBLS, withdrawalKeyBLS, depositAmountInGwei)
|
||
|
if err != nil {
|
||
|
log.Errorf("Could not generate deposit input data: %v", err)
|
||
|
continue
|
||
|
}
|
||
|
tx, err := depositContract.Deposit(
|
||
|
txOps,
|
||
|
data.PublicKey,
|
||
|
data.WithdrawalCredentials,
|
||
|
data.Signature,
|
||
|
depositRoot,
|
||
|
)
|
||
|
if err != nil {
|
||
|
log.Errorf("Unable to send transaction to contract: %v", err)
|
||
|
continue
|
||
|
}
|
||
|
log.WithFields(logrus.Fields{
|
||
|
"Transaction Hash": fmt.Sprintf("%#x", tx.Hash()),
|
||
|
}).Infof(
|
||
|
"Deposit %d sent to contract address %v for validator with a public key %#x",
|
||
|
i,
|
||
|
conf.DepositContractAddress,
|
||
|
validatingKey.PublicKey().Marshal(),
|
||
|
)
|
||
|
log.Infof(
|
||
|
"You can monitor your transaction on Etherscan here https://goerli.etherscan.io/tx/0x%x",
|
||
|
tx.Hash(),
|
||
|
)
|
||
|
log.Infof("Waiting for a short delay of %v seconds...", conf.DepositDelaySeconds)
|
||
|
if err := bar.Add(1); err != nil {
|
||
|
log.Errorf("Could not increase progress bar percentage: %v", err)
|
||
|
}
|
||
|
time.Sleep(conf.DepositDelaySeconds)
|
||
|
}
|
||
|
log.Infof("Successfully sent all validator deposits!")
|
||
|
return 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),
|
||
|
)
|
||
|
}
|