Files
prysm/beacon-chain/powchain/service.go
Raul Jordan 4d5d229f0f beacon: Define a Core Blockchain Package and Persisted Structure for Beacon (#278)
Former-commit-id: bbd5b46e7f64f762350d6fb496492207e70d7130 [formerly 43a37f7139b7d1d90f0c27a7406b63bdf390ad96]
Former-commit-id: bb7a2ff0a7619f8de0bd38cd2c9eb0de7c189edb
2018-07-19 11:31:50 -05:00

154 lines
4.8 KiB
Go

package powchain
import (
"context"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum"
"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/rpc"
log "github.com/sirupsen/logrus"
)
// Reader defines a struct that can fetch latest header events from a web3 endpoint.
type Reader interface {
SubscribeNewHead(ctx context.Context, ch chan<- *gethTypes.Header) (ethereum.Subscription, error)
}
// Logger subscribe filtered log on the PoW chain
type Logger interface {
SubscribeFilterLogs(ctx context.Context, q ethereum.FilterQuery, ch chan<- gethTypes.Log) (ethereum.Subscription, error)
}
// Web3Service fetches important information about the canonical
// Ethereum PoW chain via a web3 endpoint using an ethclient. The Random
// Beacon Chain requires synchronization with the PoW chain's current
// blockhash, block number, and access to logs within the
// Validator Registration Contract on the PoW chain to kick off the beacon
// chain's validator registration process.
type Web3Service struct {
ctx context.Context
cancel context.CancelFunc
headerChan chan *gethTypes.Header
logChan chan gethTypes.Log
pubKey string
endpoint string
validatorRegistered bool
vrcAddress common.Address
blockNumber *big.Int // the latest PoW chain blocknumber.
blockHash common.Hash // the latest PoW chain blockhash.
}
// Web3ServiceConfig defines a config struct for web3 service to use through its life cycle.
type Web3ServiceConfig struct {
Endpoint string
Pubkey string
VrcAddr common.Address
}
// NewWeb3Service sets up a new instance with an ethclient when
// given a web3 endpoint as a string.
func NewWeb3Service(ctx context.Context, config *Web3ServiceConfig) (*Web3Service, error) {
if !strings.HasPrefix(config.Endpoint, "ws") && !strings.HasPrefix(config.Endpoint, "ipc") {
return nil, fmt.Errorf("web3service requires either an IPC or WebSocket endpoint, provided %s", config.Endpoint)
}
web3ctx, cancel := context.WithCancel(ctx)
return &Web3Service{
ctx: web3ctx,
cancel: cancel,
headerChan: make(chan *gethTypes.Header),
logChan: make(chan gethTypes.Log),
pubKey: config.Pubkey,
endpoint: config.Endpoint,
validatorRegistered: false,
blockNumber: nil,
blockHash: common.BytesToHash([]byte{}),
vrcAddress: config.VrcAddr,
}, nil
}
// Start a web3 service's main event loop.
func (w *Web3Service) Start() {
log.Infof("Starting web3 proof-of-work chain service at %s", w.endpoint)
rpcClient, err := rpc.Dial(w.endpoint)
if err != nil {
log.Errorf("Cannot connect to PoW chain RPC client: %v", err)
return
}
client := ethclient.NewClient(rpcClient)
go w.latestPOWChainInfo(client, w.ctx.Done())
go w.queryValidatorStatus(client, w.ctx.Done())
}
// Stop the web3 service's main event loop and associated goroutines.
func (w *Web3Service) Stop() error {
defer w.cancel()
defer close(w.headerChan)
log.Info("Stopping web3 proof-of-work chain service")
return nil
}
func (w *Web3Service) latestPOWChainInfo(reader Reader, done <-chan struct{}) {
if _, err := reader.SubscribeNewHead(w.ctx, w.headerChan); err != nil {
log.Errorf("Unable to subscribe to incoming PoW chain headers: %v", err)
return
}
for {
select {
case <-done:
return
case header := <-w.headerChan:
w.blockNumber = header.Number
w.blockHash = header.Hash()
log.Debugf("Latest PoW chain blocknumber: %v", w.blockNumber)
log.Debugf("Latest PoW chain blockhash: %v", w.blockHash.Hex())
}
}
}
func (w *Web3Service) queryValidatorStatus(logger Logger, done <-chan struct{}) {
query := ethereum.FilterQuery{
Addresses: []common.Address{
w.vrcAddress,
},
}
_, err := logger.SubscribeFilterLogs(context.Background(), query, w.logChan)
if err != nil {
log.Errorf("Unable to query logs from VRC: %v", err)
return
}
for {
select {
case <-done:
return
case VRClog := <-w.logChan:
// public key is the second topic from validatorRegistered log and strip off 0x
pubKeyLog := VRClog.Topics[1].Hex()[2:]
if pubKeyLog == w.pubKey {
log.Infof("Validator registered in VRC with public key: %v", pubKeyLog)
w.validatorRegistered = true
return
}
}
}
}
// LatestBlockNumber is a getter for blockNumber to make it read-only.
func (w *Web3Service) LatestBlockNumber() *big.Int {
return w.blockNumber
}
// LatestBlockHash is a getter for blockHash to make it read-only.
func (w *Web3Service) LatestBlockHash() common.Hash {
return w.blockHash
}
// ValidatorRegistered is a getter for validatorRegistered to make it read-only.
func (w *Web3Service) ValidatorRegistered() bool {
return w.validatorRegistered
}