prysm-pulse/tools/sendDepositTx/sendDeposits.go
Raul Jordan 9d979de4ed
Direct Keymanager: Implement Account Creation (#6466)
* implementation using petname and keystore
* writing new account to disk along with password
* more logic for properly writing accounts
* print out mnemonic
* save deposit data rlp
* write deposit tx and ssz deposit data to account path
* wrap up account creation
* fix prompt
* generate deposit tx
* direct account creation test
* fix up formatting
* lint
* match formatting
* more sustainable approach towards unmarshaling config file
* resolve feedback
* fix broken import
* comprehensive tests for create account
* tests pass
* Merge branch 'master' into direct-keys
* tidy
* Merge branch 'direct-keys' of github.com:prysmaticlabs/prysm into direct-keys
* Merge refs/heads/master into direct-keys
* gaz
* Merge branch 'direct-keys' of github.com:prysmaticlabs/prysm into direct-keys
* nondeterministic names
* comment
* gaz
* better error wrap
* Merge refs/heads/master into direct-keys
* docker deps
* Merge branch 'direct-keys' of github.com:prysmaticlabs/prysm into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* Merge refs/heads/master into direct-keys
* ivan feedback
* Merge refs/heads/master into direct-keys
* Update validator/accounts/v2/wallet.go

Co-authored-by: Preston Van Loon <preston@prysmaticlabs.com>
* fixed tests and comments
* Merge refs/heads/master into direct-keys
2020-07-03 18:49:16 +00:00

231 lines
6.6 KiB
Go

package main
import (
"bufio"
"encoding/hex"
"fmt"
"io/ioutil"
"math/big"
"os"
"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/pkg/errors"
contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
"github.com/prysmaticlabs/prysm/shared/depositutil"
prysmKeyStore "github.com/prysmaticlabs/prysm/shared/keystore"
"github.com/prysmaticlabs/prysm/shared/params"
"github.com/prysmaticlabs/prysm/shared/version"
"github.com/sirupsen/logrus"
"github.com/urfave/cli/v2"
prefixed "github.com/x-cray/logrus-prefixed-formatter"
)
var (
log = logrus.WithField("prefix", "main")
)
func main() {
var keystoreUTCPath string
var prysmKeystorePath string
var ipcPath string
var passwordFile string
var httpPath string
var privKeyString string
var depositContractAddr string
var numberOfDeposits int64
var depositAmount int64
var depositDelay int64
var randomKey bool
customFormatter := new(prefixed.TextFormatter)
customFormatter.TimestampFormat = "2006-01-02 15:04:05"
customFormatter.FullTimestamp = true
logrus.SetFormatter(customFormatter)
app := cli.App{}
app.Name = "sendDepositTx"
app.Usage = "this is a util to send deposit transactions"
app.Version = version.GetVersion()
app.Flags = []cli.Flag{
&cli.StringFlag{
Name: "keystoreUTCPath",
Usage: "Location of keystore",
Destination: &keystoreUTCPath,
},
&cli.StringFlag{
Name: "prysm-keystore",
Usage: "The path to the existing prysm keystore. This flag is ignored if used with --random-key",
Destination: &prysmKeystorePath,
},
&cli.StringFlag{
Name: "ipcPath",
Usage: "Filename for IPC socket/pipe within the datadir",
Destination: &ipcPath,
},
&cli.StringFlag{
Name: "httpPath",
Value: "http://localhost:8545/",
Usage: "HTTP-RPC server listening interface",
Destination: &httpPath,
},
&cli.StringFlag{
Name: "passwordFile",
Value: "./password.txt",
Usage: "Password file for unlock account",
Destination: &passwordFile,
},
&cli.StringFlag{
Name: "privKey",
Usage: "Private key to send ETH transaction",
Destination: &privKeyString,
},
&cli.StringFlag{
Name: "depositContract",
Usage: "Address of the deposit contract",
Destination: &depositContractAddr,
},
&cli.Int64Flag{
Name: "numberOfDeposits",
Value: 1,
Usage: "number of deposits to send to the contract",
Destination: &numberOfDeposits,
},
&cli.Int64Flag{
Name: "depositAmount",
Value: int64(params.BeaconConfig().MaxEffectiveBalance),
Usage: "Maximum deposit value allowed in contract(in gwei)",
Destination: &depositAmount,
},
&cli.Int64Flag{
Name: "depositDelay",
Value: 5,
Usage: "The time delay between sending the deposits to the contract(in seconds)",
Destination: &depositDelay,
},
&cli.BoolFlag{
Name: "random-key",
Usage: "Use a randomly generated keystore key",
Destination: &randomKey,
},
}
app.Action = func(c *cli.Context) error {
// Set up RPC client
var rpcClient *rpc.Client
var err error
var txOps *bind.TransactOpts
// Uses HTTP-RPC if IPC is not set
if ipcPath == "" {
rpcClient, err = rpc.Dial(httpPath)
} else {
rpcClient, err = rpc.Dial(ipcPath)
}
if err != nil {
return err
}
client := ethclient.NewClient(rpcClient)
depositAmountInGwei := uint64(depositAmount)
if privKeyString != "" {
// User inputs private key, sign tx with private key
privKey, err := crypto.HexToECDSA(privKeyString)
if err != nil {
return err
}
txOps = bind.NewKeyedTransactor(privKey)
txOps.Value = new(big.Int).Mul(big.NewInt(depositAmount), big.NewInt(1e9))
} else {
// User inputs keystore json file, sign tx with keystore json
password := loadTextFromFile(passwordFile)
// #nosec - Inclusion of file via variable is OK for this tool.
keyJSON, err := ioutil.ReadFile(keystoreUTCPath)
if err != nil {
return err
}
privKey, err := keystore.DecryptKey(keyJSON, password)
if err != nil {
return err
}
txOps = bind.NewKeyedTransactor(privKey.PrivateKey)
txOps.Value = new(big.Int).Mul(big.NewInt(depositAmount), big.NewInt(1e9))
txOps.GasLimit = 500000
}
depositContract, err := contracts.NewDepositContract(common.HexToAddress(depositContractAddr), client)
if err != nil {
return err
}
validatorKeys := make(map[string]*prysmKeyStore.Key)
if randomKey {
validatorKey, err := prysmKeyStore.NewKey()
if err != nil {
return errors.Wrap(err, "Could not generate random key")
}
validatorKeys[hex.EncodeToString(validatorKey.PublicKey.Marshal())] = validatorKey
} else {
// Load from keystore
store := prysmKeyStore.NewKeystore(prysmKeystorePath)
rawPassword := loadTextFromFile(passwordFile)
prefix := params.BeaconConfig().ValidatorPrivkeyFileName
validatorKeys, err = store.GetKeys(prysmKeystorePath, prefix, rawPassword, false /* warnOnFail */)
if err != nil {
log.WithField("path", prysmKeystorePath).WithField("password", rawPassword).Errorf("Could not get keys: %v", err)
}
}
keyCounter := int64(0)
for _, validatorKey := range validatorKeys {
data, depositRoot, err := depositutil.DepositInput(validatorKey.SecretKey, validatorKey.SecretKey, depositAmountInGwei)
if err != nil {
log.Errorf("Could not generate deposit input data: %v", err)
continue
}
for j := int64(0); j < numberOfDeposits; j++ {
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", (keyCounter*numberOfDeposits)+j, depositContractAddr, validatorKey.PublicKey.Marshal())
time.Sleep(time.Duration(depositDelay) * time.Second)
}
keyCounter++
}
return nil
}
err := app.Run(os.Args)
if err != nil {
log.Fatal(err)
}
}
func loadTextFromFile(filepath string) string {
// #nosec - Inclusion of file via variable is OK for this tool.
file, err := os.Open(filepath)
if err != nil {
log.Fatal(err)
}
scanner := bufio.NewScanner(file)
scanner.Split(bufio.ScanWords)
scanner.Scan()
return scanner.Text()
}