mirror of
https://gitlab.com/pulsechaincom/prysm-pulse.git
synced 2024-12-26 21:27:19 +00:00
b6cd890a67
Former-commit-id: f7344b7d3fb2fa07f0fd421c5ed3721f6c7a9258 [formerly e678ab2f1681d9492c0e1b142cd06ee08a462fdb] Former-commit-id: d942da3d5cfcde921bebb149c26edd5c4ed178dd
254 lines
7.3 KiB
Go
254 lines
7.3 KiB
Go
// Package mainchain defines services that interacts with a Geth node via RPC.
|
|
// This package is useful for an actor in a sharded system to interact with
|
|
// a Sharding Manager Contract.
|
|
package mainchain
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"math/big"
|
|
"os"
|
|
"time"
|
|
|
|
ethereum "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/common"
|
|
gethTypes "github.com/ethereum/go-ethereum/core/types"
|
|
"github.com/ethereum/go-ethereum/ethclient"
|
|
"github.com/ethereum/go-ethereum/node"
|
|
"github.com/ethereum/go-ethereum/rpc"
|
|
"github.com/prysmaticlabs/prysm/client/contracts"
|
|
"github.com/sirupsen/logrus"
|
|
)
|
|
|
|
var log = logrus.WithField("prefix", "mainchain")
|
|
|
|
// ClientIdentifier tells us what client the node we interact with over RPC is running.
|
|
const ClientIdentifier = "geth"
|
|
|
|
// SMCClient defines a struct that interacts with a
|
|
// mainchain node via RPC. Specifically, it aids in SMC bindings that are useful
|
|
// to other sharding services.
|
|
type SMCClient struct {
|
|
endpoint string // Endpoint to JSON RPC.
|
|
dataDirPath string // Path to the data directory.
|
|
depositFlag bool // Keeps track of the deposit option passed in as via CLI flags.
|
|
passwordFile string // Path to the account password file.
|
|
client *ethclient.Client // Ethereum RPC client.
|
|
keystore *keystore.KeyStore // Keystore containing the single signer.
|
|
smc *contracts.SMC // The deployed sharding management contract.
|
|
rpcClient *rpc.Client // The RPC client connection to the main geth node.
|
|
}
|
|
|
|
// NewSMCClient constructs a new instance of an SMCClient.
|
|
func NewSMCClient(endpoint string, dataDirPath string, depositFlag bool, passwordFile string) (*SMCClient, error) {
|
|
config := &node.Config{
|
|
DataDir: dataDirPath,
|
|
}
|
|
|
|
scryptN, scryptP, keydir, err := config.AccountConfig()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
ks := keystore.NewKeyStore(keydir, scryptN, scryptP)
|
|
|
|
smcClient := &SMCClient{
|
|
keystore: ks,
|
|
endpoint: endpoint,
|
|
depositFlag: depositFlag,
|
|
dataDirPath: dataDirPath,
|
|
passwordFile: passwordFile,
|
|
}
|
|
|
|
return smcClient, nil
|
|
}
|
|
|
|
// Start the SMC Client and connect to running geth node.
|
|
func (s *SMCClient) Start() {
|
|
// Sets up a connection to a Geth node via RPC.
|
|
rpcClient, err := dialRPC(s.endpoint)
|
|
if err != nil {
|
|
log.Panicf("Cannot start rpc client: %v", err)
|
|
return
|
|
}
|
|
|
|
s.rpcClient = rpcClient
|
|
s.client = ethclient.NewClient(rpcClient)
|
|
|
|
// Check account existence and unlock account before starting.
|
|
accounts := s.keystore.Accounts()
|
|
if len(accounts) == 0 {
|
|
log.Panic("No accounts found")
|
|
return
|
|
}
|
|
|
|
if err := s.unlockAccount(accounts[0]); err != nil {
|
|
log.Panicf("Cannot unlock account: %v", err)
|
|
return
|
|
}
|
|
|
|
// Initializes bindings to SMC.
|
|
smc, err := initSMC(s)
|
|
if err != nil {
|
|
log.Panicf("Failed to initialize SMC: %v", err)
|
|
return
|
|
}
|
|
|
|
s.smc = smc
|
|
}
|
|
|
|
// Stop SMCClient immediately. This cancels any pending RPC connections.
|
|
func (s *SMCClient) Stop() error {
|
|
s.rpcClient.Close()
|
|
return nil
|
|
}
|
|
|
|
// CreateTXOpts creates a *TransactOpts with a signer using the default account on the keystore.
|
|
func (s *SMCClient) CreateTXOpts(value *big.Int) (*bind.TransactOpts, error) {
|
|
account := s.Account()
|
|
|
|
return &bind.TransactOpts{
|
|
From: account.Address,
|
|
Value: value,
|
|
Signer: func(signer gethTypes.Signer, addr common.Address, tx *gethTypes.Transaction) (*gethTypes.Transaction, error) {
|
|
networkID, err := s.client.NetworkID(context.Background())
|
|
if err != nil {
|
|
return nil, fmt.Errorf("unable to fetch networkID: %v", err)
|
|
}
|
|
return s.keystore.SignTx(*account, tx, networkID /* chainID */)
|
|
},
|
|
}, nil
|
|
}
|
|
|
|
// Account to use for sharding transactions.
|
|
func (s *SMCClient) Account() *accounts.Account {
|
|
accounts := s.keystore.Accounts()
|
|
return &accounts[0]
|
|
}
|
|
|
|
// ChainReader for interacting with the chain.
|
|
func (s *SMCClient) ChainReader() ethereum.ChainReader {
|
|
return ethereum.ChainReader(s.client)
|
|
}
|
|
|
|
// SMCCaller to interact with the sharding manager contract.
|
|
func (s *SMCClient) SMCCaller() *contracts.SMCCaller {
|
|
if s.smc == nil {
|
|
return nil
|
|
}
|
|
return &s.smc.SMCCaller
|
|
}
|
|
|
|
// SMCTransactor allows us to send tx's to the SMC programmatically.
|
|
func (s *SMCClient) SMCTransactor() *contracts.SMCTransactor {
|
|
if s.smc == nil {
|
|
return nil
|
|
}
|
|
return &s.smc.SMCTransactor
|
|
}
|
|
|
|
// SMCFilterer allows for easy filtering of events from the Sharding Manager Contract.
|
|
func (s *SMCClient) SMCFilterer() *contracts.SMCFilterer {
|
|
if s.smc == nil {
|
|
return nil
|
|
}
|
|
return &s.smc.SMCFilterer
|
|
}
|
|
|
|
// WaitForTransaction waits for transaction to be mined and returns an error if it takes
|
|
// too long.
|
|
func (s *SMCClient) WaitForTransaction(ctx context.Context, hash common.Hash, durationInSeconds time.Duration) error {
|
|
|
|
ctxTimeout, cancel := context.WithTimeout(ctx, durationInSeconds*time.Second)
|
|
|
|
for pending, err := true, error(nil); pending; _, pending, err = s.client.TransactionByHash(ctxTimeout, hash) {
|
|
if err != nil {
|
|
cancel()
|
|
return fmt.Errorf("unable to retrieve transaction: %v", err)
|
|
}
|
|
if ctxTimeout.Err() != nil {
|
|
cancel()
|
|
return fmt.Errorf("transaction timed out, transaction was not able to be mined in the duration: %v", ctxTimeout.Err())
|
|
}
|
|
}
|
|
cancel()
|
|
ctxTimeout.Done()
|
|
log.Infof("Transaction: %s has been mined", hash.Hex())
|
|
return nil
|
|
}
|
|
|
|
// TransactionReceipt allows an SMCClient to retrieve transaction receipts on
|
|
// the mainchain by hash.
|
|
func (s *SMCClient) TransactionReceipt(hash common.Hash) (*gethTypes.Receipt, error) {
|
|
|
|
receipt, err := s.client.TransactionReceipt(context.Background(), hash)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return receipt, err
|
|
}
|
|
|
|
// DepositFlag returns true for cli flag --deposit.
|
|
func (s *SMCClient) DepositFlag() bool {
|
|
return s.depositFlag
|
|
}
|
|
|
|
// SetDepositFlag updates the deposit flag property of SMCClient.
|
|
func (s *SMCClient) SetDepositFlag(deposit bool) {
|
|
s.depositFlag = deposit
|
|
}
|
|
|
|
// DataDirPath returns the datadir flag as a string.
|
|
func (s *SMCClient) DataDirPath() string {
|
|
return s.dataDirPath
|
|
}
|
|
|
|
// unlockAccount will unlock the specified account using utils.PasswordFileFlag
|
|
// or empty string if unset.
|
|
func (s *SMCClient) unlockAccount(account accounts.Account) error {
|
|
pass := ""
|
|
|
|
if s.passwordFile != "" {
|
|
file, err := os.Open(s.passwordFile)
|
|
if err != nil {
|
|
return fmt.Errorf("unable to open file containing account password %s. %v", s.passwordFile, err)
|
|
}
|
|
scanner := bufio.NewScanner(file)
|
|
scanner.Split(bufio.ScanWords)
|
|
if !scanner.Scan() {
|
|
err = scanner.Err()
|
|
if err != nil {
|
|
return fmt.Errorf("unable to read contents of file %v", err)
|
|
}
|
|
return errors.New("password not found in file")
|
|
}
|
|
|
|
pass = scanner.Text()
|
|
}
|
|
|
|
return s.keystore.Unlock(account, pass)
|
|
}
|
|
|
|
// Sign signs the hash of collationHeader contents by
|
|
// using default account on keystore and returns signed signature.
|
|
func (s *SMCClient) Sign(hash common.Hash) ([]byte, error) {
|
|
account := s.Account()
|
|
return s.keystore.SignHash(*account, hash.Bytes())
|
|
}
|
|
|
|
// GetShardCount gets the count of the total shards
|
|
// currently operating in the sharded universe.
|
|
func (s *SMCClient) GetShardCount() (int64, error) {
|
|
shardCount, err := s.SMCCaller().ShardCount(&bind.CallOpts{})
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return shardCount.Int64(), nil
|
|
}
|