mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-31 23:41:22 +00:00
a092f8d19d
Former-commit-id: 4367137b9310a7c7a74e569499def685c06c1c09 [formerly 3947fb77747b13e5bb61c0bfc51127a0df4f5796] Former-commit-id: 0abf3e2beaffc399d53a1f1919a9b5a7b7512aee
171 lines
5.0 KiB
Go
171 lines
5.0 KiB
Go
package sharding
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io/ioutil"
|
|
"math/big"
|
|
"strings"
|
|
|
|
"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)
|
|
}
|
|
|
|
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) {
|
|
|
|
accounts := c.keystore.Accounts()
|
|
if len(accounts) == 0 {
|
|
return bind.TransactOpts{}, fmt.Errorf("no accounts found")
|
|
}
|
|
|
|
if err := c.unlockAccount(accounts[0]); err != nil {
|
|
return bind.TransactOpts{}, fmt.Errorf("unable to unlock account 0: %v", err)
|
|
}
|
|
|
|
if value.Cmp(big.NewInt(0)) == 0 {
|
|
return bind.TransactOpts{
|
|
From: accounts[0].Address,
|
|
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(accounts[0], tx, networkID /* chainID */)
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
return bind.TransactOpts{
|
|
From: accounts[0].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(accounts[0], tx, networkID /* chainID */)
|
|
},
|
|
}, nil
|
|
|
|
}
|
|
|
|
// dialRPC endpoint to node.
|
|
func dialRPC(endpoint string) (*rpc.Client, error) {
|
|
if endpoint == "" {
|
|
endpoint = node.DefaultIPCEndpoint(clientIdentifier)
|
|
}
|
|
return rpc.Dial(endpoint)
|
|
}
|