diff --git a/.eslintrc.js b/.eslintrc.js index 14d4afa..b89a3a0 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,7 @@ module.exports = { }, rules: { '@typescript-eslint/no-floating-promises': 'off', + '@typescript-eslint/no-unnecessary-condition': 'off', 'no-redeclare': 'off' } } diff --git a/README.md b/README.md index 448bb7e..c23a681 100644 --- a/README.md +++ b/README.md @@ -140,17 +140,22 @@ Connect to a peer, organize the communication, see [./src/rlpx/](./src/rlpx/) ### Usage -Create your `RLPx` object, e.g.: +Instantiate an [@ethereumjs/common](https://github.com/ethereumjs/ethereumjs-vm/tree/master/packages/common) +instance with the network you want to connect to: +```typescript +const common = new Common({ chain: 'mainnet' }) ``` + +Create your `RLPx` object, e.g.: + +```typescript const rlpx = new devp2p.RLPx(PRIVATE_KEY, { dpt: dpt, maxPeers: 25, - capabilities: [ - devp2p.ETH.eth63, - devp2p.ETH.eth62 - ], - listenPort: null + capabilities: [devp2p.ETH.eth63, devp2p.ETH.eth62], + common: common, + listenPort: null, }) ``` @@ -246,7 +251,7 @@ Normally not instantiated directly but created as a `SubProtocol` in the `Peer` Send initial status message. -- `status` - Status message to send, format `{ networkId: CHAIN_ID, td: TOTAL_DIFFICULTY_BUFFER, bestHash: BEST_HASH_BUFFER, genesisHash: GENESIS_HASH_BUFFER }`. +- `status` - Status message to send, format `{td: TOTAL_DIFFICULTY_BUFFER, bestHash: BEST_HASH_BUFFER, genesisHash: GENESIS_HASH_BUFFER }`, `networkId` (respectively `chainId`) is taken from the `Common` instance #### `eth.sendMessage(code, payload)` @@ -315,7 +320,7 @@ Normally not instantiated directly but created as a `SubProtocol` in the `Peer` Send initial status message. -- `status` - Status message to send, format `{ networkId: CHAIN_ID, headTd: TOTAL_DIFFICULTY_BUFFER, headHash: HEAD_HASH_BUFFER, headNum: HEAD_NUM_BUFFER, genesisHash: GENESIS_HASH_BUFFER }`. +- `status` - Status message to send, format `{ headTd: TOTAL_DIFFICULTY_BUFFER, headHash: HEAD_HASH_BUFFER, headNum: HEAD_NUM_BUFFER, genesisHash: GENESIS_HASH_BUFFER }`, `networkId` (respectively `chainId`) is taken from the `Common` instance #### `les.sendMessage(code, reqId, payload)` diff --git a/examples/peer-communication-les.ts b/examples/peer-communication-les.ts index 12f658f..6c1e4d4 100644 --- a/examples/peer-communication-les.ts +++ b/examples/peer-communication-les.ts @@ -10,26 +10,21 @@ import { randomBytes } from 'crypto' const PRIVATE_KEY = randomBytes(32) -const CHAIN_ID = 4 // Rinkeby const GENESIS_TD = 1 const GENESIS_HASH = Buffer.from( '6341fd3daf94b748c72ced5a5b26028f2474f5f00d824504e4fa37a75767e177', 'hex' ) -const config = new Common({ chain: 'mainnet' }) -const bootstrapNodes = config.bootstrapNodes() -const BOOTNODES = bootstrapNodes - .filter((node: any) => { - return node.chainId === CHAIN_ID - }) - .map((node: any) => { - return { - address: node.ip, - udpPort: node.port, - tcpPort: node.port - } - }) +const common = new Common({ chain: 'rinkeby' }) +const bootstrapNodes = common.bootstrapNodes() +const BOOTNODES = bootstrapNodes.map((node: any) => { + return { + address: node.ip, + udpPort: node.port, + tcpPort: node.port + } +}) const REMOTE_CLIENTID_FILTER = [ 'go1.5', 'go1.6', @@ -65,6 +60,7 @@ const rlpx = new devp2p.RLPx(PRIVATE_KEY, { dpt: dpt, maxPeers: 25, capabilities: [devp2p.LES.les2], + common: common, remoteClientIdFilter: REMOTE_CLIENTID_FILTER, listenPort: null }) @@ -84,7 +80,6 @@ rlpx.on('peer:added', peer => { ) les.sendStatus({ - networkId: CHAIN_ID, headTd: devp2p.int2buffer(GENESIS_TD), headHash: GENESIS_HASH, headNum: Buffer.from([]), diff --git a/examples/peer-communication.ts b/examples/peer-communication.ts index c8f39f2..06a70ba 100644 --- a/examples/peer-communication.ts +++ b/examples/peer-communication.ts @@ -11,10 +11,9 @@ import * as devp2p from '../src/index' import { ETH, Peer } from '../src/index' const PRIVATE_KEY = randomBytes(32) -const CHAIN_ID = 1 -const config = new Common({ chain: 'mainnet' }) -const bootstrapNodes = config.bootstrapNodes() +const common = new Common({ chain: 'mainnet' }) +const bootstrapNodes = common.bootstrapNodes() const BOOTNODES = bootstrapNodes.map((node: any) => { return { address: node.ip, @@ -65,7 +64,8 @@ dpt.on('error', err => console.error(chalk.red(`DPT error: ${err}`))) const rlpx = new devp2p.RLPx(PRIVATE_KEY, { dpt: dpt, maxPeers: 25, - capabilities: [devp2p.ETH.eth63, devp2p.ETH.eth62], + capabilities: [devp2p.ETH.eth64], + common: common, remoteClientIdFilter: REMOTE_CLIENTID_FILTER, listenPort: null }) @@ -89,7 +89,6 @@ rlpx.on('peer:added', peer => { ) eth.sendStatus({ - networkId: CHAIN_ID, td: devp2p.int2buffer(17179869184), // total difficulty in genesis block bestHash: Buffer.from( 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', diff --git a/package.json b/package.json index af6d33b..9502de0 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "@types/bl": "^2.1.0", "@types/k-bucket": "^5.0.0", "@types/lru-cache": "^5.1.0", + "@ethereumjs/common": "^2.0.0-beta.2", "babel-runtime": "^6.11.6", "bl": "^1.1.2", "debug": "^2.2.0", @@ -73,7 +74,6 @@ }, "devDependencies": { "@ethereumjs/block": "^3.0.0-beta.1", - "@ethereumjs/common": "^2.0.0-beta.1", "@ethereumjs/config-coverage": "^2.0.0", "@ethereumjs/config-typescript": "^2.0.0", "@ethereumjs/eslint-config-defaults": "^2.0.0", diff --git a/src/eth/index.ts b/src/eth/index.ts index 101a471..e272807 100644 --- a/src/eth/index.ts +++ b/src/eth/index.ts @@ -1,3 +1,4 @@ +import assert from 'assert' import { EventEmitter } from 'events' import * as rlp from 'rlp' import ms from 'ms' @@ -18,6 +19,12 @@ export class ETH extends EventEmitter { _statusTimeoutId: NodeJS.Timeout _send: SendMethod + // Eth64 + _hardfork: string = 'chainstart' + _latestBlock: number = 0 + _forkHash: string = '' + _nextForkBlock: number = 0 + constructor(version: number, peer: Peer, send: SendMethod) { super() @@ -30,10 +37,24 @@ export class ETH extends EventEmitter { this._statusTimeoutId = setTimeout(() => { this._peer.disconnect(DISCONNECT_REASONS.TIMEOUT) }, ms('5s')) + + // Set forkHash and nextForkBlock + if (this._version >= 64) { + const c = this._peer._common + this._hardfork = c.hardfork() ? (c.hardfork() as string) : this._hardfork + // Set latestBlock minimally to start block of fork to have some more + // accurate basis if no latestBlock is provided along status send + this._latestBlock = c.hardforkBlock(this._hardfork) + this._forkHash = c.forkHash(this._hardfork) + // Next fork block number or 0 if none available + const nextForkBlock = c.nextHardforkBlock(this._hardfork) + this._nextForkBlock = nextForkBlock ? nextForkBlock : 0 + } } static eth62 = { name: 'eth', version: 62, length: 8, constructor: ETH } static eth63 = { name: 'eth', version: 63, length: 17, constructor: ETH } + static eth64 = { name: 'eth', version: 64, length: 29, constructor: ETH } _handleMessage(code: ETH.MESSAGE_CODES, data: any) { const payload = rlp.decode(data) as unknown @@ -80,6 +101,42 @@ export class ETH extends EventEmitter { this.emit('message', code, payload) } + /** + * Eth 64 Fork ID validation (EIP-2124) + * @param forkId Remote fork ID + */ + _validateForkId(forkId: Buffer[]) { + const c = this._peer._common + + const peerForkHash = `0x${forkId[0].toString('hex')}` + const peerNextFork = buffer2int(forkId[1]) + + if (this._forkHash === peerForkHash) { + // There is a known next fork + if (peerNextFork !== 0) { + if (this._latestBlock >= peerNextFork) { + const msg = 'Remote is advertising a future fork that passed locally' + debug(msg) + throw new assert.AssertionError({ message: msg }) + } + } + } + const peerFork: any = c.hardforkForForkHash(peerForkHash) + if (peerFork === null) { + const msg = 'Unknown fork hash' + debug(msg) + throw new assert.AssertionError({ message: msg }) + } + + if (!c.hardforkGteHardfork(peerFork.name, this._hardfork)) { + if (peerNextFork === null || c.nextHardforkBlock(peerFork.name) !== peerNextFork) { + const msg = 'Outdated fork status, remote needs software update' + debug(msg) + throw new assert.AssertionError({ message: msg }) + } + } + } + _handleStatus(): void { if (this._status === null || this._peerStatus === null) return clearTimeout(this._statusTimeoutId) @@ -88,38 +145,72 @@ export class ETH extends EventEmitter { assertEq(this._status[1], this._peerStatus[1], 'NetworkId mismatch', debug) assertEq(this._status[4], this._peerStatus[4], 'Genesis block mismatch', debug) - this.emit('status', { + const status: any = { networkId: this._peerStatus[1], td: Buffer.from(this._peerStatus[2]), bestHash: Buffer.from(this._peerStatus[3]), genesisHash: Buffer.from(this._peerStatus[4]) - }) + } + + if (this._version >= 64) { + assertEq(this._peerStatus[5].length, 2, 'Incorrect forkId msg format', debug) + this._validateForkId(this._peerStatus[5] as Buffer[]) + status['forkId'] = this._peerStatus[5] + } + + this.emit('status', status) } getVersion() { return this._version } + _forkHashFromForkId(forkId: Buffer): string { + return `0x${forkId.toString('hex')}` + } + + _nextForkFromForkId(forkId: Buffer): number { + return buffer2int(forkId) + } + _getStatusString(status: ETH.StatusMsg) { - let sStr = `[V:${buffer2int(status[0])}, NID:${buffer2int(status[1])}, TD:${buffer2int( - status[2] - )}` + let sStr = `[V:${buffer2int(status[0] as Buffer)}, NID:${buffer2int( + status[1] as Buffer + )}, TD:${buffer2int(status[2] as Buffer)}` sStr += `, BestH:${formatLogId(status[3].toString('hex'), verbose)}, GenH:${formatLogId( status[4].toString('hex'), verbose - )}]` + )}` + if (this._version >= 64) { + sStr += `, ForkHash: 0x${(status[5][0] as Buffer).toString('hex')}` + sStr += `, ForkNext: ${buffer2int(status[5][1] as Buffer)}` + } + sStr += `]` return sStr } - sendStatus(status: ETH.Status) { + sendStatus(status: ETH.StatusOpts) { if (this._status !== null) return this._status = [ int2buffer(this._version), - int2buffer(status.networkId), + int2buffer(this._peer._common.chainId()), status.td, status.bestHash, status.genesisHash ] + if (this._version >= 64) { + if (status.latestBlock) { + if (status.latestBlock < this._latestBlock) { + throw new Error( + 'latest block provided is not matching the HF setting of the Common instance (Rlpx)' + ) + } + this._latestBlock = status.latestBlock + } + const forkHashB = Buffer.from(this._forkHash.substr(2), 'hex') + const nextForkB = Buffer.from(this._nextForkBlock.toString(16), 'hex') + this._status.push([forkHashB, nextForkB]) + } debug( `Send STATUS message to ${this._peer._socket.remoteAddress}:${ @@ -171,20 +262,13 @@ export class ETH extends EventEmitter { } export namespace ETH { - export type StatusMsg = { - 0: Buffer - 1: Buffer - 2: Buffer - 3: Buffer - 4: Buffer - length: 5 - } + export interface StatusMsg extends Array {} - export type Status = { + export type StatusOpts = { version: number - networkId: number td: Buffer bestHash: Buffer + latestBlock?: number genesisHash: Buffer } diff --git a/src/les/index.ts b/src/les/index.ts index 45ae2d4..7a74b34 100644 --- a/src/les/index.ts +++ b/src/les/index.ts @@ -12,7 +12,7 @@ export const DEFAULT_ANNOUNCE_TYPE = 1 export class LES extends EventEmitter { _version: any - _peer: any + _peer: Peer _send: any _status: LES.Status | null _peerStatus: LES.Status | null @@ -140,7 +140,7 @@ export class LES extends EventEmitter { } status['announceType'] = int2buffer(status['announceType'] as number) status['protocolVersion'] = int2buffer(this._version) - status['networkId'] = int2buffer(status['networkId'] as number) + status['networkId'] = int2buffer(this._peer._common.chainId()) this._status = status @@ -209,7 +209,6 @@ export namespace LES { export interface Status { [key: string]: Buffer | number protocolVersion: Buffer - networkId: Buffer | number headTd: Buffer headHash: Buffer headNum: Buffer diff --git a/src/rlpx/peer.ts b/src/rlpx/peer.ts index 3c110d0..9a852f9 100644 --- a/src/rlpx/peer.ts +++ b/src/rlpx/peer.ts @@ -4,10 +4,11 @@ import * as util from '../util' import BufferList = require('bl') import ms from 'ms' import { debug as createDebugLogger } from 'debug' -import { int2buffer, buffer2int, formatLogData } from '../util' +import Common from '@ethereumjs/common' import { ECIES } from './ecies' -import { Socket } from 'net' import { ETH, LES } from '../' +import { int2buffer, buffer2int, formatLogData } from '../util' +import { Socket } from 'net' const debug = createDebugLogger('devp2p:rlpx:peer') const verbose = createDebugLogger('verbose').enabled @@ -77,6 +78,7 @@ export interface Hello { export class Peer extends EventEmitter { _clientId: Buffer _capabilities?: Capabilities[] + _common: Common _port: number _id: Buffer _remoteClientIdFilter: any @@ -104,6 +106,7 @@ export class Peer extends EventEmitter { // hello data this._clientId = options.clientId this._capabilities = options.capabilities + this._common = options.common this._port = options.port this._id = options.id this._remoteClientIdFilter = options.remoteClientIdFilter diff --git a/src/rlpx/rlpx.ts b/src/rlpx/rlpx.ts index 159ecf3..99d9bd2 100644 --- a/src/rlpx/rlpx.ts +++ b/src/rlpx/rlpx.ts @@ -4,7 +4,8 @@ import ms from 'ms' import { publicKeyCreate } from 'secp256k1' import { EventEmitter } from 'events' import { debug as createDebugLogger } from 'debug' -import LRUCache = require('lru-cache') +import LRUCache from 'lru-cache' +import Common from '@ethereumjs/common' // note: relative path only valid in .js file in dist const { version: pVersion } = require('../../package.json') import { pk2id, createDeferred, formatLogId } from '../util' @@ -21,6 +22,7 @@ export interface RLPxOptions { maxPeers: number remoteClientIdFilter?: string[] capabilities: Capabilities[] + common: Common listenPort: number | null } @@ -32,6 +34,7 @@ export class RLPx extends EventEmitter { _clientId: Buffer _remoteClientIdFilter?: string[] _capabilities: Capabilities[] + _common: Common _listenPort: number | null _dpt: DPT _peersLRU: LRUCache @@ -56,6 +59,7 @@ export class RLPx extends EventEmitter { this._remoteClientIdFilter = options.remoteClientIdFilter this._capabilities = options.capabilities + this._common = options.common this._listenPort = options.listenPort // DPT @@ -182,6 +186,7 @@ export class RLPx extends EventEmitter { clientId: this._clientId, remoteClientIdFilter: this._remoteClientIdFilter, capabilities: this._capabilities, + common: this._common, port: this._listenPort }) peer.on('error', err => this.emit('peer:error', peer, err)) diff --git a/test/integration/eth-simulator.ts b/test/integration/eth-simulator.ts index 2722e8a..0252576 100644 --- a/test/integration/eth-simulator.ts +++ b/test/integration/eth-simulator.ts @@ -1,8 +1,7 @@ import test from 'tape' import * as devp2p from '../../src' import * as util from './util' - -const CHAIN_ID = 1 +import Common from '@ethereumjs/common' const GENESIS_TD = 17179869184 const GENESIS_HASH = Buffer.from( @@ -13,7 +12,6 @@ const GENESIS_HASH = Buffer.from( const capabilities = [devp2p.ETH.eth63, devp2p.ETH.eth62] const status = { - networkId: CHAIN_ID, td: devp2p.int2buffer(GENESIS_TD), bestHash: GENESIS_HASH, genesisHash: GENESIS_HASH @@ -31,22 +29,23 @@ test('ETH: send status message (successful)', async t => { util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('ETH: send status message (NetworkId mismatch)', async t => { const opts: any = {} opts.status0 = Object.assign({}, status) - const status1 = Object.assign({}, status) - status1['networkId'] = 2 - opts.status1 = status1 + opts.status1 = Object.assign({}, status) opts.onPeerError0 = function(err: Error, rlpxs: any) { - const msg = 'NetworkId mismatch: 01 / 02' + const msg = 'NetworkId mismatch: 01 / 03' t.equal(err.message, msg, `should emit error: ${msg}`) util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + + const c1 = new Common({ chain: 'mainnet' }) + const c2 = new Common({ chain: 'ropsten' }) + util.twoPeerMsgExchange(t, opts, capabilities, [c1, c2]) }) test('ETH: send status message (Genesis block mismatch)', async t => { @@ -62,15 +61,15 @@ test('ETH: send status message (Genesis block mismatch)', async t => { util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) -test('ETH: send allowed eth63', async t => { +function sendWithProtocolVersion(t: test.Test, version: number, cap?: Object) { const opts: any = {} opts.status0 = Object.assign({}, status) opts.status1 = Object.assign({}, status) opts.onOnceStatus0 = function(rlpxs: any, eth: any) { - t.equal(eth.getVersion(), 63, 'should use eth63 as protocol version') + t.equal(eth.getVersion(), version, `should use eth${version} as protocol version`) eth.sendMessage(devp2p.ETH.MESSAGE_CODES.NEW_BLOCK_HASHES, [437000, 1, 0, 0]) t.pass('should send NEW_BLOCK_HASHES message') } @@ -81,26 +80,67 @@ test('ETH: send allowed eth63', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, cap) +} + +test('ETH: should use latest protocol version on default', async t => { + sendWithProtocolVersion(t, 64) }) -test('ETH: send allowed eth62', async t => { - const cap = [devp2p.ETH.eth62] +test('ETH: should work with allowed eth64', async t => { + sendWithProtocolVersion(t, 64) +}) + +test('ETH -> Eth64 -> sendStatus(): should throw on non-matching latest block provided', async t => { + const cap = [devp2p.ETH.eth64] + const common = new Common({ chain: 'mainnet', hardfork: 'byzantium' }) + const status0: any = Object.assign({}, status) + status0['latestBlock'] = 100000 // lower than Byzantium fork block 4370000 + + const rlpxs = util.initTwoPeerRLPXSetup(null, cap, common) + rlpxs[0].on('peer:added', function(peer: any) { + const protocol = peer.getProtocols()[0] + t.throws(() => { + protocol.sendStatus(status0) + }, /latest block provided is not matching the HF setting/) + util.destroyRLPXs(rlpxs) + t.end() + }) +}) + +test('ETH -> Eth64 -> ForkId validation 1a)', async t => { const opts: any = {} - opts.status0 = Object.assign({}, status) + const cap = [devp2p.ETH.eth64] + const common = new Common({ chain: 'mainnet', hardfork: 'byzantium' }) + const status0: any = Object.assign({}, status) + // Take a latest block > next mainnet fork block (constantinople) + // to trigger validation condition + status0['latestBlock'] = 9069000 + opts.status0 = status0 opts.status1 = Object.assign({}, status) - opts.onOnceStatus0 = function(rlpxs: any, eth: any) { - eth.sendMessage(devp2p.ETH.MESSAGE_CODES.NEW_BLOCK_HASHES, [437000, 1, 0, 0]) - t.pass('should send NEW_BLOCK_HASHES message') - } - opts.onOnMsg1 = function(rlpxs: any, eth: any, code: any) { - if (code === devp2p.ETH.MESSAGE_CODES.NEW_BLOCK_HASHES) { - t.pass('should receive NEW_BLOCK_HASHES message') - util.destroyRLPXs(rlpxs) - t.end() - } + opts.onPeerError0 = function(err: Error, rlpxs: any) { + const msg = 'Remote is advertising a future fork that passed locally' + t.equal(err.message, msg, `should emit error: ${msg}`) + util.destroyRLPXs(rlpxs) + t.end() } - util.twoPeerMsgExchange(t, cap, opts) + + util.twoPeerMsgExchange(t, opts, cap, common) +}) + +test('ETH: should work with allowed eth63', async t => { + const cap = [devp2p.ETH.eth63] + sendWithProtocolVersion(t, 63, cap) +}) + +test('ETH: should work with allowed eth63', async t => { + const cap = [devp2p.ETH.eth63] + sendWithProtocolVersion(t, 63, cap) +}) + +test('ETH: work with allowed eth62', async t => { + const cap = [devp2p.ETH.eth62] + sendWithProtocolVersion(t, 62, cap) }) test('ETH: send not-allowed eth62', async t => { @@ -118,7 +158,7 @@ test('ETH: send not-allowed eth62', async t => { t.end() } } - util.twoPeerMsgExchange(t, cap, opts) + util.twoPeerMsgExchange(t, opts, cap) }) test('ETH: send unknown message code', async t => { @@ -135,7 +175,7 @@ test('ETH: send unknown message code', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('ETH: invalid status send', async t => { @@ -152,5 +192,5 @@ test('ETH: invalid status send', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) diff --git a/test/integration/les-simulator.ts b/test/integration/les-simulator.ts index 4a3ce9d..8c5fce3 100644 --- a/test/integration/les-simulator.ts +++ b/test/integration/les-simulator.ts @@ -1,9 +1,8 @@ import test from 'tape' +import Common from '@ethereumjs/common' import * as devp2p from '../../src' import * as util from './util' -const CHAIN_ID = 1 - const GENESIS_TD = 17179869184 const GENESIS_HASH = Buffer.from( 'd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3', @@ -13,7 +12,6 @@ const GENESIS_HASH = Buffer.from( const capabilities = [devp2p.LES.les2] const status = { - networkId: CHAIN_ID, headTd: devp2p.int2buffer(GENESIS_TD), // total difficulty in genesis block headHash: GENESIS_HASH, headNum: devp2p.int2buffer(0), @@ -32,7 +30,7 @@ test('LES: send status message (successful)', async t => { util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('LES: send status message (modified announceType)', async t => { @@ -46,22 +44,23 @@ test('LES: send status message (modified announceType)', async t => { util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('LES: send status message (NetworkId mismatch)', async t => { const opts: any = {} opts.status0 = Object.assign({}, status) - const status1 = Object.assign({}, status) - status1['networkId'] = 2 - opts.status1 = status1 + opts.status1 = Object.assign({}, status) opts.onPeerError0 = function(err: Error, rlpxs: any) { - const msg = 'NetworkId mismatch: 01 / 02' + const msg = 'NetworkId mismatch: 01 / 03' t.equal(err.message, msg, `should emit error: ${msg}`) util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + + const c1 = new Common({ chain: 'mainnet' }) + const c2 = new Common({ chain: 'ropsten' }) + util.twoPeerMsgExchange(t, opts, capabilities, [c1, c2]) }) test('ETH: send status message (Genesis block mismatch)', async t => { @@ -77,7 +76,7 @@ test('ETH: send status message (Genesis block mismatch)', async t => { util.destroyRLPXs(rlpxs) t.end() } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('LES: send valid message', async t => { @@ -96,7 +95,7 @@ test('LES: send valid message', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('LES: send unknown message code', async t => { @@ -113,7 +112,7 @@ test('LES: send unknown message code', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) test('LES: invalid status send', async t => { @@ -130,5 +129,5 @@ test('LES: invalid status send', async t => { t.end() } } - util.twoPeerMsgExchange(t, capabilities, opts) + util.twoPeerMsgExchange(t, opts, capabilities) }) diff --git a/test/integration/rlpx-simulator.ts b/test/integration/rlpx-simulator.ts index aceb630..447cbc4 100644 --- a/test/integration/rlpx-simulator.ts +++ b/test/integration/rlpx-simulator.ts @@ -4,7 +4,7 @@ import * as util from './util' import { DISCONNECT_REASONS } from '../../src/rlpx/peer' test('RLPX: add working node', async t => { - const rlpxs = util.initTwoPeerRLPXSetup(null, null) + const rlpxs = util.initTwoPeerRLPXSetup() rlpxs[0].on('peer:added', function(peer: any) { t.equal(peer._port, 30306, 'should have added peer on peer:added after successful handshake') @@ -16,7 +16,7 @@ test('RLPX: add working node', async t => { }) test('RLPX: ban node with missing tcp port', async t => { - const rlpxs = util.initTwoPeerRLPXSetup(null, null) + const rlpxs = util.initTwoPeerRLPXSetup() rlpxs[0].on('peer:added', function() { const peer = { id: Buffer.from('abcd', 'hex'), @@ -33,7 +33,7 @@ test('RLPX: ban node with missing tcp port', async t => { }) test('RLPX: remove node', async t => { - const rlpxs = util.initTwoPeerRLPXSetup(null, null) + const rlpxs = util.initTwoPeerRLPXSetup() async.series( [ @@ -66,7 +66,7 @@ test('RLPX: remove node', async t => { }) test('RLPX: test peer queue / refill connections', async t => { - const rlpxs = util.getTestRLPXs(3, 1, null) + const rlpxs = util.getTestRLPXs(3, 1) const peer = { address: util.localhost, udpPort: util.basePort + 1, tcpPort: util.basePort + 1 } rlpxs[0]._dpt.addPeer(peer) diff --git a/test/integration/util.ts b/test/integration/util.ts index 35cbe9e..95304ed 100644 --- a/test/integration/util.ts +++ b/test/integration/util.ts @@ -1,5 +1,6 @@ import { Test } from 'tape' import { DPT, ETH, RLPx, genPrivateKey } from '../../src' +import Common from '@ethereumjs/common' export const localhost = '127.0.0.1' export const basePort = 30306 @@ -33,10 +34,18 @@ export function destroyDPTs(dpts: any) { for (const dpt of dpts) dpt.destroy() } -export function getTestRLPXs(numRLPXs: any, maxPeers: any, capabilities: any) { +export function getTestRLPXs( + numRLPXs: number, + maxPeers: number = 10, + capabilities?: any, + common?: Object | Common +) { const rlpxs = [] if (!capabilities) { - capabilities = [ETH.eth63, ETH.eth62] + capabilities = [ETH.eth64, ETH.eth63, ETH.eth62] + } + if (!common) { + common = new Common({ chain: 'mainnet' }) } const dpts = getTestDPTs(numRLPXs) @@ -45,6 +54,7 @@ export function getTestRLPXs(numRLPXs: any, maxPeers: any, capabilities: any) { dpt: dpts[i], maxPeers: maxPeers, capabilities: capabilities, + common: common.constructor === Array ? common[i] : (common as Common), listenPort: basePort + i }) rlpx.listen(basePort + i) @@ -53,8 +63,8 @@ export function getTestRLPXs(numRLPXs: any, maxPeers: any, capabilities: any) { return rlpxs } -export function initTwoPeerRLPXSetup(maxPeers: any, capabilities: any) { - const rlpxs = getTestRLPXs(2, maxPeers, capabilities) +export function initTwoPeerRLPXSetup(maxPeers?: any, capabilities?: any, common?: Object | Common) { + const rlpxs = getTestRLPXs(2, maxPeers, capabilities, common) const peer = { address: localhost, udpPort: basePort + 1, tcpPort: basePort + 1 } rlpxs[0]._dpt.addPeer(peer) return rlpxs @@ -72,8 +82,13 @@ export function initTwoPeerRLPXSetup(maxPeers: any, capabilities: any) { * @param {Function} opts.onOnMsg0 (rlpxs, protocol, code, payload) Optional handler function * @param {Function} opts.onOnMsg1 (rlpxs, protocol, code, payload) Optional handler function */ -export function twoPeerMsgExchange(t: Test, capabilities: any, opts: any) { - const rlpxs = initTwoPeerRLPXSetup(null, capabilities) +export function twoPeerMsgExchange( + t: Test, + opts: any, + capabilities?: any, + common?: Object | Common +) { + const rlpxs = initTwoPeerRLPXSetup(null, capabilities, common) rlpxs[0].on('peer:added', function(peer: any) { const protocol = peer.getProtocols()[0] protocol.sendStatus(opts.status0) // (1 ->)