Commit 6fb8ae2b authored by Péter Szilágyi's avatar Péter Szilágyi Committed by GitHub

Merge pull request #2979 from karalabe/ethclient-sync-api

ethereum, ethclient: add SyncProgress API endpoint
parents eac390f2 2924fdfc
...@@ -19,6 +19,7 @@ package downloader ...@@ -19,6 +19,7 @@ package downloader
import ( import (
"sync" "sync"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/event" "github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"golang.org/x/net/context" "golang.org/x/net/context"
...@@ -73,9 +74,10 @@ func (api *PublicDownloaderAPI) eventLoop() { ...@@ -73,9 +74,10 @@ func (api *PublicDownloaderAPI) eventLoop() {
var notification interface{} var notification interface{}
switch event.Data.(type) { switch event.Data.(type) {
case StartEvent: case StartEvent:
result := &SyncingResult{Syncing: true} notification = &SyncingResult{
result.Status.Origin, result.Status.Current, result.Status.Height, result.Status.Pulled, result.Status.Known = api.d.Progress() Syncing: true,
notification = result Status: api.d.Progress(),
}
case DoneEvent, FailedEvent: case DoneEvent, FailedEvent:
notification = false notification = false
} }
...@@ -117,19 +119,10 @@ func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, ...@@ -117,19 +119,10 @@ func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription,
return rpcSub, nil return rpcSub, nil
} }
// Progress gives progress indications when the node is synchronising with the Ethereum network.
type Progress struct {
Origin uint64 `json:"startingBlock"`
Current uint64 `json:"currentBlock"`
Height uint64 `json:"highestBlock"`
Pulled uint64 `json:"pulledStates"`
Known uint64 `json:"knownStates"`
}
// SyncingResult provides information about the current synchronisation status for this node. // SyncingResult provides information about the current synchronisation status for this node.
type SyncingResult struct { type SyncingResult struct {
Syncing bool `json:"syncing"` Syncing bool `json:"syncing"`
Status Progress `json:"status"` Status ethereum.SyncProgress `json:"status"`
} }
// uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop. // uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop.
......
...@@ -28,6 +28,7 @@ import ( ...@@ -28,6 +28,7 @@ import (
"sync/atomic" "sync/atomic"
"time" "time"
ethereum "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types" "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/ethdb"
...@@ -211,7 +212,7 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, ha ...@@ -211,7 +212,7 @@ func New(stateDb ethdb.Database, mux *event.TypeMux, hasHeader headerCheckFn, ha
// In addition, during the state download phase of fast synchronisation the number // In addition, during the state download phase of fast synchronisation the number
// of processed and the total number of known states are also returned. Otherwise // of processed and the total number of known states are also returned. Otherwise
// these are zero. // these are zero.
func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { func (d *Downloader) Progress() ethereum.SyncProgress {
// Fetch the pending state count outside of the lock to prevent unforeseen deadlocks // Fetch the pending state count outside of the lock to prevent unforeseen deadlocks
pendingStates := uint64(d.queue.PendingNodeData()) pendingStates := uint64(d.queue.PendingNodeData())
...@@ -228,7 +229,13 @@ func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) { ...@@ -228,7 +229,13 @@ func (d *Downloader) Progress() (uint64, uint64, uint64, uint64, uint64) {
case LightSync: case LightSync:
current = d.headHeader().Number.Uint64() current = d.headHeader().Number.Uint64()
} }
return d.syncStatsChainOrigin, current, d.syncStatsChainHeight, d.syncStatsStateDone, d.syncStatsStateDone + pendingStates return ethereum.SyncProgress{
StartingBlock: d.syncStatsChainOrigin,
CurrentBlock: current,
HighestBlock: d.syncStatsChainHeight,
PulledStates: d.syncStatsStateDone,
KnownStates: d.syncStatsStateDone + pendingStates,
}
} }
// Synchronising returns whether the downloader is currently retrieving blocks. // Synchronising returns whether the downloader is currently retrieving blocks.
......
This diff is collapsed.
...@@ -191,6 +191,39 @@ func toBlockNumArg(number *big.Int) string { ...@@ -191,6 +191,39 @@ func toBlockNumArg(number *big.Int) string {
return fmt.Sprintf("%#x", number) return fmt.Sprintf("%#x", number)
} }
type rpcProgress struct {
StartingBlock rpc.HexNumber
CurrentBlock rpc.HexNumber
HighestBlock rpc.HexNumber
PulledStates rpc.HexNumber
KnownStates rpc.HexNumber
}
// SyncProgress retrieves the current progress of the sync algorithm. If there's
// no sync currently running, it returns nil.
func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, error) {
var raw json.RawMessage
if err := ec.c.CallContext(ctx, &raw, "eth_syncing"); err != nil {
return nil, err
}
// Handle the possible response types
var syncing bool
if err := json.Unmarshal(raw, &syncing); err == nil {
return nil, nil // Not syncing (always false)
}
var progress *rpcProgress
if err := json.Unmarshal(raw, &progress); err != nil {
return nil, err
}
return &ethereum.SyncProgress{
StartingBlock: progress.StartingBlock.Uint64(),
CurrentBlock: progress.CurrentBlock.Uint64(),
HighestBlock: progress.HighestBlock.Uint64(),
PulledStates: progress.PulledStates.Uint64(),
KnownStates: progress.KnownStates.Uint64(),
}, nil
}
// SubscribeNewHead subscribes to notifications about the current blockchain head // SubscribeNewHead subscribes to notifications about the current blockchain head
// on the given channel. // on the given channel.
func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) { func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (ethereum.Subscription, error) {
......
...@@ -6,6 +6,7 @@ import "github.com/ethereum/go-ethereum" ...@@ -6,6 +6,7 @@ import "github.com/ethereum/go-ethereum"
var ( var (
_ = ethereum.ChainReader(&Client{}) _ = ethereum.ChainReader(&Client{})
_ = ethereum.ChainStateReader(&Client{}) _ = ethereum.ChainStateReader(&Client{})
_ = ethereum.ChainSyncReader(&Client{})
_ = ethereum.ChainHeadEventer(&Client{}) _ = ethereum.ChainHeadEventer(&Client{})
_ = ethereum.ContractCaller(&Client{}) _ = ethereum.ContractCaller(&Client{})
_ = ethereum.GasEstimator(&Client{}) _ = ethereum.GasEstimator(&Client{})
......
...@@ -67,6 +67,22 @@ type ChainStateReader interface { ...@@ -67,6 +67,22 @@ type ChainStateReader interface {
NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error)
} }
// SyncProgress gives progress indications when the node is synchronising with
// the Ethereum network.
type SyncProgress struct {
StartingBlock uint64 // Block number where sync began
CurrentBlock uint64 // Current block number where sync is at
HighestBlock uint64 // Highest alleged block number in the chain
PulledStates uint64 // Number of state trie entries already downloaded
KnownStates uint64 // Total number os state trie entries known about
}
// ChainSyncReader wraps access to the node's current sync status. If there's no
// sync currently running, it returns nil.
type ChainSyncReader interface {
SyncProgress(ctx context.Context) (*SyncProgress, error)
}
// A ChainHeadEventer returns notifications whenever the canonical head block is updated. // A ChainHeadEventer returns notifications whenever the canonical head block is updated.
type ChainHeadEventer interface { type ChainHeadEventer interface {
SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header) (Subscription, error)
......
...@@ -73,19 +73,19 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber { ...@@ -73,19 +73,19 @@ func (s *PublicEthereumAPI) ProtocolVersion() *rpc.HexNumber {
// - pulledStates: number of state entries processed until now // - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled // - knownStates: number of known state entries that still need to be pulled
func (s *PublicEthereumAPI) Syncing() (interface{}, error) { func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
origin, current, height, pulled, known := s.b.Downloader().Progress() progress := s.b.Downloader().Progress()
// Return not syncing if the synchronisation already completed // Return not syncing if the synchronisation already completed
if current >= height { if progress.CurrentBlock >= progress.HighestBlock {
return false, nil return false, nil
} }
// Otherwise gather the block sync stats // Otherwise gather the block sync stats
return map[string]interface{}{ return map[string]interface{}{
"startingBlock": rpc.NewHexNumber(origin), "startingBlock": rpc.NewHexNumber(progress.StartingBlock),
"currentBlock": rpc.NewHexNumber(current), "currentBlock": rpc.NewHexNumber(progress.CurrentBlock),
"highestBlock": rpc.NewHexNumber(height), "highestBlock": rpc.NewHexNumber(progress.HighestBlock),
"pulledStates": rpc.NewHexNumber(pulled), "pulledStates": rpc.NewHexNumber(progress.PulledStates),
"knownStates": rpc.NewHexNumber(known), "knownStates": rpc.NewHexNumber(progress.KnownStates),
}, nil }, nil
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment