github.com/prysmaticlabs/prysm@v1.4.4/tools/deployContract/deployContract.go (about)

     1  package main
     2  
     3  import (
     4  	"bufio"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"math/big"
     9  	"os"
    10  	"time"
    11  
    12  	"github.com/ethereum/go-ethereum/accounts/abi/bind"
    13  	"github.com/ethereum/go-ethereum/accounts/keystore"
    14  	"github.com/ethereum/go-ethereum/common"
    15  	"github.com/ethereum/go-ethereum/crypto"
    16  	"github.com/ethereum/go-ethereum/ethclient"
    17  	"github.com/ethereum/go-ethereum/rpc"
    18  	contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract"
    19  	"github.com/prysmaticlabs/prysm/shared/version"
    20  	"github.com/sirupsen/logrus"
    21  	"github.com/urfave/cli/v2"
    22  	prefixed "github.com/x-cray/logrus-prefixed-formatter"
    23  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    24  	k8s "k8s.io/client-go/kubernetes"
    25  	"k8s.io/client-go/rest"
    26  )
    27  
    28  func main() {
    29  	var keystoreUTCPath string
    30  	var ipcPath string
    31  	var passwordFile string
    32  	var httpPath string
    33  	var privKeyString string
    34  	var k8sConfigMapName string
    35  	var drainAddress string
    36  
    37  	customFormatter := new(prefixed.TextFormatter)
    38  	customFormatter.TimestampFormat = "2006-01-02 15:04:05"
    39  	customFormatter.FullTimestamp = true
    40  	logrus.SetFormatter(customFormatter)
    41  	log := logrus.WithField("prefix", "main")
    42  
    43  	app := cli.App{}
    44  	app.Name = "deployDepositContract"
    45  	app.Usage = "this is a util to deploy deposit contract"
    46  	app.Version = version.Version()
    47  	app.Flags = []cli.Flag{
    48  		&cli.StringFlag{
    49  			Name:        "keystoreUTCPath",
    50  			Usage:       "Location of keystore",
    51  			Destination: &keystoreUTCPath,
    52  		},
    53  		&cli.StringFlag{
    54  			Name:        "ipcPath",
    55  			Usage:       "Filename for IPC socket/pipe within the datadir",
    56  			Destination: &ipcPath,
    57  		},
    58  		&cli.StringFlag{
    59  			Name:        "httpPath",
    60  			Value:       "http://localhost:8545/",
    61  			Usage:       "HTTP-RPC server listening interface",
    62  			Destination: &httpPath,
    63  		},
    64  		&cli.StringFlag{
    65  			Name:        "passwordFile",
    66  			Value:       "./password.txt",
    67  			Usage:       "Password file for unlock account",
    68  			Destination: &passwordFile,
    69  		},
    70  		&cli.StringFlag{
    71  			Name:        "privKey",
    72  			Usage:       "Private key to unlock account",
    73  			Destination: &privKeyString,
    74  		},
    75  		&cli.StringFlag{
    76  			Name:        "k8sConfig",
    77  			Usage:       "Name of kubernetes config map to update with the contract address",
    78  			Destination: &k8sConfigMapName,
    79  		},
    80  		&cli.StringFlag{
    81  			Name:        "drainAddress",
    82  			Value:       "",
    83  			Usage:       "The drain address to specify in the contract. The default will be msg.sender",
    84  			Destination: &drainAddress,
    85  		},
    86  	}
    87  
    88  	app.Action = func(c *cli.Context) error {
    89  		// Set up RPC client
    90  		var rpcClient *rpc.Client
    91  		var err error
    92  		var txOps *bind.TransactOpts
    93  
    94  		// Uses HTTP-RPC if IPC is not set
    95  		if ipcPath == "" {
    96  			rpcClient, err = rpc.Dial(httpPath)
    97  		} else {
    98  			rpcClient, err = rpc.Dial(ipcPath)
    99  		}
   100  		if err != nil {
   101  			return err
   102  		}
   103  
   104  		client := ethclient.NewClient(rpcClient)
   105  
   106  		// User inputs private key, sign tx with private key
   107  		if privKeyString != "" {
   108  			privKey, err := crypto.HexToECDSA(privKeyString)
   109  			if err != nil {
   110  				log.Fatal(err)
   111  			}
   112  			txOps, err = bind.NewKeyedTransactorWithChainID(privKey, big.NewInt(1337))
   113  			if err != nil {
   114  				log.Fatal(err)
   115  			}
   116  			txOps.Value = big.NewInt(0)
   117  			txOps.GasLimit = 4000000
   118  			txOps.Context = context.Background()
   119  			// User inputs keystore json file, sign tx with keystore json
   120  		} else {
   121  			// #nosec - Inclusion of file via variable is OK for this tool.
   122  			file, err := os.Open(passwordFile)
   123  			if err != nil {
   124  				return err
   125  			}
   126  
   127  			scanner := bufio.NewScanner(file)
   128  			scanner.Split(bufio.ScanWords)
   129  			scanner.Scan()
   130  			password := scanner.Text()
   131  
   132  			// #nosec - Inclusion of file via variable is OK for this tool.
   133  			keyJSON, err := ioutil.ReadFile(keystoreUTCPath)
   134  			if err != nil {
   135  				return err
   136  			}
   137  			privKey, err := keystore.DecryptKey(keyJSON, password)
   138  			if err != nil {
   139  				return err
   140  			}
   141  
   142  			txOps, err = bind.NewKeyedTransactorWithChainID(privKey.PrivateKey, big.NewInt(1337))
   143  			if err != nil {
   144  				log.Fatal(err)
   145  			}
   146  			txOps.Value = big.NewInt(0)
   147  			txOps.GasLimit = 4000000
   148  			txOps.Context = context.Background()
   149  		}
   150  
   151  		drain := txOps.From
   152  		if drainAddress != "" {
   153  			drain = common.HexToAddress(drainAddress)
   154  		}
   155  
   156  		txOps.GasPrice = big.NewInt(10 * 1e9 /* 10 gwei */)
   157  
   158  		// Deploy validator registration contract
   159  		addr, tx, _, err := contracts.DeployDepositContract(
   160  			txOps,
   161  			client,
   162  			drain,
   163  		)
   164  
   165  		if err != nil {
   166  			return err
   167  		}
   168  
   169  		// Wait for contract to mine
   170  		for pending := true; pending; _, pending, err = client.TransactionByHash(context.Background(), tx.Hash()) {
   171  			if err != nil {
   172  				return err
   173  			}
   174  			time.Sleep(1 * time.Second)
   175  		}
   176  
   177  		log.WithFields(logrus.Fields{
   178  			"address": addr.Hex(),
   179  		}).Info("New contract deployed")
   180  
   181  		if k8sConfigMapName != "" {
   182  			if err := updateKubernetesConfigMap(context.Background(), addr.Hex()); err != nil {
   183  				log.Fatalf("Failed to update kubernetes config map: %v", err)
   184  			} else {
   185  				log.Printf("Updated config map %s", k8sConfigMapName)
   186  			}
   187  		}
   188  		return nil
   189  	}
   190  
   191  	err := app.Run(os.Args)
   192  	if err != nil {
   193  		log.Fatal(err)
   194  	}
   195  }
   196  
   197  // updateKubernetesConfigMap in the beacon-chain namespace. This specifically
   198  // updates the data value for DEPOSIT_CONTRACT_ADDRESS.
   199  func updateKubernetesConfigMap(ctx context.Context, contractAddr string) error {
   200  	config, err := rest.InClusterConfig()
   201  	if err != nil {
   202  		return err
   203  	}
   204  
   205  	client, err := k8s.NewForConfig(config)
   206  	if err != nil {
   207  		return err
   208  	}
   209  
   210  	cm, err := client.CoreV1().ConfigMaps("beacon-chain").Get(ctx, "beacon-config", metav1.GetOptions{})
   211  	if err != nil {
   212  		return err
   213  	}
   214  
   215  	if cm.Data["DEPOSIT_CONTRACT_ADDRESS"] != "0x0" {
   216  		return fmt.Errorf("existing vcr address in config map = %v", cm.Data["DEPOSIT_CONTRACT_ADDRESS"])
   217  	}
   218  	cm.Data["DEPOSIT_CONTRACT_ADDRESS"] = contractAddr
   219  
   220  	_, err = client.CoreV1().ConfigMaps("beacon-chain").Update(ctx, cm, metav1.UpdateOptions{})
   221  
   222  	return err
   223  }