eth, rpc: standardize the chain sync progress counters
This commit is contained in:
		@@ -130,10 +130,9 @@ type Downloader struct {
 | 
			
		||||
	interrupt int32 // Atomic boolean to signal termination
 | 
			
		||||
 | 
			
		||||
	// Statistics
 | 
			
		||||
	importStart time.Time // Instance when the last blocks were taken from the cache
 | 
			
		||||
	importQueue []*Block  // Previously taken blocks to check import progress
 | 
			
		||||
	importDone  int       // Number of taken blocks already imported from the last batch
 | 
			
		||||
	importLock  sync.Mutex
 | 
			
		||||
	syncStatsOrigin uint64       // Origin block number where syncing started at
 | 
			
		||||
	syncStatsHeight uint64       // Highest block number known when syncing started
 | 
			
		||||
	syncStatsLock   sync.RWMutex // Lock protecting the sync stats fields
 | 
			
		||||
 | 
			
		||||
	// Callbacks
 | 
			
		||||
	hasBlock    hashCheckFn      // Checks if a block is present in the chain
 | 
			
		||||
@@ -161,6 +160,7 @@ type Downloader struct {
 | 
			
		||||
	cancelLock sync.RWMutex  // Lock to protect the cancel channel in delivers
 | 
			
		||||
 | 
			
		||||
	// Testing hooks
 | 
			
		||||
	syncInitHook    func(uint64, uint64)  // Method to call upon initiating a new sync run
 | 
			
		||||
	bodyFetchHook   func([]*types.Header) // Method to call upon starting a block body fetch
 | 
			
		||||
	chainInsertHook func([]*Block)        // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
 | 
			
		||||
}
 | 
			
		||||
@@ -192,27 +192,14 @@ func New(mux *event.TypeMux, hasBlock hashCheckFn, getBlock blockRetrievalFn, he
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stats retrieves the current status of the downloader.
 | 
			
		||||
func (d *Downloader) Stats() (pending int, cached int, importing int, estimate time.Duration) {
 | 
			
		||||
	// Fetch the download status
 | 
			
		||||
	pending, cached = d.queue.Size()
 | 
			
		||||
// Boundaries retrieves the synchronisation boundaries, specifically the origin
 | 
			
		||||
// block where synchronisation started at (may have failed/suspended) and the
 | 
			
		||||
// latest known block which the synchonisation targets.
 | 
			
		||||
func (d *Downloader) Boundaries() (uint64, uint64) {
 | 
			
		||||
	d.syncStatsLock.RLock()
 | 
			
		||||
	defer d.syncStatsLock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	// Figure out the import progress
 | 
			
		||||
	d.importLock.Lock()
 | 
			
		||||
	defer d.importLock.Unlock()
 | 
			
		||||
 | 
			
		||||
	for len(d.importQueue) > 0 && d.hasBlock(d.importQueue[0].RawBlock.Hash()) {
 | 
			
		||||
		d.importQueue = d.importQueue[1:]
 | 
			
		||||
		d.importDone++
 | 
			
		||||
	}
 | 
			
		||||
	importing = len(d.importQueue)
 | 
			
		||||
 | 
			
		||||
	// Make an estimate on the total sync
 | 
			
		||||
	estimate = 0
 | 
			
		||||
	if d.importDone > 0 {
 | 
			
		||||
		estimate = time.Since(d.importStart) / time.Duration(d.importDone) * time.Duration(pending+cached+importing)
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
	return d.syncStatsOrigin, d.syncStatsHeight
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Synchronising returns whether the downloader is currently retrieving blocks.
 | 
			
		||||
@@ -333,14 +320,29 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
 | 
			
		||||
 | 
			
		||||
	switch {
 | 
			
		||||
	case p.version == eth61:
 | 
			
		||||
		// Old eth/61, use forward, concurrent hash and block retrieval algorithm
 | 
			
		||||
		number, err := d.findAncestor61(p)
 | 
			
		||||
		// Look up the sync boundaries: the common ancestor and the target block
 | 
			
		||||
		latest, err := d.fetchHeight61(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		origin, err := d.findAncestor61(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		d.syncStatsLock.Lock()
 | 
			
		||||
		if d.syncStatsHeight <= origin || d.syncStatsOrigin > origin {
 | 
			
		||||
			d.syncStatsOrigin = origin
 | 
			
		||||
		}
 | 
			
		||||
		d.syncStatsHeight = latest
 | 
			
		||||
		d.syncStatsLock.Unlock()
 | 
			
		||||
 | 
			
		||||
		// Initiate the sync using a  concurrent hash and block retrieval algorithm
 | 
			
		||||
		if d.syncInitHook != nil {
 | 
			
		||||
			d.syncInitHook(origin, latest)
 | 
			
		||||
		}
 | 
			
		||||
		errc := make(chan error, 2)
 | 
			
		||||
		go func() { errc <- d.fetchHashes61(p, td, number+1) }()
 | 
			
		||||
		go func() { errc <- d.fetchBlocks61(number + 1) }()
 | 
			
		||||
		go func() { errc <- d.fetchHashes61(p, td, origin+1) }()
 | 
			
		||||
		go func() { errc <- d.fetchBlocks61(origin + 1) }()
 | 
			
		||||
 | 
			
		||||
		// If any fetcher fails, cancel the other
 | 
			
		||||
		if err := <-errc; err != nil {
 | 
			
		||||
@@ -351,14 +353,29 @@ func (d *Downloader) syncWithPeer(p *peer, hash common.Hash, td *big.Int) (err e
 | 
			
		||||
		return <-errc
 | 
			
		||||
 | 
			
		||||
	case p.version >= eth62:
 | 
			
		||||
		// New eth/62, use forward, concurrent header and block body retrieval algorithm
 | 
			
		||||
		number, err := d.findAncestor(p)
 | 
			
		||||
		// Look up the sync boundaries: the common ancestor and the target block
 | 
			
		||||
		latest, err := d.fetchHeight(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		origin, err := d.findAncestor(p)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		d.syncStatsLock.Lock()
 | 
			
		||||
		if d.syncStatsHeight <= origin || d.syncStatsOrigin > origin {
 | 
			
		||||
			d.syncStatsOrigin = origin
 | 
			
		||||
		}
 | 
			
		||||
		d.syncStatsHeight = latest
 | 
			
		||||
		d.syncStatsLock.Unlock()
 | 
			
		||||
 | 
			
		||||
		// Initiate the sync using a  concurrent hash and block retrieval algorithm
 | 
			
		||||
		if d.syncInitHook != nil {
 | 
			
		||||
			d.syncInitHook(origin, latest)
 | 
			
		||||
		}
 | 
			
		||||
		errc := make(chan error, 2)
 | 
			
		||||
		go func() { errc <- d.fetchHeaders(p, td, number+1) }()
 | 
			
		||||
		go func() { errc <- d.fetchBodies(number + 1) }()
 | 
			
		||||
		go func() { errc <- d.fetchHeaders(p, td, origin+1) }()
 | 
			
		||||
		go func() { errc <- d.fetchBodies(origin + 1) }()
 | 
			
		||||
 | 
			
		||||
		// If any fetcher fails, cancel the other
 | 
			
		||||
		if err := <-errc; err != nil {
 | 
			
		||||
@@ -401,6 +418,50 @@ func (d *Downloader) Terminate() {
 | 
			
		||||
	d.cancel()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fetchHeight61 retrieves the head block of the remote peer to aid in estimating
 | 
			
		||||
// the total time a pending synchronisation would take.
 | 
			
		||||
func (d *Downloader) fetchHeight61(p *peer) (uint64, error) {
 | 
			
		||||
	glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
 | 
			
		||||
 | 
			
		||||
	// Request the advertised remote head block and wait for the response
 | 
			
		||||
	go p.getBlocks([]common.Hash{p.head})
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(blockSoftTTL)
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-d.cancelCh:
 | 
			
		||||
			return 0, errCancelBlockFetch
 | 
			
		||||
 | 
			
		||||
		case <-d.headerCh:
 | 
			
		||||
			// Out of bounds eth/62 block headers received, ignore them
 | 
			
		||||
 | 
			
		||||
		case <-d.bodyCh:
 | 
			
		||||
			// Out of bounds eth/62 block bodies received, ignore them
 | 
			
		||||
 | 
			
		||||
		case <-d.hashCh:
 | 
			
		||||
			// Out of bounds hashes received, ignore them
 | 
			
		||||
 | 
			
		||||
		case blockPack := <-d.blockCh:
 | 
			
		||||
			// Discard anything not from the origin peer
 | 
			
		||||
			if blockPack.peerId != p.id {
 | 
			
		||||
				glog.V(logger.Debug).Infof("Received blocks from incorrect peer(%s)", blockPack.peerId)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			// Make sure the peer actually gave something valid
 | 
			
		||||
			blocks := blockPack.blocks
 | 
			
		||||
			if len(blocks) != 1 {
 | 
			
		||||
				glog.V(logger.Debug).Infof("%v: invalid number of head blocks: %d != 1", p, len(blocks))
 | 
			
		||||
				return 0, errBadPeer
 | 
			
		||||
			}
 | 
			
		||||
			return blocks[0].NumberU64(), nil
 | 
			
		||||
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			glog.V(logger.Debug).Infof("%v: head block timeout", p)
 | 
			
		||||
			return 0, errTimeout
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findAncestor61 tries to locate the common ancestor block of the local chain and
 | 
			
		||||
// a remote peers blockchain. In the general case when our node was in sync and
 | 
			
		||||
// on the correct chain, checking the top N blocks should already get us a match.
 | 
			
		||||
@@ -776,6 +837,50 @@ func (d *Downloader) fetchBlocks61(from uint64) error {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// fetchHeight retrieves the head header of the remote peer to aid in estimating
 | 
			
		||||
// the total time a pending synchronisation would take.
 | 
			
		||||
func (d *Downloader) fetchHeight(p *peer) (uint64, error) {
 | 
			
		||||
	glog.V(logger.Debug).Infof("%v: retrieving remote chain height", p)
 | 
			
		||||
 | 
			
		||||
	// Request the advertised remote head block and wait for the response
 | 
			
		||||
	go p.getRelHeaders(p.head, 1, 0, false)
 | 
			
		||||
 | 
			
		||||
	timeout := time.After(headerTTL)
 | 
			
		||||
	for {
 | 
			
		||||
		select {
 | 
			
		||||
		case <-d.cancelCh:
 | 
			
		||||
			return 0, errCancelBlockFetch
 | 
			
		||||
 | 
			
		||||
		case headerPack := <-d.headerCh:
 | 
			
		||||
			// Discard anything not from the origin peer
 | 
			
		||||
			if headerPack.peerId != p.id {
 | 
			
		||||
				glog.V(logger.Debug).Infof("Received headers from incorrect peer(%s)", headerPack.peerId)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
			// Make sure the peer actually gave something valid
 | 
			
		||||
			headers := headerPack.headers
 | 
			
		||||
			if len(headers) != 1 {
 | 
			
		||||
				glog.V(logger.Debug).Infof("%v: invalid number of head headers: %d != 1", p, len(headers))
 | 
			
		||||
				return 0, errBadPeer
 | 
			
		||||
			}
 | 
			
		||||
			return headers[0].Number.Uint64(), nil
 | 
			
		||||
 | 
			
		||||
		case <-d.bodyCh:
 | 
			
		||||
			// Out of bounds block bodies received, ignore them
 | 
			
		||||
 | 
			
		||||
		case <-d.hashCh:
 | 
			
		||||
			// Out of bounds eth/61 hashes received, ignore them
 | 
			
		||||
 | 
			
		||||
		case <-d.blockCh:
 | 
			
		||||
			// Out of bounds eth/61 blocks received, ignore them
 | 
			
		||||
 | 
			
		||||
		case <-timeout:
 | 
			
		||||
			glog.V(logger.Debug).Infof("%v: head header timeout", p)
 | 
			
		||||
			return 0, errTimeout
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findAncestor tries to locate the common ancestor block of the local chain and
 | 
			
		||||
// a remote peers blockchain. In the general case when our node was in sync and
 | 
			
		||||
// on the correct chain, checking the top N blocks should already get us a match.
 | 
			
		||||
@@ -1203,16 +1308,10 @@ func (d *Downloader) process() {
 | 
			
		||||
			d.process()
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	// Release the lock upon exit (note, before checking for reentry!), and set
 | 
			
		||||
	// Release the lock upon exit (note, before checking for reentry!)
 | 
			
		||||
	// the import statistics to zero.
 | 
			
		||||
	defer func() {
 | 
			
		||||
		d.importLock.Lock()
 | 
			
		||||
		d.importQueue = nil
 | 
			
		||||
		d.importDone = 0
 | 
			
		||||
		d.importLock.Unlock()
 | 
			
		||||
	defer atomic.StoreInt32(&d.processing, 0)
 | 
			
		||||
 | 
			
		||||
		atomic.StoreInt32(&d.processing, 0)
 | 
			
		||||
	}()
 | 
			
		||||
	// Repeat the processing as long as there are blocks to import
 | 
			
		||||
	for {
 | 
			
		||||
		// Fetch the next batch of blocks
 | 
			
		||||
@@ -1223,13 +1322,6 @@ func (d *Downloader) process() {
 | 
			
		||||
		if d.chainInsertHook != nil {
 | 
			
		||||
			d.chainInsertHook(blocks)
 | 
			
		||||
		}
 | 
			
		||||
		// Reset the import statistics
 | 
			
		||||
		d.importLock.Lock()
 | 
			
		||||
		d.importStart = time.Now()
 | 
			
		||||
		d.importQueue = blocks
 | 
			
		||||
		d.importDone = 0
 | 
			
		||||
		d.importLock.Unlock()
 | 
			
		||||
 | 
			
		||||
		// Actually import the blocks
 | 
			
		||||
		glog.V(logger.Debug).Infof("Inserting chain with %d blocks (#%v - #%v)\n", len(blocks), blocks[0].RawBlock.Number(), blocks[len(blocks)-1].RawBlock.Number())
 | 
			
		||||
		for len(blocks) != 0 {
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"math/big"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"sync/atomic"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
@@ -99,6 +100,8 @@ type downloadTester struct {
 | 
			
		||||
	peerHashes   map[string][]common.Hash                // Hash chain belonging to different test peers
 | 
			
		||||
	peerBlocks   map[string]map[common.Hash]*types.Block // Blocks belonging to different test peers
 | 
			
		||||
	peerChainTds map[string]map[common.Hash]*big.Int     // Total difficulties of the blocks in the peer chains
 | 
			
		||||
 | 
			
		||||
	lock sync.RWMutex
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newTester creates a new downloader test mocker.
 | 
			
		||||
@@ -118,8 +121,8 @@ func newTester() *downloadTester {
 | 
			
		||||
 | 
			
		||||
// sync starts synchronizing with a remote peer, blocking until it completes.
 | 
			
		||||
func (dl *downloadTester) sync(id string, td *big.Int) error {
 | 
			
		||||
	dl.lock.RLock()
 | 
			
		||||
	hash := dl.peerHashes[id][0]
 | 
			
		||||
 | 
			
		||||
	// If no particular TD was requested, load from the peer's blockchain
 | 
			
		||||
	if td == nil {
 | 
			
		||||
		td = big.NewInt(1)
 | 
			
		||||
@@ -127,14 +130,19 @@ func (dl *downloadTester) sync(id string, td *big.Int) error {
 | 
			
		||||
			td = diff
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	err := dl.downloader.synchronise(id, hash, td)
 | 
			
		||||
	dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	err := dl.downloader.synchronise(id, hash, td)
 | 
			
		||||
	for {
 | 
			
		||||
		// If the queue is empty and processing stopped, break
 | 
			
		||||
		hashes, blocks := dl.downloader.queue.Size()
 | 
			
		||||
		if hashes+blocks == 0 && atomic.LoadInt32(&dl.downloader.processing) == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		// If there are queued blocks, but the head is missing, it's a stale leftover
 | 
			
		||||
		if hashes+blocks > 0 && atomic.LoadInt32(&dl.downloader.processing) == 0 && dl.downloader.queue.GetHeadBlock() == nil {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		// Otherwise sleep a bit and retry
 | 
			
		||||
		time.Sleep(time.Millisecond)
 | 
			
		||||
	}
 | 
			
		||||
@@ -143,26 +151,41 @@ func (dl *downloadTester) sync(id string, td *big.Int) error {
 | 
			
		||||
 | 
			
		||||
// hasBlock checks if a block is pres	ent in the testers canonical chain.
 | 
			
		||||
func (dl *downloadTester) hasBlock(hash common.Hash) bool {
 | 
			
		||||
	dl.lock.RLock()
 | 
			
		||||
	defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return dl.getBlock(hash) != nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getBlock retrieves a block from the testers canonical chain.
 | 
			
		||||
func (dl *downloadTester) getBlock(hash common.Hash) *types.Block {
 | 
			
		||||
	dl.lock.RLock()
 | 
			
		||||
	defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return dl.ownBlocks[hash]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// headBlock retrieves the current head block from the canonical chain.
 | 
			
		||||
func (dl *downloadTester) headBlock() *types.Block {
 | 
			
		||||
	dl.lock.RLock()
 | 
			
		||||
	defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return dl.getBlock(dl.ownHashes[len(dl.ownHashes)-1])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// getTd retrieves the block's total difficulty from the canonical chain.
 | 
			
		||||
func (dl *downloadTester) getTd(hash common.Hash) *big.Int {
 | 
			
		||||
	dl.lock.RLock()
 | 
			
		||||
	defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
	return dl.ownChainTd[hash]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// insertChain injects a new batch of blocks into the simulated chain.
 | 
			
		||||
func (dl *downloadTester) insertChain(blocks types.Blocks) (int, error) {
 | 
			
		||||
	dl.lock.Lock()
 | 
			
		||||
	defer dl.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	for i, block := range blocks {
 | 
			
		||||
		if _, ok := dl.ownBlocks[block.ParentHash()]; !ok {
 | 
			
		||||
			return i, errors.New("unknown parent")
 | 
			
		||||
@@ -183,9 +206,12 @@ func (dl *downloadTester) newPeer(id string, version int, hashes []common.Hash,
 | 
			
		||||
// specific delay time on processing the network packets sent to it, simulating
 | 
			
		||||
// potentially slow network IO.
 | 
			
		||||
func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Hash, blocks map[common.Hash]*types.Block, delay time.Duration) error {
 | 
			
		||||
	dl.lock.Lock()
 | 
			
		||||
	defer dl.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	err := dl.downloader.RegisterPeer(id, version, hashes[0],
 | 
			
		||||
		dl.peerGetRelHashesFn(id, delay), dl.peerGetAbsHashesFn(id, delay), dl.peerGetBlocksFn(id, delay),
 | 
			
		||||
		nil, dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay))
 | 
			
		||||
		dl.peerGetRelHeadersFn(id, delay), dl.peerGetAbsHeadersFn(id, delay), dl.peerGetBodiesFn(id, delay))
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		// Assign the owned hashes and blocks to the peer (deep copy)
 | 
			
		||||
		dl.peerHashes[id] = make([]common.Hash, len(hashes))
 | 
			
		||||
@@ -207,6 +233,9 @@ func (dl *downloadTester) newSlowPeer(id string, version int, hashes []common.Ha
 | 
			
		||||
 | 
			
		||||
// dropPeer simulates a hard peer removal from the connection pool.
 | 
			
		||||
func (dl *downloadTester) dropPeer(id string) {
 | 
			
		||||
	dl.lock.Lock()
 | 
			
		||||
	defer dl.lock.Unlock()
 | 
			
		||||
 | 
			
		||||
	delete(dl.peerHashes, id)
 | 
			
		||||
	delete(dl.peerBlocks, id)
 | 
			
		||||
	delete(dl.peerChainTds, id)
 | 
			
		||||
@@ -221,6 +250,9 @@ func (dl *downloadTester) peerGetRelHashesFn(id string, delay time.Duration) fun
 | 
			
		||||
	return func(head common.Hash) error {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		// Gather the next batch of hashes
 | 
			
		||||
		hashes := dl.peerHashes[id]
 | 
			
		||||
		result := make([]common.Hash, 0, MaxHashFetch)
 | 
			
		||||
@@ -250,6 +282,9 @@ func (dl *downloadTester) peerGetAbsHashesFn(id string, delay time.Duration) fun
 | 
			
		||||
	return func(head uint64, count int) error {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		// Gather the next batch of hashes
 | 
			
		||||
		hashes := dl.peerHashes[id]
 | 
			
		||||
		result := make([]common.Hash, 0, count)
 | 
			
		||||
@@ -271,6 +306,10 @@ func (dl *downloadTester) peerGetAbsHashesFn(id string, delay time.Duration) fun
 | 
			
		||||
func (dl *downloadTester) peerGetBlocksFn(id string, delay time.Duration) func([]common.Hash) error {
 | 
			
		||||
	return func(hashes []common.Hash) error {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		blocks := dl.peerBlocks[id]
 | 
			
		||||
		result := make([]*types.Block, 0, len(hashes))
 | 
			
		||||
		for _, hash := range hashes {
 | 
			
		||||
@@ -284,6 +323,27 @@ func (dl *downloadTester) peerGetBlocksFn(id string, delay time.Duration) func([
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// peerGetRelHeadersFn constructs a GetBlockHeaders function based on a hashed
 | 
			
		||||
// origin; associated with a particular peer in the download tester. The returned
 | 
			
		||||
// function can be used to retrieve batches of headers from the particular peer.
 | 
			
		||||
func (dl *downloadTester) peerGetRelHeadersFn(id string, delay time.Duration) func(common.Hash, int, int, bool) error {
 | 
			
		||||
	return func(origin common.Hash, amount int, skip int, reverse bool) error {
 | 
			
		||||
		// Find the canonical number of the hash
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		number := uint64(0)
 | 
			
		||||
		for num, hash := range dl.peerHashes[id] {
 | 
			
		||||
			if hash == origin {
 | 
			
		||||
				number = uint64(len(dl.peerHashes[id]) - num - 1)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		// Use the absolute header fetcher to satisfy the query
 | 
			
		||||
		return dl.peerGetAbsHeadersFn(id, delay)(number, amount, skip, reverse)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// peerGetAbsHeadersFn constructs a GetBlockHeaders function based on a numbered
 | 
			
		||||
// origin; associated with a particular peer in the download tester. The returned
 | 
			
		||||
// function can be used to retrieve batches of headers from the particular peer.
 | 
			
		||||
@@ -291,6 +351,9 @@ func (dl *downloadTester) peerGetAbsHeadersFn(id string, delay time.Duration) fu
 | 
			
		||||
	return func(origin uint64, amount int, skip int, reverse bool) error {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		// Gather the next batch of hashes
 | 
			
		||||
		hashes := dl.peerHashes[id]
 | 
			
		||||
		blocks := dl.peerBlocks[id]
 | 
			
		||||
@@ -315,6 +378,10 @@ func (dl *downloadTester) peerGetAbsHeadersFn(id string, delay time.Duration) fu
 | 
			
		||||
func (dl *downloadTester) peerGetBodiesFn(id string, delay time.Duration) func([]common.Hash) error {
 | 
			
		||||
	return func(hashes []common.Hash) error {
 | 
			
		||||
		time.Sleep(delay)
 | 
			
		||||
 | 
			
		||||
		dl.lock.RLock()
 | 
			
		||||
		defer dl.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
		blocks := dl.peerBlocks[id]
 | 
			
		||||
 | 
			
		||||
		transactions := make([][]*types.Transaction, 0, len(hashes))
 | 
			
		||||
@@ -384,13 +451,23 @@ func testThrottling(t *testing.T, protocol int) {
 | 
			
		||||
		errc <- tester.sync("peer", nil)
 | 
			
		||||
	}()
 | 
			
		||||
	// Iteratively take some blocks, always checking the retrieval count
 | 
			
		||||
	for len(tester.ownBlocks) < targetBlocks+1 {
 | 
			
		||||
	for {
 | 
			
		||||
		// Check the retrieval count synchronously (! reason for this ugly block)
 | 
			
		||||
		tester.lock.RLock()
 | 
			
		||||
		retrieved := len(tester.ownBlocks)
 | 
			
		||||
		tester.lock.RUnlock()
 | 
			
		||||
		if retrieved >= targetBlocks+1 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		// Wait a bit for sync to throttle itself
 | 
			
		||||
		var cached int
 | 
			
		||||
		for start := time.Now(); time.Since(start) < time.Second; {
 | 
			
		||||
			time.Sleep(25 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
			tester.downloader.queue.lock.RLock()
 | 
			
		||||
			cached = len(tester.downloader.queue.blockPool)
 | 
			
		||||
			tester.downloader.queue.lock.RUnlock()
 | 
			
		||||
 | 
			
		||||
			if cached == blockCacheLimit || len(tester.ownBlocks)+cached+int(atomic.LoadUint32(&blocked)) == targetBlocks+1 {
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
@@ -727,3 +804,259 @@ func testBlockBodyAttackerDropping(t *testing.T, protocol int) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests that synchronisation boundaries (origin block number and highest block
 | 
			
		||||
// number) is tracked and updated correctly.
 | 
			
		||||
func TestSyncBoundaries61(t *testing.T) { testSyncBoundaries(t, 61) }
 | 
			
		||||
func TestSyncBoundaries62(t *testing.T) { testSyncBoundaries(t, 62) }
 | 
			
		||||
func TestSyncBoundaries63(t *testing.T) { testSyncBoundaries(t, 63) }
 | 
			
		||||
func TestSyncBoundaries64(t *testing.T) { testSyncBoundaries(t, 64) }
 | 
			
		||||
 | 
			
		||||
func testSyncBoundaries(t *testing.T, protocol int) {
 | 
			
		||||
	// Create a small enough block chain to download
 | 
			
		||||
	targetBlocks := blockCacheLimit - 15
 | 
			
		||||
	hashes, blocks := makeChain(targetBlocks, 0, genesis)
 | 
			
		||||
 | 
			
		||||
	// Set a sync init hook to catch boundary changes
 | 
			
		||||
	starting := make(chan struct{})
 | 
			
		||||
	progress := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	tester := newTester()
 | 
			
		||||
	tester.downloader.syncInitHook = func(origin, latest uint64) {
 | 
			
		||||
		starting <- struct{}{}
 | 
			
		||||
		<-progress
 | 
			
		||||
	}
 | 
			
		||||
	// Retrieve the sync boundaries and ensure they are zero (pristine sync)
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != 0 {
 | 
			
		||||
		t.Fatalf("Pristine boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, 0)
 | 
			
		||||
	}
 | 
			
		||||
	// Synchronise half the blocks and check initial boundaries
 | 
			
		||||
	tester.newPeer("peer-half", protocol, hashes[targetBlocks/2:], blocks)
 | 
			
		||||
	pending := new(sync.WaitGroup)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("peer-half", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(targetBlocks/2+1) {
 | 
			
		||||
		t.Fatalf("Initial boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, targetBlocks/2+1)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
 | 
			
		||||
	// Synchronise all the blocks and check continuation boundaries
 | 
			
		||||
	tester.newPeer("peer-full", protocol, hashes, blocks)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("peer-full", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != uint64(targetBlocks/2+1) || latest != uint64(targetBlocks) {
 | 
			
		||||
		t.Fatalf("Completing boundary mismatch: have %v/%v, want %v/%v", origin, latest, targetBlocks/2+1, targetBlocks)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests that synchronisation boundaries (origin block number and highest block
 | 
			
		||||
// number) is tracked and updated correctly in case of a fork (or manual head
 | 
			
		||||
// revertal).
 | 
			
		||||
func TestForkedSyncBoundaries61(t *testing.T) { testForkedSyncBoundaries(t, 61) }
 | 
			
		||||
func TestForkedSyncBoundaries62(t *testing.T) { testForkedSyncBoundaries(t, 62) }
 | 
			
		||||
func TestForkedSyncBoundaries63(t *testing.T) { testForkedSyncBoundaries(t, 63) }
 | 
			
		||||
func TestForkedSyncBoundaries64(t *testing.T) { testForkedSyncBoundaries(t, 64) }
 | 
			
		||||
 | 
			
		||||
func testForkedSyncBoundaries(t *testing.T, protocol int) {
 | 
			
		||||
	// Create a forked chain to simulate origin revertal
 | 
			
		||||
	common, fork := MaxHashFetch, 2*MaxHashFetch
 | 
			
		||||
	hashesA, hashesB, blocksA, blocksB := makeChainFork(common+fork, fork, genesis)
 | 
			
		||||
 | 
			
		||||
	// Set a sync init hook to catch boundary changes
 | 
			
		||||
	starting := make(chan struct{})
 | 
			
		||||
	progress := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	tester := newTester()
 | 
			
		||||
	tester.downloader.syncInitHook = func(origin, latest uint64) {
 | 
			
		||||
		starting <- struct{}{}
 | 
			
		||||
		<-progress
 | 
			
		||||
	}
 | 
			
		||||
	// Retrieve the sync boundaries and ensure they are zero (pristine sync)
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != 0 {
 | 
			
		||||
		t.Fatalf("Pristine boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, 0)
 | 
			
		||||
	}
 | 
			
		||||
	// Synchronise with one of the forks and check boundaries
 | 
			
		||||
	tester.newPeer("fork A", protocol, hashesA, blocksA)
 | 
			
		||||
	pending := new(sync.WaitGroup)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("fork A", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(len(hashesA)-1) {
 | 
			
		||||
		t.Fatalf("Initial boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, len(hashesA)-1)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
 | 
			
		||||
	// Simulate a successful sync above the fork
 | 
			
		||||
	tester.downloader.syncStatsOrigin = tester.downloader.syncStatsHeight
 | 
			
		||||
 | 
			
		||||
	// Synchronise with the second fork and check boundary resets
 | 
			
		||||
	tester.newPeer("fork B", protocol, hashesB, blocksB)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("fork B", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != uint64(common) || latest != uint64(len(hashesB)-1) {
 | 
			
		||||
		t.Fatalf("Forking boundary mismatch: have %v/%v, want %v/%v", origin, latest, common, len(hashesB)-1)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests that if synchronisation is aborted due to some failure, then the boundary
 | 
			
		||||
// origin is not updated in the next sync cycle, as it should be considered the
 | 
			
		||||
// continuation of the previous sync and not a new instance.
 | 
			
		||||
func TestFailedSyncBoundaries61(t *testing.T) { testFailedSyncBoundaries(t, 61) }
 | 
			
		||||
func TestFailedSyncBoundaries62(t *testing.T) { testFailedSyncBoundaries(t, 62) }
 | 
			
		||||
func TestFailedSyncBoundaries63(t *testing.T) { testFailedSyncBoundaries(t, 63) }
 | 
			
		||||
func TestFailedSyncBoundaries64(t *testing.T) { testFailedSyncBoundaries(t, 64) }
 | 
			
		||||
 | 
			
		||||
func testFailedSyncBoundaries(t *testing.T, protocol int) {
 | 
			
		||||
	// Create a small enough block chain to download
 | 
			
		||||
	targetBlocks := blockCacheLimit - 15
 | 
			
		||||
	hashes, blocks := makeChain(targetBlocks, 0, genesis)
 | 
			
		||||
 | 
			
		||||
	// Set a sync init hook to catch boundary changes
 | 
			
		||||
	starting := make(chan struct{})
 | 
			
		||||
	progress := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	tester := newTester()
 | 
			
		||||
	tester.downloader.syncInitHook = func(origin, latest uint64) {
 | 
			
		||||
		starting <- struct{}{}
 | 
			
		||||
		<-progress
 | 
			
		||||
	}
 | 
			
		||||
	// Retrieve the sync boundaries and ensure they are zero (pristine sync)
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != 0 {
 | 
			
		||||
		t.Fatalf("Pristine boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, 0)
 | 
			
		||||
	}
 | 
			
		||||
	// Attempt a full sync with a faulty peer
 | 
			
		||||
	tester.newPeer("faulty", protocol, hashes, blocks)
 | 
			
		||||
	missing := targetBlocks / 2
 | 
			
		||||
	delete(tester.peerBlocks["faulty"], hashes[missing])
 | 
			
		||||
 | 
			
		||||
	pending := new(sync.WaitGroup)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("faulty", nil); err == nil {
 | 
			
		||||
			t.Fatalf("succeeded faulty synchronisation")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(targetBlocks) {
 | 
			
		||||
		t.Fatalf("Initial boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, targetBlocks)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
 | 
			
		||||
	// Synchronise with a good peer and check that the boundary origin remind the same after a failure
 | 
			
		||||
	tester.newPeer("valid", protocol, hashes, blocks)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("valid", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(targetBlocks) {
 | 
			
		||||
		t.Fatalf("Completing boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, targetBlocks)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Tests that if an attacker fakes a chain height, after the attack is detected,
 | 
			
		||||
// the boundary height is successfully reduced at the next sync invocation.
 | 
			
		||||
func TestFakedSyncBoundaries61(t *testing.T) { testFakedSyncBoundaries(t, 61) }
 | 
			
		||||
func TestFakedSyncBoundaries62(t *testing.T) { testFakedSyncBoundaries(t, 62) }
 | 
			
		||||
func TestFakedSyncBoundaries63(t *testing.T) { testFakedSyncBoundaries(t, 63) }
 | 
			
		||||
func TestFakedSyncBoundaries64(t *testing.T) { testFakedSyncBoundaries(t, 64) }
 | 
			
		||||
 | 
			
		||||
func testFakedSyncBoundaries(t *testing.T, protocol int) {
 | 
			
		||||
	// Create a small block chain
 | 
			
		||||
	targetBlocks := blockCacheLimit - 15
 | 
			
		||||
	hashes, blocks := makeChain(targetBlocks+3, 0, genesis)
 | 
			
		||||
 | 
			
		||||
	// Set a sync init hook to catch boundary changes
 | 
			
		||||
	starting := make(chan struct{})
 | 
			
		||||
	progress := make(chan struct{})
 | 
			
		||||
 | 
			
		||||
	tester := newTester()
 | 
			
		||||
	tester.downloader.syncInitHook = func(origin, latest uint64) {
 | 
			
		||||
		starting <- struct{}{}
 | 
			
		||||
		<-progress
 | 
			
		||||
	}
 | 
			
		||||
	// Retrieve the sync boundaries and ensure they are zero (pristine sync)
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != 0 {
 | 
			
		||||
		t.Fatalf("Pristine boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, 0)
 | 
			
		||||
	}
 | 
			
		||||
	//  Create and sync with an attacker that promises a higher chain than available
 | 
			
		||||
	tester.newPeer("attack", protocol, hashes, blocks)
 | 
			
		||||
	for i := 1; i < 3; i++ {
 | 
			
		||||
		delete(tester.peerBlocks["attack"], hashes[i])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	pending := new(sync.WaitGroup)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("attack", nil); err == nil {
 | 
			
		||||
			t.Fatalf("succeeded attacker synchronisation")
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(targetBlocks+3) {
 | 
			
		||||
		t.Fatalf("Initial boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, targetBlocks+3)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
 | 
			
		||||
	// Synchronise with a good peer and check that the boundary height has been reduced to the true value
 | 
			
		||||
	tester.newPeer("valid", protocol, hashes[3:], blocks)
 | 
			
		||||
	pending.Add(1)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
		defer pending.Done()
 | 
			
		||||
		if err := tester.sync("valid", nil); err != nil {
 | 
			
		||||
			t.Fatalf("failed to synchronise blocks: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
	}()
 | 
			
		||||
	<-starting
 | 
			
		||||
	if origin, latest := tester.downloader.Boundaries(); origin != 0 || latest != uint64(targetBlocks) {
 | 
			
		||||
		t.Fatalf("Initial boundary mismatch: have %v/%v, want %v/%v", origin, latest, 0, targetBlocks)
 | 
			
		||||
	}
 | 
			
		||||
	progress <- struct{}{}
 | 
			
		||||
	pending.Wait()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -55,7 +55,6 @@ var (
 | 
			
		||||
		"admin_exportChain":        (*adminApi).ExportChain,
 | 
			
		||||
		"admin_importChain":        (*adminApi).ImportChain,
 | 
			
		||||
		"admin_verbosity":          (*adminApi).Verbosity,
 | 
			
		||||
		"admin_chainSyncStatus":    (*adminApi).ChainSyncStatus,
 | 
			
		||||
		"admin_setSolc":            (*adminApi).SetSolc,
 | 
			
		||||
		"admin_datadir":            (*adminApi).DataDir,
 | 
			
		||||
		"admin_startRPC":           (*adminApi).StartRPC,
 | 
			
		||||
@@ -232,17 +231,6 @@ func (self *adminApi) Verbosity(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *adminApi) ChainSyncStatus(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	pending, cached, importing, estimate := self.ethereum.Downloader().Stats()
 | 
			
		||||
 | 
			
		||||
	return map[string]interface{}{
 | 
			
		||||
		"blocksAvailable":        pending,
 | 
			
		||||
		"blocksWaitingForImport": cached,
 | 
			
		||||
		"importing":              importing,
 | 
			
		||||
		"estimate":               estimate.String(),
 | 
			
		||||
	}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *adminApi) SetSolc(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	args := new(SetSolcArgs)
 | 
			
		||||
	if err := self.coder.Decode(req.Params, &args); err != nil {
 | 
			
		||||
 
 | 
			
		||||
@@ -143,10 +143,6 @@ web3._extend({
 | 
			
		||||
		new web3._extend.Property({
 | 
			
		||||
			name: 'datadir',
 | 
			
		||||
			getter: 'admin_datadir'
 | 
			
		||||
		}),
 | 
			
		||||
		new web3._extend.Property({
 | 
			
		||||
			name: 'chainSyncStatus',
 | 
			
		||||
			getter: 'admin_chainSyncStatus'
 | 
			
		||||
		})
 | 
			
		||||
	]
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -55,6 +55,7 @@ var (
 | 
			
		||||
		"eth_protocolVersion":                     (*ethApi).ProtocolVersion,
 | 
			
		||||
		"eth_coinbase":                            (*ethApi).Coinbase,
 | 
			
		||||
		"eth_mining":                              (*ethApi).IsMining,
 | 
			
		||||
		"eth_syncing":                             (*ethApi).IsSyncing,
 | 
			
		||||
		"eth_gasPrice":                            (*ethApi).GasPrice,
 | 
			
		||||
		"eth_getStorage":                          (*ethApi).GetStorage,
 | 
			
		||||
		"eth_storageAt":                           (*ethApi).GetStorage,
 | 
			
		||||
@@ -166,6 +167,20 @@ func (self *ethApi) IsMining(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	return self.xeth.IsMining(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *ethApi) IsSyncing(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	current := self.ethereum.ChainManager().CurrentBlock().NumberU64()
 | 
			
		||||
	origin, height := self.ethereum.Downloader().Boundaries()
 | 
			
		||||
 | 
			
		||||
	if current < height {
 | 
			
		||||
		return map[string]interface{}{
 | 
			
		||||
			"startingBlock": origin,
 | 
			
		||||
			"currentBlock":  current,
 | 
			
		||||
			"highestBlock":  height,
 | 
			
		||||
		}, nil
 | 
			
		||||
	}
 | 
			
		||||
	return false, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (self *ethApi) GasPrice(req *shared.Request) (interface{}, error) {
 | 
			
		||||
	return newHexNum(self.xeth.DefaultGasPrice().Bytes()), nil
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -42,6 +42,10 @@ web3._extend({
 | 
			
		||||
		new web3._extend.Property({
 | 
			
		||||
			name: 'pendingTransactions',
 | 
			
		||||
			getter: 'eth_pendingTransactions'
 | 
			
		||||
		}),
 | 
			
		||||
		new web3._extend.Property({
 | 
			
		||||
			name: 'syncing',
 | 
			
		||||
			getter: 'eth_syncing'
 | 
			
		||||
		})
 | 
			
		||||
	]
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,6 @@ var (
 | 
			
		||||
	AutoCompletion = map[string][]string{
 | 
			
		||||
		"admin": []string{
 | 
			
		||||
			"addPeer",
 | 
			
		||||
			"chainSyncStatus",
 | 
			
		||||
			"datadir",
 | 
			
		||||
			"exportChain",
 | 
			
		||||
			"getContractInfo",
 | 
			
		||||
@@ -99,6 +98,7 @@ var (
 | 
			
		||||
			"sendRawTransaction",
 | 
			
		||||
			"sendTransaction",
 | 
			
		||||
			"sign",
 | 
			
		||||
			"syncing",
 | 
			
		||||
		},
 | 
			
		||||
		"miner": []string{
 | 
			
		||||
			"hashrate",
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user