package main import ( "bufio" "context" "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" contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" "github.com/prysmaticlabs/prysm/shared/version" "github.com/sirupsen/logrus" "github.com/urfave/cli/v2" prefixed "github.com/x-cray/logrus-prefixed-formatter" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" k8s "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" ) func main() { var keystoreUTCPath string var ipcPath string var passwordFile string var httpPath string var privKeyString string var k8sConfigMapName string var drainAddress string customFormatter := new(prefixed.TextFormatter) customFormatter.TimestampFormat = "2006-01-02 15:04:05" customFormatter.FullTimestamp = true logrus.SetFormatter(customFormatter) log := logrus.WithField("prefix", "main") app := cli.App{} app.Name = "deployDepositContract" app.Usage = "this is a util to deploy deposit contract" app.Version = version.GetVersion() app.Flags = []cli.Flag{ &cli.StringFlag{ Name: "keystoreUTCPath", Usage: "Location of keystore", Destination: &keystoreUTCPath, }, &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 unlock account", Destination: &privKeyString, }, &cli.StringFlag{ Name: "k8sConfig", Usage: "Name of kubernetes config map to update with the contract address", Destination: &k8sConfigMapName, }, &cli.StringFlag{ Name: "drainAddress", Value: "", Usage: "The drain address to specify in the contract. The default will be msg.sender", Destination: &drainAddress, }, } 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) // User inputs private key, sign tx with private key if privKeyString != "" { privKey, err := crypto.HexToECDSA(privKeyString) if err != nil { log.Fatal(err) } txOps = bind.NewKeyedTransactor(privKey) txOps.Value = big.NewInt(0) txOps.GasLimit = 4000000 // User inputs keystore json file, sign tx with keystore json } else { // #nosec - Inclusion of file via variable is OK for this tool. file, err := os.Open(passwordFile) if err != nil { return err } scanner := bufio.NewScanner(file) scanner.Split(bufio.ScanWords) scanner.Scan() password := scanner.Text() // #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 = big.NewInt(0) txOps.GasLimit = 4000000 } drain := txOps.From if drainAddress != "" { drain = common.HexToAddress(drainAddress) } txOps.GasPrice = big.NewInt(10 * 1e9 /* 10 gwei */) // Deploy validator registration contract addr, tx, _, err := contracts.DeployDepositContract( txOps, client, drain, ) if err != nil { return err } // Wait for contract to mine for pending := true; pending; _, pending, err = client.TransactionByHash(context.Background(), tx.Hash()) { if err != nil { return err } time.Sleep(1 * time.Second) } log.WithFields(logrus.Fields{ "address": addr.Hex(), }).Info("New contract deployed") if k8sConfigMapName != "" { if err := updateKubernetesConfigMap(context.Background(), addr.Hex()); err != nil { log.Fatalf("Failed to update kubernetes config map: %v", err) } else { log.Printf("Updated config map %s", k8sConfigMapName) } } return nil } err := app.Run(os.Args) if err != nil { log.Fatal(err) } } // updateKubernetesConfigMap in the beacon-chain namespace. This specifically // updates the data value for DEPOSIT_CONTRACT_ADDRESS. func updateKubernetesConfigMap(ctx context.Context, contractAddr string) error { config, err := rest.InClusterConfig() if err != nil { return err } client, err := k8s.NewForConfig(config) if err != nil { return err } cm, err := client.CoreV1().ConfigMaps("beacon-chain").Get(ctx, "beacon-config", metav1.GetOptions{}) if err != nil { return err } if cm.Data["DEPOSIT_CONTRACT_ADDRESS"] != "0x0" { return fmt.Errorf("existing vcr address in config map = %v", cm.Data["DEPOSIT_CONTRACT_ADDRESS"]) } cm.Data["DEPOSIT_CONTRACT_ADDRESS"] = contractAddr _, err = client.CoreV1().ConfigMaps("beacon-chain").Update(ctx, cm, metav1.UpdateOptions{}) return err }