-
Notifications
You must be signed in to change notification settings - Fork 973
/
client.go
188 lines (159 loc) · 5.39 KB
/
client.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package sharding
//go:generate abigen --sol contracts/validator_manager.sol --pkg contracts --out contracts/validator_manager.go
import (
"context"
"fmt"
"io/ioutil"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/sharding/contracts"
cli "gopkg.in/urfave/cli.v1"
)
const (
clientIdentifier = "geth" // Used to determine the ipc name.
)
// Client for sharding. Communicates to geth node via JSON RPC.
type Client struct {
endpoint string // Endpoint to JSON RPC
client *ethclient.Client // Ethereum RPC client.
keystore *keystore.KeyStore // Keystore containing the single signer
ctx *cli.Context // Command line context
vmc *contracts.VMC // The deployed validator management contract
}
// MakeShardingClient for interfacing with geth full node.
func MakeShardingClient(ctx *cli.Context) *Client {
path := node.DefaultDataDir()
if ctx.GlobalIsSet(utils.DataDirFlag.Name) {
path = ctx.GlobalString(utils.DataDirFlag.Name)
}
endpoint := ctx.Args().First()
if endpoint == "" {
endpoint = fmt.Sprintf("%s/%s.ipc", path, clientIdentifier)
}
if ctx.GlobalIsSet(utils.IPCPathFlag.Name) {
endpoint = ctx.GlobalString(utils.IPCPathFlag.Name)
}
config := &node.Config{
DataDir: path,
}
scryptN, scryptP, keydir, err := config.AccountConfig()
if err != nil {
panic(err) // TODO(prestonvanloon): handle this
}
ks := keystore.NewKeyStore(keydir, scryptN, scryptP)
return &Client{
endpoint: endpoint,
keystore: ks,
ctx: ctx,
}
}
// Start the sharding client.
// * Connects to node.
// * Verifies or deploys the validator management contract.
func (c *Client) Start() error {
log.Info("Starting sharding client")
rpcClient, err := dialRPC(c.endpoint)
if err != nil {
return err
}
c.client = ethclient.NewClient(rpcClient)
defer rpcClient.Close()
if err := initVMC(c); err != nil {
return err
}
// Deposit 100ETH into the validator set in the VMC. Checks if account
// is already a validator in the VMC (in the case the client restarted).
// Once that's done we can subscribe to block headers.
//
// TODO: this function should store the validator's VMC index as a property
// in the client's struct
if err := joinValidatorSet(c); err != nil {
return err
}
// Listens to block headers from the geth node and if we are an eligible
// proposer, we fetch pending transactions and propose a collation
if err := subscribeBlockHeaders(c); err != nil {
return err
}
return nil
}
// Wait until sharding client is shutdown.
func (c *Client) Wait() {
log.Info("Sharding client has been shutdown...")
}
// WatchCollationHeaders checks the logs for add_header func calls
// and updates the head collation of the client. We can probably store
// this as a property of the client struct
func (c *Client) WatchCollationHeaders() {
}
// UnlockAccount will unlock the specified account using utils.PasswordFileFlag or empty string if unset.
func (c *Client) unlockAccount(account accounts.Account) error {
pass := ""
if c.ctx.GlobalIsSet(utils.PasswordFileFlag.Name) {
blob, err := ioutil.ReadFile(c.ctx.GlobalString(utils.PasswordFileFlag.Name))
if err != nil {
return fmt.Errorf("unable to read account password contents in file %s. %v", utils.PasswordFileFlag.Value, err)
}
// TODO: Use bufio.Scanner or other reader that doesn't include a trailing newline character.
pass = strings.Trim(string(blob), "\n") // Some text files end in new line, remove with strings.Trim.
}
return c.keystore.Unlock(account, pass)
}
func (c *Client) createTXOps(value *big.Int) (*bind.TransactOpts, error) {
account, err := c.Account()
if err != nil {
return nil, err
}
return &bind.TransactOpts{
From: account.Address,
Value: value,
Signer: func(signer types.Signer, addr common.Address, tx *types.Transaction) (*types.Transaction, error) {
networkID, err := c.client.NetworkID(context.Background())
if err != nil {
return nil, fmt.Errorf("unable to fetch networkID: %v", err)
}
return c.keystore.SignTx(*account, tx, networkID /* chainID */)
},
}, nil
}
// Account to use for sharding transactions.
func (c *Client) Account() (*accounts.Account, error) {
accounts := c.keystore.Accounts()
if len(accounts) == 0 {
return nil, fmt.Errorf("no accounts found")
}
if err := c.unlockAccount(accounts[0]); err != nil {
return nil, fmt.Errorf("cannot unlock account. %v", err)
}
return &accounts[0], nil
}
// ChainReader for interacting with the chain.
func (c *Client) ChainReader() ethereum.ChainReader {
return ethereum.ChainReader(c.client)
}
// Client to interact with ethereum node.
func (c *Client) Client() *ethclient.Client {
return c.client
}
// VMCCaller to interact with the validator management contract.
func (c *Client) VMCCaller() *contracts.VMCCaller {
return &c.vmc.VMCCaller
}
// dialRPC endpoint to node.
func dialRPC(endpoint string) (*rpc.Client, error) {
if endpoint == "" {
endpoint = node.DefaultIPCEndpoint(clientIdentifier)
}
return rpc.Dial(endpoint)
}