github.com/prysmaticlabs/prysm@v1.4.4/endtoend/components/eth1.go (about) 1 package components 2 3 import ( 4 "bytes" 5 "context" 6 "errors" 7 "fmt" 8 "io/ioutil" 9 "math/big" 10 "os" 11 "os/exec" 12 "path" 13 "strings" 14 "time" 15 16 "github.com/bazelbuild/rules_go/go/tools/bazel" 17 "github.com/ethereum/go-ethereum/accounts/abi/bind" 18 "github.com/ethereum/go-ethereum/accounts/keystore" 19 "github.com/ethereum/go-ethereum/core/types" 20 "github.com/ethereum/go-ethereum/ethclient" 21 "github.com/ethereum/go-ethereum/rpc" 22 contracts "github.com/prysmaticlabs/prysm/contracts/deposit-contract" 23 "github.com/prysmaticlabs/prysm/endtoend/helpers" 24 e2e "github.com/prysmaticlabs/prysm/endtoend/params" 25 e2etypes "github.com/prysmaticlabs/prysm/endtoend/types" 26 "github.com/prysmaticlabs/prysm/shared/params" 27 ) 28 29 const timeGapPerTX = 100 * time.Millisecond 30 const timeGapPerMiningTX = 250 * time.Millisecond 31 32 var _ e2etypes.ComponentRunner = (*Eth1Node)(nil) 33 34 // Eth1Node represents ETH1 node. 35 type Eth1Node struct { 36 e2etypes.ComponentRunner 37 started chan struct{} 38 keystorePath string 39 } 40 41 // NewEth1Node creates and returns ETH1 node. 42 func NewEth1Node() *Eth1Node { 43 return &Eth1Node{ 44 started: make(chan struct{}, 1), 45 } 46 } 47 48 // KeystorePath exposes node's keystore path. 49 func (node *Eth1Node) KeystorePath() string { 50 return node.keystorePath 51 } 52 53 // Start starts an ETH1 local dev chain and deploys a deposit contract. 54 func (node *Eth1Node) Start(ctx context.Context) error { 55 binaryPath, found := bazel.FindBinary("cmd/geth", "geth") 56 if !found { 57 return errors.New("go-ethereum binary not found") 58 } 59 60 eth1Path := path.Join(e2e.TestParams.TestPath, "eth1data/") 61 // Clear out ETH1 to prevent issues. 62 if _, err := os.Stat(eth1Path); !os.IsNotExist(err) { 63 if err = os.RemoveAll(eth1Path); err != nil { 64 return err 65 } 66 } 67 68 args := []string{ 69 fmt.Sprintf("--datadir=%s", eth1Path), 70 fmt.Sprintf("--rpcport=%d", e2e.TestParams.Eth1RPCPort), 71 fmt.Sprintf("--ws.port=%d", e2e.TestParams.Eth1RPCPort+1), 72 "--rpc", 73 "--rpcaddr=127.0.0.1", 74 "--rpccorsdomain=\"*\"", 75 "--rpcvhosts=\"*\"", 76 "--rpc.allow-unprotected-txs", 77 "--ws", 78 "--ws.addr=127.0.0.1", 79 "--ws.origins=\"*\"", 80 "--dev", 81 "--dev.period=2", 82 "--ipcdisable", 83 } 84 cmd := exec.CommandContext(ctx, binaryPath, args...) 85 file, err := helpers.DeleteAndCreateFile(e2e.TestParams.LogPath, "eth1.log") 86 if err != nil { 87 return err 88 } 89 cmd.Stdout = file 90 cmd.Stderr = file 91 if err = cmd.Start(); err != nil { 92 return fmt.Errorf("failed to start eth1 chain: %w", err) 93 } 94 95 if err = helpers.WaitForTextInFile(file, "Commit new mining work"); err != nil { 96 return fmt.Errorf("mining log not found, this means the eth1 chain had issues starting: %w", err) 97 } 98 99 // Connect to the started geth dev chain. 100 client, err := rpc.DialHTTP(fmt.Sprintf("http://127.0.0.1:%d", e2e.TestParams.Eth1RPCPort)) 101 if err != nil { 102 return fmt.Errorf("failed to connect to ipc: %w", err) 103 } 104 web3 := ethclient.NewClient(client) 105 106 // Access the dev account keystore to deploy the contract. 107 fileName, err := exec.Command("ls", path.Join(eth1Path, "keystore")).Output() 108 if err != nil { 109 return err 110 } 111 keystorePath := path.Join(eth1Path, fmt.Sprintf("keystore/%s", strings.TrimSpace(string(fileName)))) 112 jsonBytes, err := ioutil.ReadFile(keystorePath) 113 if err != nil { 114 return err 115 } 116 store, err := keystore.DecryptKey(jsonBytes, "" /*password*/) 117 if err != nil { 118 return err 119 } 120 121 // Advancing the blocks eth1follow distance to prevent issues reading the chain. 122 if err = mineBlocks(web3, store, params.BeaconConfig().Eth1FollowDistance); err != nil { 123 return fmt.Errorf("unable to advance chain: %w", err) 124 } 125 126 txOpts, err := bind.NewTransactorWithChainID(bytes.NewReader(jsonBytes), "" /*password*/, big.NewInt(1337)) 127 if err != nil { 128 return err 129 } 130 nonce, err := web3.PendingNonceAt(context.Background(), store.Address) 131 if err != nil { 132 return err 133 } 134 txOpts.Nonce = big.NewInt(int64(nonce)) 135 txOpts.Context = context.Background() 136 contractAddr, tx, _, err := contracts.DeployDepositContract(txOpts, web3, txOpts.From) 137 if err != nil { 138 return fmt.Errorf("failed to deploy deposit contract: %w", err) 139 } 140 e2e.TestParams.ContractAddress = contractAddr 141 142 // Wait for contract to mine. 143 for pending := true; pending; _, pending, err = web3.TransactionByHash(context.Background(), tx.Hash()) { 144 if err != nil { 145 return err 146 } 147 time.Sleep(timeGapPerTX) 148 } 149 150 // Advancing the blocks another eth1follow distance to prevent issues reading the chain. 151 if err = mineBlocks(web3, store, params.BeaconConfig().Eth1FollowDistance); err != nil { 152 return fmt.Errorf("unable to advance chain: %w", err) 153 } 154 155 // Save keystore path (used for saving and mining deposits). 156 node.keystorePath = keystorePath 157 158 // Mark node as ready. 159 close(node.started) 160 161 return cmd.Wait() 162 } 163 164 // Started checks whether ETH1 node is started and ready to be queried. 165 func (node *Eth1Node) Started() <-chan struct{} { 166 return node.started 167 } 168 169 func mineBlocks(web3 *ethclient.Client, keystore *keystore.Key, blocksToMake uint64) error { 170 nonce, err := web3.PendingNonceAt(context.Background(), keystore.Address) 171 if err != nil { 172 return err 173 } 174 chainID, err := web3.NetworkID(context.Background()) 175 if err != nil { 176 return err 177 } 178 block, err := web3.BlockByNumber(context.Background(), nil) 179 if err != nil { 180 return err 181 } 182 finishBlock := block.NumberU64() + blocksToMake 183 184 for block.NumberU64() <= finishBlock { 185 spamTX := types.NewTransaction(nonce, keystore.Address, big.NewInt(0), 21000, big.NewInt(1e6), []byte{}) 186 signed, err := types.SignTx(spamTX, types.NewEIP155Signer(chainID), keystore.PrivateKey) 187 if err != nil { 188 return err 189 } 190 if err = web3.SendTransaction(context.Background(), signed); err != nil { 191 return err 192 } 193 nonce++ 194 time.Sleep(timeGapPerMiningTX) 195 block, err = web3.BlockByNumber(context.Background(), nil) 196 if err != nil { 197 return err 198 } 199 } 200 return nil 201 }