Skip to content
This repository has been archived by the owner on Dec 10, 2020. It is now read-only.

Implement full sync #183

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ Output:

### Example 1: Light sync

In this example, we will run two ethereumjs-clients. The first will be a fast sync client that
In this example, we will run two ethereumjs-clients. The first will be a full sync client that
will connect to the rinkeby network and start downloading the blockchain. The second will be a
light client that connects to the first client and syncs headers as they are downloaded.

Expand All @@ -187,7 +187,7 @@ listener. The second client will use libp2p to connect to the first client.
Run the first client and start downloading blocks:

```
ethereumjs --syncmode fast --lightserv true --datadir first --network rinkeby --transports rlpx libp2p:multiaddrs=/ip4/127.0.0.1/tcp/50505/ws
ethereumjs --syncmode full --lightserv true --datadir first --network rinkeby --transports rlpx libp2p:multiaddrs=/ip4/127.0.0.1/tcp/50505/ws
```

Output:
Expand Down Expand Up @@ -272,7 +272,7 @@ to help contributors better understand how the project is organized.
- `/docs` Contains auto-generated API docs.
- `/lib/blockchain` Contains the `Chain` class.
- `/lib/net` Contains all of the network layer classes including `Peer`, `Protocol` and its subclasses, `Server` and its subclasses, and `PeerPool`.
- `/lib/service` Contains the main Ethereum services (`FastEthereumService` and `LightEthereumService`).
- `/lib/service` Contains the main Ethereum services (`FullEthereumService` and `LightEthereumService`).
- `/lib/rpc` Contains the RPC server (optionally) embedded in the client.
- `/lib/sync` Contains the various chain synchronizers and `Fetcher` helpers.
- `/test` Contains test cases, testing helper functions, mocks and test data.
Expand All @@ -297,12 +297,12 @@ to help contributors better understand how the project is organized.
and `removed` events when new peers are added and removed and also emit the `message` event whenever
any of the peers in the pool emit a message. Each `Service` has an associated `PeerPool` and they are used primarily by `Synchronizer`s to help with blockchain synchronization.
- `Synchronizer` Subclasses of this class implements a specific blockchain synchronization strategy. They
also make use of subclasses of the `Fetcher` class that help fetch headers and bodies from pool peers. The fetchers internally make use of streams to handle things like queuing and backpressure. - `FastSynchronizer` [**In Progress**] Implements fast syncing of the blockchain - `LightSynchronizer` [**In Progress**] Implements light syncing of the blockchain
also make use of subclasses of the `Fetcher` class that help fetch headers and bodies from pool peers. The fetchers internally make use of streams to handle things like queuing and backpressure. - `FullSynchronizer` [**In Progress**] Implements full syncing of the blockchain - `LightSynchronizer` [**In Progress**] Implements light syncing of the blockchain
- `Handler` Subclasses of this class implements a protocol message handler. Handlers respond to incoming requests from peers.
- `EthHandler` [**In Progress**] Handles incoming ETH requests
- `LesHandler` [**In Progress**] Handles incoming LES requests
- `Service` Subclasses of `Service` will implement specific functionality of a `Node`. For example, the `EthereumService` subclasses will synchronize the blockchain using the fast or light sync protocols. Each service must specify which protocols it needs and define a `start()` and `stop()` function.
- `FastEthereumService` [**In Progress**] Implementation of ethereum fast sync.
- `Service` Subclasses of `Service` will implement specific functionality of a `Node`. For example, the `EthereumService` subclasses will synchronize the blockchain using the full or light sync protocols. Each service must specify which protocols it needs and define a `start()` and `stop()` function.
- `FullEthereumService` [**In Progress**] Implementation of ethereum full sync.
- `LightEthereumService` [**In Progress**] Implementation of ethereum light sync.
- `WhisperService` [**Not Started**] Implementation of an ethereum whisper node.
- `Node` [**In Progress**] Represents the top-level ethereum node, and is responsible for managing the lifecycle of included services.
Expand Down
2 changes: 1 addition & 1 deletion bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const args = require('yargs')
},
syncmode: {
describe: 'Blockchain sync mode',
choices: ['light', 'fast'],
choices: ['light', 'full'],
default: Config.SYNCMODE_DEFAULT,
},
lightserv: {
Expand Down
6 changes: 3 additions & 3 deletions browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,12 @@ export * from '../lib/node'

// Service
export * from '../lib/service/service'
export * from '../lib/service/fastethereumservice'
export * from '../lib/service/fullethereumservice'
export * from '../lib/service/lightethereumservice'

// Synchronizer
export * from '../lib/sync/sync'
export * from '../lib/sync/fastsync'
export * from '../lib/sync/fullsync'
export * from '../lib/sync/lightsync'

// Utilities
Expand All @@ -47,7 +47,7 @@ export function createNode(args: any) {
const options = {
common: new Common({ chain: args.network ?? 'mainnet' }),
servers: [new exports.Libp2pServer({ multiaddrs: [], ...args })],
syncmode: args.syncmode ?? 'fast',
syncmode: args.syncmode ?? 'full',
db: level(args.db ?? 'ethereumjs'),
logger: logger,
}
Expand Down
11 changes: 8 additions & 3 deletions lib/blockchain/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Blockchain from '@ethereumjs/blockchain'
import { BN, toBuffer } from 'ethereumjs-util'
import type { LevelUp } from 'levelup'
import { Config } from '../config'

import VM from '@ethereumjs/vm'
/**
* The options that the Blockchain constructor can receive.
*/
Expand Down Expand Up @@ -82,6 +82,7 @@ export class Chain extends EventEmitter {
public db: LevelUp
public blockchain: Blockchain
public opened: boolean
public vm: VM

private _headers: ChainHeaders = {
latest: null,
Expand Down Expand Up @@ -109,11 +110,15 @@ export class Chain extends EventEmitter {
new Blockchain({
db: options.db,
common: this.config.common,
validateBlocks: false,
validateConsensus: false,
validateBlocks: true,
validateConsensus: true,
})

this.db = this.blockchain.db
this.vm = new VM({
blockchain: this.blockchain,
common: this.config.common,
})
this.opened = false
}

Expand Down
6 changes: 3 additions & 3 deletions lib/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ export interface ConfigOptions {
common?: Common

/**
* Synchronization mode ('fast' or 'light')
* Synchronization mode ('full' or 'light')
*
* Default: 'fast'
* Default: 'full'
*/
syncmode?: string

Expand Down Expand Up @@ -101,7 +101,7 @@ export class Config {
// hardfork awareness is implemented within the library
// Also a fix for https://github.com/ethereumjs/ethereumjs-vm/issues/757
public static readonly COMMON_DEFAULT = new Common({ chain: 'mainnet', hardfork: 'chainstart' })
public static readonly SYNCMODE_DEFAULT = 'fast'
public static readonly SYNCMODE_DEFAULT = 'full'
public static readonly LIGHTSERV_DEFAULT = false
public static readonly DATADIR_DEFAULT = `${os.homedir()}/Library/Ethereum`
public static readonly TRANSPORTS_DEFAULT = ['rlpx:port=30303', 'libp2p']
Expand Down
2 changes: 1 addition & 1 deletion lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ exports.define('EthereumService', './service/ethereumservice')
// Synchronizer
exports.define('sync', './sync')
exports.define('Synchronizer', './sync/sync')
exports.define('FastSynchronizer', './sync/fastsync')
exports.define('FullSynchronizer', './sync/fullsync')
exports.define('LightSynchronizer', './sync/lightsync')

// Utilities
Expand Down
8 changes: 4 additions & 4 deletions lib/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import events from 'events'
import { LevelUp } from 'levelup'
import { BootnodeLike } from './types'
import { Config } from './config'
import { FastEthereumService, LightEthereumService } from './service'
import { FullEthereumService, LightEthereumService } from './service'

export interface NodeOptions {
/* Client configuration */
Expand All @@ -29,7 +29,7 @@ export interface NodeOptions {
export default class Node extends events.EventEmitter {
public config: Config

public services: (FastEthereumService | LightEthereumService)[]
public services: (FullEthereumService | LightEthereumService)[]

public opened: boolean
public started: boolean
Expand All @@ -44,8 +44,8 @@ export default class Node extends events.EventEmitter {
this.config = options.config

this.services = [
this.config.syncmode === 'fast'
? new FastEthereumService({
this.config.syncmode === 'full'
? new FullEthereumService({
config: this.config,
db: options.db,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { EthereumService, EthereumServiceOptions } from './ethereumservice'
import { FastSynchronizer } from '../sync/fastsync'
import { FullSynchronizer } from '../sync/fullsync'
import { EthProtocol } from '../net/protocol/ethprotocol'
import { LesProtocol } from '../net/protocol/lesprotocol'
import { Peer } from '../net/peer/peer'
import { Protocol, BoundProtocol } from '../net/protocol'

interface FastEthereumServiceOptions extends EthereumServiceOptions {
interface FullEthereumServiceOptions extends EthereumServiceOptions {
/* Serve LES requests (default: false) */
lightserv?: boolean
}
Expand All @@ -14,20 +14,20 @@ interface FastEthereumServiceOptions extends EthereumServiceOptions {
* Ethereum service
* @memberof module:service
*/
export class FastEthereumService extends EthereumService {
public synchronizer: FastSynchronizer
export class FullEthereumService extends EthereumService {
public synchronizer: FullSynchronizer
public lightserv: boolean
/**
* Create new ETH service
* @param {FastEthereumServiceOptions}
* @param {FullEthereumServiceOptions}
*/
constructor(options: FastEthereumServiceOptions) {
constructor(options: FullEthereumServiceOptions) {
super(options)

this.lightserv = options.lightserv ?? false

this.config.logger.info('Fast sync mode')
this.synchronizer = new FastSynchronizer({
this.config.logger.info('Full sync mode')
this.synchronizer = new FullSynchronizer({
config: this.config,
pool: this.pool,
chain: this.chain,
Expand Down
2 changes: 1 addition & 1 deletion lib/service/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,5 @@

export * from './service'
export * from './ethereumservice'
export * from './fastethereumservice'
export * from './fullethereumservice'
export * from './lightethereumservice'
22 changes: 15 additions & 7 deletions lib/sync/fastsync.ts → lib/sync/fullsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,35 @@ import { BoundProtocol } from '../net/protocol'
import { short } from '../util'
import { Synchronizer, SynchronizerOptions } from './sync'
import { BlockFetcher } from './fetcher'
import { Block } from '@ethereumjs/block'
import { RunBlockResult } from '@ethereumjs/vm/dist/runBlock'

/**
* Implements an ethereum fast sync synchronizer
* Implements an ethereum full sync synchronizer
* @memberof module:sync
*/
export class FastSynchronizer extends Synchronizer {
export class FullSynchronizer extends Synchronizer {
private blockFetcher: BlockFetcher | null

constructor(options: SynchronizerOptions) {
super(options)
this.blockFetcher = null
options.chain.vm.on('beforeBlock', function(block: Block) {
options.config.logger.info("Running block: " + block.header.number.toString())
})
// TODO: we don't know which block
options.chain.vm.on('afterBlock', function(blockResults: RunBlockResult){
options.config.logger.info("Succesfully ran block.")
})
options.chain.vm.runBlockchain()
}

/**
* Returns synchronizer type
* @return {string} type
*/
get type(): string {
return 'fast'
return 'full'
}

/**
Expand Down Expand Up @@ -107,19 +117,17 @@ export class FastSynchronizer extends Synchronizer {
this.pool.size
}`
)
this.chain.vm.runBlockchain()
})
await this.blockFetcher.fetch()
// TODO: Should this be deleted?
// @ts-ignore: error: The operand of a 'delete' operator must be optional
delete this.blockFetcher
return true

// TO DO: Fetch state trie as well
}

/**
* Fetch all blocks from current height up to highest found amongst peers and
* fetch entire recent state trie
* Fetch all blocks from current height up to highest found amongst peers
* @return Resolves with true if sync successful
*/
async sync(): Promise<boolean> {
Expand Down
2 changes: 1 addition & 1 deletion lib/sync/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
*/
export * from './sync'
export * from './lightsync'
export * from './fastsync'
export * from './fullsync'
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
"@ethereumjs/block": "^3.0.0-beta.2",
"@ethereumjs/blockchain": "^5.0.0-beta.2",
"@ethereumjs/common": "^2.0.0-beta.2",
"@ethereumjs/vm": "^5.0.0-beta.2",
"chalk": "^2.4.1",
"ethereumjs-devp2p": "^3.0.3",
"ethereumjs-util": "^7.0.7",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
import tape from 'tape'
import { BN } from 'ethereumjs-util'
import { Config } from '../../lib/config'
import { FastEthereumService } from '../../lib/service'
import { FullEthereumService } from '../../lib/service'
import MockServer from './mocks/mockserver'
import MockChain from './mocks/mockchain'
import { destroy } from './util'

tape('[Integration:FastEthereumService]', async (t) => {
async function setup(): Promise<[MockServer, FastEthereumService]> {
tape('[Integration:FullEthereumService]', async (t) => {
async function setup(): Promise<[MockServer, FullEthereumService]> {
const loglevel = 'error'
const config = new Config({ loglevel })
const server = new MockServer({ config })
const chain = new MockChain({ config })
const service = new FastEthereumService({
const service = new FullEthereumService({
config: new Config({ loglevel, servers: [server as any], lightserv: true }),
chain,
})
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import tape from 'tape'
import { wait, setup, destroy } from './util'

tape('[Integration:FastSync]', async (t) => {
tape('[Integration:FullSync]', async (t) => {
t.test('should sync blocks', async (t) => {
const [remoteServer, remoteService] = await setup({ location: '127.0.0.2', height: 200 })
const [localServer, localService] = await setup({ location: '127.0.0.1', height: 0 })
Expand Down
8 changes: 4 additions & 4 deletions test/integration/lightsync.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ tape.skip('[Integration:LightSync]', async (t) => {
const [remoteServer, remoteService] = await setup({
location: '127.0.0.2',
height: 200,
syncmode: 'fast',
syncmode: 'full',
})
const [localServer, localService] = await setup({
location: '127.0.0.1',
Expand All @@ -26,7 +26,7 @@ tape.skip('[Integration:LightSync]', async (t) => {
const [remoteServer, remoteService] = await setup({
location: '127.0.0.2',
height: 9,
syncmode: 'fast',
syncmode: 'full',
})
const [localServer, localService] = await setup({
location: '127.0.0.1',
Expand All @@ -48,12 +48,12 @@ tape.skip('[Integration:LightSync]', async (t) => {
const [remoteServer1, remoteService1] = await setup({
location: '127.0.0.2',
height: 9,
syncmode: 'fast',
syncmode: 'full',
})
const [remoteServer2, remoteService2] = await setup({
location: '127.0.0.3',
height: 10,
syncmode: 'fast',
syncmode: 'full',
})
const [localServer, localService] = await setup({
location: '127.0.0.1',
Expand Down
2 changes: 1 addition & 1 deletion test/integration/node.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import MockServer from './mocks/mockserver'

tape('[Integration:Node]', (t) => {
const servers = [new MockServer({ config: new Config({ loglevel: 'error' }) }) as any]
const config = new Config({ servers, syncmode: 'fast', lightserv: false, loglevel: 'error' })
const config = new Config({ servers, syncmode: 'full', lightserv: false, loglevel: 'error' })
const node = new Node({ config })

t.test('should start/stop', async (t) => {
Expand Down
Loading