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" 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.NewApp() 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) { // 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 { log.Fatal(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 { log.Fatal(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 { log.Fatal(err) } privKey, err := keystore.DecryptKey(keyJSON, password) if err != nil { log.Fatal(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 { log.Fatal(err) } // Wait for contract to mine for pending := true; pending; _, pending, err = client.TransactionByHash(context.Background(), tx.Hash()) { if err != nil { log.Fatal(err) } time.Sleep(1 * time.Second) } log.WithFields(logrus.Fields{ "address": addr.Hex(), }).Info("New contract deployed") if k8sConfigMapName != "" { if err := updateKubernetesConfigMap(k8sConfigMapName, addr.Hex()); err != nil { log.Fatalf("Failed to update kubernetes config map: %v", err) } else { log.Printf("Updated config map %s", k8sConfigMapName) } } } 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(configMapName string, 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("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(cm) return err }