├── tsconfig.eslint.json ├── browser_sync.png ├── lib ├── blockchain │ └── index.ts ├── index.ts ├── sync │ ├── index.ts │ ├── fetcher │ │ ├── index.ts │ │ ├── headerfetcher.ts │ │ └── blockfetcher.ts │ └── sync.ts ├── net │ ├── server │ │ ├── index.ts │ │ └── server.ts │ ├── peer │ │ ├── index.ts │ │ └── libp2pnode.ts │ └── protocol │ │ ├── index.ts │ │ ├── sender.ts │ │ ├── rlpxsender.ts │ │ ├── libp2psender.ts │ │ └── ethprotocol.ts ├── rpc │ ├── modules │ │ ├── index.ts │ │ ├── web3.ts │ │ ├── net.ts │ │ └── admin.ts │ ├── error-code.ts │ ├── index.ts │ └── validation.ts ├── service │ ├── index.ts │ ├── lightethereumservice.ts │ └── ethereumservice.ts ├── types.ts ├── util │ └── index.ts ├── logging.ts └── index_old.ts ├── diagram └── client.png ├── prettier.config.js ├── .nycrc ├── test ├── net │ ├── protocol │ │ ├── testdouble-timers.d.ts │ │ ├── sender.spec.ts │ │ ├── libp2psender.spec.ts │ │ ├── flowcontrol.spec.ts │ │ ├── rlpxsender.spec.ts │ │ ├── ethprotocol.spec.ts │ │ └── lesprotocol.spec.ts │ └── peer │ │ ├── libp2pnode.spec.ts │ │ └── peer.spec.ts ├── integration │ ├── lightethereumservice.spec.ts │ ├── mocks │ │ ├── mocksender.ts │ │ ├── mockchain.ts │ │ ├── mockpeer.ts │ │ ├── mockserver.ts │ │ └── network.ts │ ├── client.spec.ts │ ├── util.ts │ ├── fullsync.spec.ts │ ├── fullethereumservice.spec.ts │ ├── lightsync.spec.ts │ └── peerpool.spec.ts ├── config.spec.ts ├── rpc │ ├── blockChainStub.ts │ ├── eth │ │ ├── protocolVersion.spec.ts │ │ ├── getBlockNumber.spec.ts │ │ ├── getBlockTransactionCountByHash.spec.ts │ │ ├── getBlockByHash.spec.ts │ │ └── getBlockByNumber.spec.ts │ ├── util.ts │ ├── admin │ │ └── nodeInfo.spec.ts │ ├── net │ │ ├── peerCount.spec.ts │ │ ├── listening.spec.ts │ │ └── version.spec.ts │ ├── rpc.spec.ts │ ├── validation.spec.ts │ ├── web3 │ │ ├── clientVersion.spec.ts │ │ └── sha3.spec.ts │ └── helpers.ts ├── logging.spec.ts ├── sync │ ├── sync.spec.ts │ ├── fetcher │ │ ├── headerfetcher.spec.ts │ │ ├── fetcher.spec.ts │ │ └── blockfetcher.spec.ts │ └── lightsync.spec.ts ├── util │ ├── params.json │ └── parse.spec.ts ├── client.spec.ts └── service │ └── lightethereumservice.spec.ts ├── codecov.yml ├── .github ├── issue_template.md ├── contributing.md └── workflows │ └── build.yml ├── tsconfig.json ├── docs ├── modules │ ├── _rpc_modules_eth_.md │ ├── _rpc_modules_net_.md │ ├── _rpc_modules_web3_.md │ ├── _rpc_modules_admin_.md │ ├── _sync_fullsync_.md │ ├── _sync_lightsync_.md │ ├── _net_protocol_sender_.md │ ├── _net_peer_libp2pnode_.md │ ├── _node_.md │ ├── _config_.md │ ├── _net_protocol_rlpxsender_.md │ ├── _net_protocol_ethprotocol_.md │ ├── _net_protocol_libp2psender_.md │ ├── _net_peer_peer_.md │ ├── _service_fullethereumservice_.md │ ├── _net_peerpool_.md │ ├── _service_lightethereumservice_.md │ ├── _sync_sync_.md │ ├── _service_service_.md │ ├── _net_server_server_.md │ ├── _net_peer_rlpxpeer_.md │ ├── _sync_fetcher_fetcher_.md │ ├── _net_peer_libp2ppeer_.md │ ├── _net_server_rlpxserver_.md │ ├── _net_server_libp2pserver_.md │ ├── _net_protocol_flowcontrol_.md │ ├── _net_protocol_lesprotocol_.md │ ├── _service_ethereumservice_.md │ ├── _sync_fetcher_blockfetcher_.md │ ├── _net_protocol_boundprotocol_.md │ ├── _sync_fetcher_headerfetcher_.md │ ├── _blockchain_chain_.md │ ├── _net_protocol_protocol_.md │ ├── _logging_.md │ ├── _types_.md │ ├── _util_parse_.md │ └── _rpc_validation_.md ├── interfaces │ ├── _blockchain_chain_.genesisblockparams.md │ ├── _net_peerpool_.peerpooloptions.md │ ├── _service_service_.serviceoptions.md │ ├── _net_peer_libp2ppeer_.libp2ppeeroptions.md │ ├── _net_peer_rlpxpeer_.rlpxpeeroptions.md │ ├── _types_.bootnode.md │ ├── _net_protocol_protocol_.protocoloptions.md │ ├── _blockchain_chain_.chainblocks.md │ ├── _blockchain_chain_.chainheaders.md │ ├── _net_protocol_flowcontrol_.flowcontroloptions.md │ ├── _blockchain_chain_.chainoptions.md │ ├── _node_.nodeoptions.md │ ├── _net_protocol_boundprotocol_.boundprotocoloptions.md │ ├── _net_server_server_.serveroptions.md │ ├── _sync_sync_.synchronizeroptions.md │ ├── _net_protocol_lesprotocol_.lesprotocoloptions.md │ ├── _service_ethereumservice_.ethereumserviceoptions.md │ ├── _net_peer_peer_.peeroptions.md │ ├── _sync_fetcher_fetcher_.fetcheroptions.md │ ├── _net_server_libp2pserver_.libp2pserveroptions.md │ └── _net_server_rlpxserver_.rlpxserveroptions.md ├── classes │ ├── _net_peer_libp2pnode_.libp2pnode.md │ ├── _rpc_modules_web3_.web3.md │ ├── _rpc_modules_admin_.admin.md │ └── _rpc_modules_net_.net.md └── README.md ├── tsconfig.karma.json ├── browser ├── logging.ts ├── libp2pnode.ts └── index.ts ├── tsconfig.prod.json ├── tsconfig.browser.json ├── .eslintrc.js ├── typedoc.json ├── karma.conf.js ├── .gitignore └── webpack.config.js /tsconfig.eslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "include": [ 3 | "webpack.config.js", 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /browser_sync.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereumjs/ethereumjs-client/HEAD/browser_sync.png -------------------------------------------------------------------------------- /lib/blockchain/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module blockchain 3 | */ 4 | 5 | export * from './chain' 6 | -------------------------------------------------------------------------------- /lib/index.ts: -------------------------------------------------------------------------------- 1 | export * from './config' 2 | export { default as EthereumClient } from './client' 3 | -------------------------------------------------------------------------------- /diagram/client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ethereumjs/ethereumjs-client/HEAD/diagram/client.png -------------------------------------------------------------------------------- /prettier.config.js: -------------------------------------------------------------------------------- 1 | module.exports = require('@ethereumjs/eslint-config-defaults/prettier.config.js') 2 | -------------------------------------------------------------------------------- /.nycrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ethereumjs/config-coverage", 3 | "include": [ 4 | "lib/**/*.ts" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /test/net/protocol/testdouble-timers.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'testdouble-timers' { 2 | export function use(td: any): void 3 | } 4 | -------------------------------------------------------------------------------- /lib/sync/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module sync 3 | */ 4 | export * from './sync' 5 | export * from './lightsync' 6 | export * from './fullsync' 7 | -------------------------------------------------------------------------------- /codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | status: 3 | project: 4 | default: 5 | target: auto 6 | threshold: 2% 7 | base: auto 8 | -------------------------------------------------------------------------------- /lib/net/server/index.ts: -------------------------------------------------------------------------------- 1 | export * from './server' 2 | export * from './rlpxserver' 3 | export * from './libp2pserver' 4 | 5 | /** 6 | * @module net/server 7 | */ 8 | -------------------------------------------------------------------------------- /lib/sync/fetcher/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module sync/fetcher 3 | */ 4 | export * from './fetcher' 5 | export * from './blockfetcher' 6 | export * from './headerfetcher' 7 | -------------------------------------------------------------------------------- /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | #### Description 2 | 3 | 4 | 5 | #### Possible Implementation 6 | 7 | 8 | 9 | #### UI 10 | 11 | 12 | 13 | #### Reference Implementations / Links 14 | -------------------------------------------------------------------------------- /lib/net/peer/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module net/peer 3 | */ 4 | 5 | export * from './peer' 6 | export * from './rlpxpeer' 7 | export * from './libp2ppeer' 8 | export * from './libp2pnode' 9 | -------------------------------------------------------------------------------- /lib/rpc/modules/index.ts: -------------------------------------------------------------------------------- 1 | export const list = ['Eth', 'Web3', 'Net', 'Admin'] 2 | 3 | export * from './eth' 4 | export * from './web3' 5 | export * from './net' 6 | export * from './admin' 7 | -------------------------------------------------------------------------------- /lib/service/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module service 3 | */ 4 | 5 | export * from './service' 6 | export * from './ethereumservice' 7 | export * from './fullethereumservice' 8 | export * from './lightethereumservice' 9 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ethereumjs/config-typescript/tsconfig.json", 3 | "include": ["bin", "lib", "test"], 4 | "compilerOptions": { 5 | "typeRoots": [ 6 | "node_modules/@polkadot/ts" 7 | ] 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /docs/modules/_rpc_modules_eth_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/eth"](_rpc_modules_eth_.md) 2 | 3 | # Module: "rpc/modules/eth" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Eth](../classes/_rpc_modules_eth_.eth.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_rpc_modules_net_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/net"](_rpc_modules_net_.md) 2 | 3 | # Module: "rpc/modules/net" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Net](../classes/_rpc_modules_net_.net.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_rpc_modules_web3_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/web3"](_rpc_modules_web3_.md) 2 | 3 | # Module: "rpc/modules/web3" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Web3](../classes/_rpc_modules_web3_.web3.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_rpc_modules_admin_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/admin"](_rpc_modules_admin_.md) 2 | 3 | # Module: "rpc/modules/admin" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Admin](../classes/_rpc_modules_admin_.admin.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_sync_fullsync_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/fullsync"](_sync_fullsync_.md) 2 | 3 | # Module: "sync/fullsync" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [FullSynchronizer](../classes/_sync_fullsync_.fullsynchronizer.md) 10 | -------------------------------------------------------------------------------- /tsconfig.karma.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ethereumjs/config-typescript", 3 | "include": ["bin", "lib", "test"], 4 | "compilerOptions": { 5 | "typeRoots": [ 6 | "node_modules/@polkadot/ts" 7 | ], 8 | "skipLibCheck": true 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /docs/modules/_sync_lightsync_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/lightsync"](_sync_lightsync_.md) 2 | 3 | # Module: "sync/lightsync" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [LightSynchronizer](../classes/_sync_lightsync_.lightsynchronizer.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_sender_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/sender"](_net_protocol_sender_.md) 2 | 3 | # Module: "net/protocol/sender" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Sender](../classes/_net_protocol_sender_.sender.md) 10 | -------------------------------------------------------------------------------- /browser/logging.ts: -------------------------------------------------------------------------------- 1 | const pino = require('pino') 2 | 3 | export function getLogger(options = { loglevel: 'info' }) { 4 | return pino({ 5 | level: options.loglevel, 6 | base: null, 7 | }) 8 | } 9 | 10 | export const defaultLogger = getLogger({ loglevel: 'debug' }) 11 | -------------------------------------------------------------------------------- /docs/modules/_net_peer_libp2pnode_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/libp2pnode"](_net_peer_libp2pnode_.md) 2 | 3 | # Module: "net/peer/libp2pnode" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Libp2pNode](../classes/_net_peer_libp2pnode_.libp2pnode.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_node_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["node"](_node_.md) 2 | 3 | # Module: "node" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Node](../classes/_node_.node.md) 10 | 11 | ### Interfaces 12 | 13 | * [NodeOptions](../interfaces/_node_.nodeoptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_config_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["config"](_config_.md) 2 | 3 | # Module: "config" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Config](../classes/_config_.config.md) 10 | 11 | ### Interfaces 12 | 13 | * [ConfigOptions](../interfaces/_config_.configoptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_rlpxsender_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/rlpxsender"](_net_protocol_rlpxsender_.md) 2 | 3 | # Module: "net/protocol/rlpxsender" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [RlpxSender](../classes/_net_protocol_rlpxsender_.rlpxsender.md) 10 | -------------------------------------------------------------------------------- /tsconfig.prod.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ethereumjs/config-typescript/tsconfig.prod.json", 3 | "include": ["bin", "lib"], 4 | "compilerOptions": { 5 | "outDir": "dist", 6 | "lib": ["es2018", "dom"], 7 | "typeRoots": [ 8 | "node_modules/@polkadot/ts" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_ethprotocol_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/ethprotocol"](_net_protocol_ethprotocol_.md) 2 | 3 | # Module: "net/protocol/ethprotocol" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [EthProtocol](../classes/_net_protocol_ethprotocol_.ethprotocol.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_libp2psender_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/libp2psender"](_net_protocol_libp2psender_.md) 2 | 3 | # Module: "net/protocol/libp2psender" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Libp2pSender](../classes/_net_protocol_libp2psender_.libp2psender.md) 10 | -------------------------------------------------------------------------------- /tsconfig.browser.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@ethereumjs/config-typescript/tsconfig.browser.json", 3 | "include": ["browser/index.ts"], 4 | "exclude": ["lib/index.js"], 5 | "compilerOptions": { 6 | "outDir": "dist.browser", 7 | "typeRoots": [ 8 | "node_modules/@polkadot/ts" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/rpc/error-code.ts: -------------------------------------------------------------------------------- 1 | // Error code from JSON-RPC 2.0 spec 2 | // reference: http://www.jsonrpc.org/specification#error_object 3 | export const PARSE_ERROR = -32700 4 | export const INVALID_REQUEST = -32600 5 | export const METHOD_NOT_FOUND = -32601 6 | export const INVALID_PARAMS = -32602 7 | export const INTERNAL_ERROR = -32603 8 | -------------------------------------------------------------------------------- /docs/modules/_net_peer_peer_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/peer"](_net_peer_peer_.md) 2 | 3 | # Module: "net/peer/peer" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Peer](../classes/_net_peer_peer_.peer.md) 10 | 11 | ### Interfaces 12 | 13 | * [PeerOptions](../interfaces/_net_peer_peer_.peeroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_service_fullethereumservice_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/fullethereumservice"](_service_fullethereumservice_.md) 2 | 3 | # Module: "service/fullethereumservice" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [FullEthereumService](../classes/_service_fullethereumservice_.fullethereumservice.md) 10 | -------------------------------------------------------------------------------- /lib/net/protocol/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module net/protocol 3 | */ 4 | 5 | export * from './boundprotocol' 6 | export * from './protocol' 7 | export * from './ethprotocol' 8 | export * from './lesprotocol' 9 | export * from './sender' 10 | export * from './rlpxsender' 11 | export * from './libp2psender' 12 | export * from './flowcontrol' 13 | -------------------------------------------------------------------------------- /docs/modules/_net_peerpool_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peerpool"](_net_peerpool_.md) 2 | 3 | # Module: "net/peerpool" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [PeerPool](../classes/_net_peerpool_.peerpool.md) 10 | 11 | ### Interfaces 12 | 13 | * [PeerPoolOptions](../interfaces/_net_peerpool_.peerpooloptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_service_lightethereumservice_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/lightethereumservice"](_service_lightethereumservice_.md) 2 | 3 | # Module: "service/lightethereumservice" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [LightEthereumService](../classes/_service_lightethereumservice_.lightethereumservice.md) 10 | -------------------------------------------------------------------------------- /docs/modules/_sync_sync_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/sync"](_sync_sync_.md) 2 | 3 | # Module: "sync/sync" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Synchronizer](../classes/_sync_sync_.synchronizer.md) 10 | 11 | ### Interfaces 12 | 13 | * [SynchronizerOptions](../interfaces/_sync_sync_.synchronizeroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_service_service_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/service"](_service_service_.md) 2 | 3 | # Module: "service/service" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Service](../classes/_service_service_.service.md) 10 | 11 | ### Interfaces 12 | 13 | * [ServiceOptions](../interfaces/_service_service_.serviceoptions.md) 14 | -------------------------------------------------------------------------------- /lib/types.ts: -------------------------------------------------------------------------------- 1 | export type Key = Buffer 2 | export type KeyLike = string | Key 3 | 4 | export type Multiaddrs = string[] 5 | export type MultiaddrsLike = string | Multiaddrs 6 | 7 | export interface Bootnode { 8 | id?: string | null 9 | ip: string | null 10 | port: number 11 | } 12 | export type BootnodeLike = string | string[] | Bootnode | Bootnode[] 13 | -------------------------------------------------------------------------------- /docs/modules/_net_server_server_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/server"](_net_server_server_.md) 2 | 3 | # Module: "net/server/server" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Server](../classes/_net_server_server_.server.md) 10 | 11 | ### Interfaces 12 | 13 | * [ServerOptions](../interfaces/_net_server_server_.serveroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_peer_rlpxpeer_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/rlpxpeer"](_net_peer_rlpxpeer_.md) 2 | 3 | # Module: "net/peer/rlpxpeer" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [RlpxPeer](../classes/_net_peer_rlpxpeer_.rlpxpeer.md) 10 | 11 | ### Interfaces 12 | 13 | * [RlpxPeerOptions](../interfaces/_net_peer_rlpxpeer_.rlpxpeeroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_sync_fetcher_fetcher_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/fetcher/fetcher"](_sync_fetcher_fetcher_.md) 2 | 3 | # Module: "sync/fetcher/fetcher" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Fetcher](../classes/_sync_fetcher_fetcher_.fetcher.md) 10 | 11 | ### Interfaces 12 | 13 | * [FetcherOptions](../interfaces/_sync_fetcher_fetcher_.fetcheroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_peer_libp2ppeer_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/libp2ppeer"](_net_peer_libp2ppeer_.md) 2 | 3 | # Module: "net/peer/libp2ppeer" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Libp2pPeer](../classes/_net_peer_libp2ppeer_.libp2ppeer.md) 10 | 11 | ### Interfaces 12 | 13 | * [Libp2pPeerOptions](../interfaces/_net_peer_libp2ppeer_.libp2ppeeroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_server_rlpxserver_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/rlpxserver"](_net_server_rlpxserver_.md) 2 | 3 | # Module: "net/server/rlpxserver" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [RlpxServer](../classes/_net_server_rlpxserver_.rlpxserver.md) 10 | 11 | ### Interfaces 12 | 13 | * [RlpxServerOptions](../interfaces/_net_server_rlpxserver_.rlpxserveroptions.md) 14 | -------------------------------------------------------------------------------- /test/integration/lightethereumservice.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { setup, destroy } from './util' 3 | 4 | tape('[Integration:LightEthereumService]', async (t) => { 5 | t.test('should handle LES requests', async (t) => { 6 | const [server, service] = await setup() 7 | // TO DO: test handlers once they are implemented 8 | await destroy(server, service) 9 | t.end() 10 | }) 11 | }) 12 | -------------------------------------------------------------------------------- /docs/modules/_net_server_libp2pserver_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/libp2pserver"](_net_server_libp2pserver_.md) 2 | 3 | # Module: "net/server/libp2pserver" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Libp2pServer](../classes/_net_server_libp2pserver_.libp2pserver.md) 10 | 11 | ### Interfaces 12 | 13 | * [Libp2pServerOptions](../interfaces/_net_server_libp2pserver_.libp2pserveroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_flowcontrol_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/flowcontrol"](_net_protocol_flowcontrol_.md) 2 | 3 | # Module: "net/protocol/flowcontrol" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [FlowControl](../classes/_net_protocol_flowcontrol_.flowcontrol.md) 10 | 11 | ### Interfaces 12 | 13 | * [FlowControlOptions](../interfaces/_net_protocol_flowcontrol_.flowcontroloptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_lesprotocol_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/lesprotocol"](_net_protocol_lesprotocol_.md) 2 | 3 | # Module: "net/protocol/lesprotocol" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [LesProtocol](../classes/_net_protocol_lesprotocol_.lesprotocol.md) 10 | 11 | ### Interfaces 12 | 13 | * [LesProtocolOptions](../interfaces/_net_protocol_lesprotocol_.lesprotocoloptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_service_ethereumservice_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/ethereumservice"](_service_ethereumservice_.md) 2 | 3 | # Module: "service/ethereumservice" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [EthereumService](../classes/_service_ethereumservice_.ethereumservice.md) 10 | 11 | ### Interfaces 12 | 13 | * [EthereumServiceOptions](../interfaces/_service_ethereumservice_.ethereumserviceoptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_sync_fetcher_blockfetcher_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/fetcher/blockfetcher"](_sync_fetcher_blockfetcher_.md) 2 | 3 | # Module: "sync/fetcher/blockfetcher" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [BlockFetcher](../classes/_sync_fetcher_blockfetcher_.blockfetcher.md) 10 | 11 | ### Interfaces 12 | 13 | * [BlockFetcherOptions](../interfaces/_sync_fetcher_blockfetcher_.blockfetcheroptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_boundprotocol_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/boundprotocol"](_net_protocol_boundprotocol_.md) 2 | 3 | # Module: "net/protocol/boundprotocol" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [BoundProtocol](../classes/_net_protocol_boundprotocol_.boundprotocol.md) 10 | 11 | ### Interfaces 12 | 13 | * [BoundProtocolOptions](../interfaces/_net_protocol_boundprotocol_.boundprotocoloptions.md) 14 | -------------------------------------------------------------------------------- /docs/modules/_sync_fetcher_headerfetcher_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/fetcher/headerfetcher"](_sync_fetcher_headerfetcher_.md) 2 | 3 | # Module: "sync/fetcher/headerfetcher" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [HeaderFetcher](../classes/_sync_fetcher_headerfetcher_.headerfetcher.md) 10 | 11 | ### Interfaces 12 | 13 | * [HeaderFetcherOptions](../interfaces/_sync_fetcher_headerfetcher_.headerfetcheroptions.md) 14 | -------------------------------------------------------------------------------- /lib/util/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @module util 3 | */ 4 | import { platform } from 'os' 5 | import { version as packageVersion } from '../../package.json' 6 | 7 | export * from './parse' 8 | 9 | export function short(buffer: Buffer): string { 10 | return buffer.toString('hex').slice(0, 8) + '...' 11 | } 12 | 13 | export function getClientVersion() { 14 | const { version } = process 15 | return `EthereumJS/${packageVersion}/${platform()}/node${version.substring(1)}` 16 | } 17 | -------------------------------------------------------------------------------- /test/config.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import { Config } from '../lib/config' 3 | 4 | tape('[Config]', (t) => { 5 | t.test('Initialization with default parameters', (t) => { 6 | const config = new Config() 7 | t.equal(config.maxPeers, 25) 8 | t.end() 9 | }) 10 | 11 | t.test('Initialization with parameters passed', (t) => { 12 | const config = new Config({ maxPeers: 10 }) 13 | t.equal(config.maxPeers, 10) 14 | t.end() 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /docs/interfaces/_blockchain_chain_.genesisblockparams.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["blockchain/chain"](../modules/_blockchain_chain_.md) › [GenesisBlockParams](_blockchain_chain_.genesisblockparams.md) 2 | 3 | # Interface: GenesisBlockParams 4 | 5 | common.genesis() with all values converted to Buffer 6 | 7 | ## Hierarchy 8 | 9 | * **GenesisBlockParams** 10 | 11 | ## Indexable 12 | 13 | * \[ **key**: *string*\]: Buffer 14 | 15 | common.genesis() with all values converted to Buffer 16 | -------------------------------------------------------------------------------- /test/rpc/blockChainStub.ts: -------------------------------------------------------------------------------- 1 | export function blockChain(options: any) { 2 | const block = { 3 | toJSON: () => ({ 4 | number: options.number ?? 444444, 5 | hash: options.hash ?? '0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf', 6 | transactions: options.transactions ?? [ 7 | { hash: '0x0be3065cf288b071ccff922c1c601e2e5628d488b66e781c260ecee36054a2dc' }, 8 | ], 9 | }), 10 | } 11 | return { 12 | getBlock: async function (_data: any) { 13 | return Promise.resolve(block) 14 | }, 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/modules/_blockchain_chain_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["blockchain/chain"](_blockchain_chain_.md) 2 | 3 | # Module: "blockchain/chain" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Chain](../classes/_blockchain_chain_.chain.md) 10 | 11 | ### Interfaces 12 | 13 | * [ChainBlocks](../interfaces/_blockchain_chain_.chainblocks.md) 14 | * [ChainHeaders](../interfaces/_blockchain_chain_.chainheaders.md) 15 | * [ChainOptions](../interfaces/_blockchain_chain_.chainoptions.md) 16 | * [GenesisBlockParams](../interfaces/_blockchain_chain_.genesisblockparams.md) 17 | -------------------------------------------------------------------------------- /docs/interfaces/_net_peerpool_.peerpooloptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peerpool"](../modules/_net_peerpool_.md) › [PeerPoolOptions](_net_peerpool_.peerpooloptions.md) 2 | 3 | # Interface: PeerPoolOptions 4 | 5 | ## Hierarchy 6 | 7 | * **PeerPoolOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [config](_net_peerpool_.peerpooloptions.md#config) 14 | 15 | ## Properties 16 | 17 | ### config 18 | 19 | • **config**: *[Config](../classes/_config_.config.md)* 20 | 21 | *Defined in [lib/net/peerpool.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peerpool.ts#L8)* 22 | -------------------------------------------------------------------------------- /test/rpc/eth/protocolVersion.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { baseSetup, params, baseRequest } from '../helpers' 3 | 4 | const method = 'eth_protocolVersion' 5 | 6 | tape(`${method}: call`, (t) => { 7 | const server = baseSetup() 8 | 9 | const req = params(method, []) 10 | const expectRes = (res: any) => { 11 | const responseBlob = res.body 12 | const msg = 'protocol version should be a string' 13 | if (typeof responseBlob.result !== 'string') { 14 | throw new Error(msg) 15 | } else { 16 | t.pass(msg) 17 | } 18 | } 19 | baseRequest(t, server, req, 200, expectRes) 20 | }) 21 | -------------------------------------------------------------------------------- /test/rpc/util.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | 3 | export function checkError(t: tape.Test, expectedCode: any, expectedMessage?: any) { 4 | return (res: any) => { 5 | if (!res.body.error) { 6 | throw new Error('should return an error object') 7 | } 8 | if (res.body.error.code !== expectedCode) { 9 | throw new Error(`should have an error code ${expectedCode}`) 10 | } 11 | if (expectedMessage && res.body.error.message !== expectedMessage) { 12 | throw new Error(`should have an error message "${expectedMessage}"`) 13 | } 14 | t.pass('should return error object with error code and message') 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /test/rpc/admin/nodeInfo.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { startRPC, createManager, createNode, params, baseRequest } from '../helpers' 3 | 4 | const method = 'admin_nodeInfo' 5 | 6 | tape(method, (t) => { 7 | const manager = createManager(createNode({ opened: true })) 8 | const server = startRPC(manager.getMethods()) 9 | 10 | const req = params(method, []) 11 | 12 | const expectRes = (res: any) => { 13 | const { result } = res.body 14 | if (result) { 15 | t.pass('admin_nodeInfo returns a value') 16 | } else { 17 | throw new Error('no return value') 18 | } 19 | } 20 | baseRequest(t, server, req, 200, expectRes) 21 | }) 22 | -------------------------------------------------------------------------------- /test/net/peer/libp2pnode.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import td from 'testdouble' 3 | 4 | tape('[Libp2pNode]', async (t) => { 5 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 6 | const libp2p = td.replace('libp2p') 7 | const { Libp2pNode } = await import('../../../lib/net/peer/libp2pnode') 8 | 9 | t.test('should be a libp2p bundle', (t) => { 10 | const peerInfo = td.object('PeerInfo') 11 | const node = new Libp2pNode({ peerInfo }) 12 | t.equals(node.constructor.name, Libp2pNode.name, 'is libp2p bundle') 13 | t.end() 14 | }) 15 | 16 | t.test('should reset td', (t) => { 17 | td.reset() 18 | t.end() 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /test/net/protocol/sender.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | const { Sender } = require('../../../lib/net/protocol') 3 | 4 | tape('[Sender]', (t) => { 5 | t.test('should get/set status', (t) => { 6 | const sender = new Sender() 7 | t.deepEquals(sender.status, null, 'empty status') 8 | sender.status = { id: 1 } 9 | t.deepEquals(sender.status, { id: 1 }, 'status correct') 10 | t.end() 11 | }) 12 | 13 | t.test('should error on abstract method calls', (t) => { 14 | const sender = new Sender() 15 | t.throws(() => sender.sendStatus(), /Unimplemented/) 16 | t.throws(() => sender.sendMessage(), /Unimplemented/) 17 | t.end() 18 | }) 19 | }) 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: "@ethereumjs/eslint-config-defaults", 3 | parserOptions: { 4 | project: ['./tsconfig.json', './tsconfig.browser.json', './tsconfig.eslint.json'] 5 | }, 6 | rules: { 7 | // Many methods have been sketched in as stubs & their params trigger this. 8 | // Duplicates the (more tolerant) @typescript-eslint/no-unused-vars 9 | 'no-unused-vars': 'off' 10 | }, 11 | overrides: [ 12 | { 13 | files: ['test/**/*.ts'], 14 | rules: { 15 | 'implicit-dependencies/no-implicit': [ 16 | 'error', 17 | { peer: false, dev: true, optional: false }, 18 | ], 19 | }, 20 | }, 21 | ], 22 | } 23 | -------------------------------------------------------------------------------- /test/rpc/net/peerCount.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { startRPC, createManager, createNode, params, baseRequest } from '../helpers' 3 | 4 | const method = 'net_peerCount' 5 | 6 | tape(`${method}: call`, (t) => { 7 | const manager = createManager(createNode({ opened: true })) 8 | const server = startRPC(manager.getMethods()) 9 | 10 | const req = params(method, []) 11 | const expectRes = (res: any) => { 12 | const { result } = res.body 13 | const msg = 'result should be a hex number' 14 | if (result.substring(0, 2) !== '0x') { 15 | throw new Error(msg) 16 | } else { 17 | t.pass(msg) 18 | } 19 | } 20 | baseRequest(t, server, req, 200, expectRes) 21 | }) 22 | -------------------------------------------------------------------------------- /docs/interfaces/_service_service_.serviceoptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/service"](../modules/_service_service_.md) › [ServiceOptions](_service_service_.serviceoptions.md) 2 | 3 | # Interface: ServiceOptions 4 | 5 | ## Hierarchy 6 | 7 | * **ServiceOptions** 8 | 9 | ↳ [EthereumServiceOptions](_service_ethereumservice_.ethereumserviceoptions.md) 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [config](_service_service_.serviceoptions.md#config) 16 | 17 | ## Properties 18 | 19 | ### config 20 | 21 | • **config**: *[Config](../classes/_config_.config.md)* 22 | 23 | *Defined in [lib/service/service.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/service.ts#L9)* 24 | -------------------------------------------------------------------------------- /typedoc.json: -------------------------------------------------------------------------------- 1 | { 2 | "inputFiles": ["lib"], 3 | "out": "docs", 4 | "plugin": "typedoc-plugin-markdown", 5 | "readme": "none", 6 | "gitRevision": "master", 7 | "exclude": [ 8 | "bin/cli.ts", 9 | "lib/blockchain/index.ts", 10 | "lib/index.ts", 11 | "lib/net/server/index.ts", 12 | "lib/net/peer/index.ts", 13 | "lib/net/protocol/index.ts", 14 | "lib/rpc/index.ts", 15 | "lib/rpc/error-code.ts", 16 | "lib/rpc/modules/index.ts", 17 | "lib/service/index.ts", 18 | "lib/sync/index.ts", 19 | "lib/sync/fetcher/index.ts", 20 | "lib/util/index.ts" 21 | ], 22 | "excludeNotExported": true, 23 | "excludeExternals": true, 24 | "excludePrivate": true, 25 | "excludeProtected": true 26 | } 27 | -------------------------------------------------------------------------------- /docs/interfaces/_net_peer_libp2ppeer_.libp2ppeeroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/libp2ppeer"](../modules/_net_peer_libp2ppeer_.md) › [Libp2pPeerOptions](_net_peer_libp2ppeer_.libp2ppeeroptions.md) 2 | 3 | # Interface: Libp2pPeerOptions 4 | 5 | ## Hierarchy 6 | 7 | * object 8 | 9 | ↳ **Libp2pPeerOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [multiaddrs](_net_peer_libp2ppeer_.libp2ppeeroptions.md#optional-multiaddrs) 16 | 17 | ## Properties 18 | 19 | ### `Optional` multiaddrs 20 | 21 | • **multiaddrs**? : *[MultiaddrsLike](../modules/_types_.md#multiaddrslike)* 22 | 23 | *Defined in [lib/net/peer/libp2ppeer.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2ppeer.ts#L11)* 24 | -------------------------------------------------------------------------------- /.github/contributing.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Great that you want to contribute to the `EthereumJS` [ecosystem](https://ethereumjs.readthedocs.io/en/latest/introduction.html). `EthereumJS` is managed by the Ethereum Foundation and largely driven by the wider community. Everyone is welcome to join the effort and help to improve on the libraries (see our [Code of Conduct](https://ethereumjs.readthedocs.io/en/latest/code_of_conduct.html) 🌷). 4 | 5 | We have written up some [Contribution Guidelines](https://ethereumjs.readthedocs.io/en/latest/contributing.html#how-to-start) to help you getting started. 6 | 7 | These include information on how we work with **Git** and how our **general workflow** and **technical setup** looks like (stuff like language, tooling, code quality and style). 8 | 9 | Happy Coding! 👾 😀 💻 10 | -------------------------------------------------------------------------------- /test/rpc/eth/getBlockNumber.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { intToHex, BN } from 'ethereumjs-util' 3 | import { startRPC, createManager, createNode, params, baseRequest } from '../helpers' 4 | 5 | const method = 'eth_blockNumber' 6 | 7 | tape(`${method}: call with valid arguments`, (t) => { 8 | const mockBlockNumber = 123 9 | const mockBlockChain = { 10 | getLatestHeader: async function (): Promise { 11 | return Promise.resolve({ 12 | number: new BN(mockBlockNumber), 13 | }) 14 | }, 15 | } 16 | const manager = createManager(createNode({ blockchain: mockBlockChain })) 17 | const server = startRPC(manager.getMethods()) 18 | 19 | const req = params(method) 20 | const expectRes = (res: any) => { 21 | t.equal(res.body.result, intToHex(mockBlockNumber)) 22 | } 23 | baseRequest(t, server, req, 200, expectRes) 24 | }) 25 | -------------------------------------------------------------------------------- /docs/interfaces/_net_peer_rlpxpeer_.rlpxpeeroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/rlpxpeer"](../modules/_net_peer_rlpxpeer_.md) › [RlpxPeerOptions](_net_peer_rlpxpeer_.rlpxpeeroptions.md) 2 | 3 | # Interface: RlpxPeerOptions 4 | 5 | ## Hierarchy 6 | 7 | * object 8 | 9 | ↳ **RlpxPeerOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [host](_net_peer_rlpxpeer_.rlpxpeeroptions.md#host) 16 | * [port](_net_peer_rlpxpeer_.rlpxpeeroptions.md#port) 17 | 18 | ## Properties 19 | 20 | ### host 21 | 22 | • **host**: *string* 23 | 24 | *Defined in [lib/net/peer/rlpxpeer.ts:21](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/rlpxpeer.ts#L21)* 25 | 26 | ___ 27 | 28 | ### port 29 | 30 | • **port**: *number* 31 | 32 | *Defined in [lib/net/peer/rlpxpeer.ts:24](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/rlpxpeer.ts#L24)* 33 | -------------------------------------------------------------------------------- /docs/modules/_net_protocol_protocol_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/protocol"](_net_protocol_protocol_.md) 2 | 3 | # Module: "net/protocol/protocol" 4 | 5 | ## Index 6 | 7 | ### Classes 8 | 9 | * [Protocol](../classes/_net_protocol_protocol_.protocol.md) 10 | 11 | ### Interfaces 12 | 13 | * [ProtocolOptions](../interfaces/_net_protocol_protocol_.protocoloptions.md) 14 | 15 | ### Type aliases 16 | 17 | * [Message](_net_protocol_protocol_.md#message) 18 | 19 | ## Type aliases 20 | 21 | ### Message 22 | 23 | Ƭ **Message**: *object* 24 | 25 | *Defined in [lib/net/protocol/protocol.ts:14](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/protocol.ts#L14)* 26 | 27 | #### Type declaration: 28 | 29 | * **code**: *number* 30 | 31 | * **decode**? : *Function* 32 | 33 | * **encode**? : *Function* 34 | 35 | * **name**: *string* 36 | 37 | * **response**? : *undefined | number* 38 | -------------------------------------------------------------------------------- /docs/interfaces/_types_.bootnode.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["types"](../modules/_types_.md) › [Bootnode](_types_.bootnode.md) 2 | 3 | # Interface: Bootnode 4 | 5 | ## Hierarchy 6 | 7 | * **Bootnode** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [id](_types_.bootnode.md#optional-id) 14 | * [ip](_types_.bootnode.md#ip) 15 | * [port](_types_.bootnode.md#port) 16 | 17 | ## Properties 18 | 19 | ### `Optional` id 20 | 21 | • **id**? : *string | null* 22 | 23 | *Defined in [lib/types.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L8)* 24 | 25 | ___ 26 | 27 | ### ip 28 | 29 | • **ip**: *string | null* 30 | 31 | *Defined in [lib/types.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L9)* 32 | 33 | ___ 34 | 35 | ### port 36 | 37 | • **port**: *number* 38 | 39 | *Defined in [lib/types.ts:10](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L10)* 40 | -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | module.exports = function (config) { 2 | config.set({ 3 | frameworks: ['karma-typescript', 'tap'], 4 | 5 | files: ['lib/**/*.ts', 'test/blockchain/chain.spec.ts'], 6 | 7 | preprocessors: { 8 | '**/*.ts': ['karma-typescript'], 9 | }, 10 | 11 | reporters: ['progress'], 12 | 13 | karmaTypescriptConfig: { 14 | bundlerOptions: { 15 | entrypoints: /\.spec\.ts$/, 16 | }, 17 | tsconfig: './tsconfig.karma.json', 18 | }, 19 | 20 | browsers: ['FirefoxHeadless', 'ChromeHeadless'], 21 | 22 | colors: true, 23 | 24 | // Continuous Integration mode 25 | // if true, Karma captures browsers, runs the tests and exits 26 | singleRun: true, 27 | 28 | // Concurrency level 29 | // how many browser should be started simultaneous 30 | concurrency: 1, 31 | 32 | // Fail after timeout 33 | browserDisconnectTimeout: 100000, 34 | browserNoActivityTimeout: 100000, 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /test/integration/mocks/mocksender.ts: -------------------------------------------------------------------------------- 1 | import { Sender } from '../../../lib/net/protocol' 2 | 3 | export default class MockSender extends Sender { 4 | public protocol: string 5 | public pushable: any 6 | public receiver: any 7 | 8 | constructor(protocol: string, pushable: any, receiver: any) { 9 | super() 10 | 11 | this.protocol = protocol 12 | this.pushable = pushable 13 | this.receiver = receiver 14 | this.init() 15 | } 16 | 17 | init() { 18 | this.receiver.on('data', ([protocol, code, payload]: any[]) => { 19 | if (protocol !== this.protocol) return 20 | if (code === 0) { 21 | this.status = payload 22 | } else { 23 | this.emit('message', { code, payload }) 24 | } 25 | }) 26 | } 27 | 28 | sendStatus(status: any) { 29 | this.pushable.push([this.protocol, 0, status]) 30 | } 31 | 32 | sendMessage(code: any, data: any) { 33 | this.pushable.push([this.protocol, code, data]) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build 2 | on: 3 | push: 4 | branches: 5 | - master 6 | tags: 7 | - '*' 8 | pull_request: 9 | types: [opened, reopened, synchronize] 10 | jobs: 11 | test: 12 | runs-on: ubuntu-latest 13 | strategy: 14 | matrix: 15 | node-version: [10, 12, 13] 16 | steps: 17 | - name: Use Node.js ${{ matrix.node-version }} 18 | uses: actions/setup-node@v1 19 | with: 20 | node-version: ${{ matrix.node-version }} 21 | - uses: actions/checkout@v2 22 | - run: npm install 23 | - run: npm run lint 24 | - run: npm run coverage 25 | - uses: codecov/codecov-action@v1 26 | if: matrix.node-version == 12 27 | with: 28 | file: ./coverage/lcov.info 29 | test-browser: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/setup-node@v1 33 | with: 34 | node-version: 12.x 35 | - uses: actions/checkout@v2 36 | - run: npm install 37 | - run: npm run test:browser 38 | -------------------------------------------------------------------------------- /test/integration/mocks/mockchain.ts: -------------------------------------------------------------------------------- 1 | import { Block } from '@ethereumjs/block' 2 | import { Chain, ChainOptions } from '../../../lib/blockchain' 3 | 4 | interface MockChainOptions extends ChainOptions { 5 | height?: number 6 | } 7 | 8 | export default class MockChain extends Chain { 9 | public height: number 10 | 11 | constructor(options: MockChainOptions) { 12 | super(options) 13 | this.height = options.height ?? 10 14 | } 15 | 16 | async open() { 17 | if (this.opened) { 18 | return false 19 | } 20 | await super.open() 21 | await this.build() 22 | } 23 | 24 | async build() { 25 | const blocks: Block[] = [] 26 | for (let number = 0; number < this.height; number++) { 27 | const block = Block.fromBlockData({ 28 | header: { 29 | number: number + 1, 30 | difficulty: 1, 31 | parentHash: number ? blocks[number - 1].hash() : (this.genesis as any).hash, 32 | }, 33 | }) 34 | blocks.push(block) 35 | } 36 | await this.putBlocks(blocks) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /docs/interfaces/_net_protocol_protocol_.protocoloptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/protocol"](../modules/_net_protocol_protocol_.md) › [ProtocolOptions](_net_protocol_protocol_.protocoloptions.md) 2 | 3 | # Interface: ProtocolOptions 4 | 5 | ## Hierarchy 6 | 7 | * **ProtocolOptions** 8 | 9 | ↳ [LesProtocolOptions](_net_protocol_lesprotocol_.lesprotocoloptions.md) 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [config](_net_protocol_protocol_.protocoloptions.md#config) 16 | * [timeout](_net_protocol_protocol_.protocoloptions.md#optional-timeout) 17 | 18 | ## Properties 19 | 20 | ### config 21 | 22 | • **config**: *[Config](../classes/_config_.config.md)* 23 | 24 | *Defined in [lib/net/protocol/protocol.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/protocol.ts#L8)* 25 | 26 | ___ 27 | 28 | ### `Optional` timeout 29 | 30 | • **timeout**? : *undefined | number* 31 | 32 | *Defined in [lib/net/protocol/protocol.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/protocol.ts#L11)* 33 | -------------------------------------------------------------------------------- /test/integration/client.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { Config } from '../../lib/config' 3 | import EthereumClient from '../../lib/client' 4 | import MockServer from './mocks/mockserver' 5 | 6 | tape('[Integration:EthereumClient]', (t) => { 7 | const serverConfig = new Config({ loglevel: 'error' }) 8 | const servers = [new MockServer({ config: serverConfig }) as any] 9 | const config = new Config({ servers, syncmode: 'full', lightserv: false, loglevel: 'error' }) 10 | const node = new EthereumClient({ config }) 11 | 12 | t.test('should start/stop', async (t) => { 13 | t.plan(4) 14 | node.on('error', (err: any) => t.equal(err, 'err0', 'got error')) 15 | node.on('listening', (details: any) => { 16 | t.deepEqual(details, { transport: 'mock', url: 'mock://127.0.0.1' }, 'server listening') 17 | }) 18 | await node.open() 19 | ;(node.service('eth') as any).interval = 100 20 | node.service('eth')?.emit('error', 'err0') 21 | await node.start() 22 | t.ok((node.service('eth') as any).synchronizer.running, 'sync running') 23 | await node.stop() 24 | t.pass('node stopped') 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /test/logging.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { getLogger } from '../lib/logging' 3 | 4 | tape('[Logging]', (t) => { 5 | const logger = getLogger() 6 | 7 | t.test('should log error stacks properly', (st) => { 8 | try { 9 | throw new Error('an error') 10 | } catch (e) { 11 | e.level = 'error' 12 | // st.ok( 13 | // /an error\n {4}at/.test(logger.format.transform(e).message), 14 | // 'log message should contain stack trace (1)') 15 | st.ok( 16 | /an error\n {4}at/.test( 17 | (logger.format.transform({ level: 'error', message: e }) as any).message 18 | ), 19 | 'log message should contain stack trace (2)' 20 | ) 21 | st.end() 22 | } 23 | }) 24 | 25 | t.test('should colorize key=value pairs', (st) => { 26 | if (process.env.GITHUB_ACTION) { 27 | st.skip('no color functionality in ci') 28 | return st.end() 29 | } 30 | const { message } = logger.format.transform({ level: 'info', message: 'test key=value' }) as any 31 | st.equal(message, 'test \u001b[32mkey\u001b[39m=value ', 'key=value pairs should be colorized') 32 | st.end() 33 | }) 34 | }) 35 | -------------------------------------------------------------------------------- /lib/net/protocol/sender.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | 3 | /** 4 | * Base class for transport specific message sender/receiver. Subclasses should 5 | * emit a message event when the sender receives a new message, and they should 6 | * emit a status event when the sender receives a handshake status message 7 | * @emits message 8 | * @emits status 9 | * @memberof module:net/protocol 10 | */ 11 | export class Sender extends EventEmitter { 12 | private _status: any 13 | 14 | constructor() { 15 | super() 16 | this._status = null 17 | } 18 | 19 | get status(): any { 20 | return this._status 21 | } 22 | 23 | set status(status: any) { 24 | this._status = status 25 | this.emit('status', status) 26 | } 27 | 28 | /** 29 | * Send a status to peer 30 | * @protected 31 | * @param {Object} status 32 | */ 33 | sendStatus(_status: any) { 34 | throw new Error('Unimplemented') 35 | } 36 | 37 | /** 38 | * Send a message to peer 39 | * @protected 40 | * @param {number} code message code 41 | * @param {Array|Buffer} rlpEncodedData rlp encoded message payload 42 | */ 43 | sendMessage(_code: number, _rlpEncodedData: any[] | Buffer) { 44 | throw new Error('Unimplemented') 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/rpc/rpc.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | const request = require('supertest') 3 | import { startRPC, closeRPC } from './helpers' 4 | import { METHOD_NOT_FOUND } from '../../lib/rpc/error-code' 5 | 6 | tape('call JSON-RPC without Content-Type header', (t) => { 7 | const server = startRPC({}) 8 | 9 | const req = 'plaintext' 10 | 11 | request(server) 12 | .post('/') 13 | .send(req) 14 | .expect(415) 15 | .end((err: any) => { 16 | closeRPC(server) 17 | t.end(err) 18 | }) 19 | }) 20 | 21 | tape('call JSON RPC with non-exist method', (t) => { 22 | const server = startRPC({}) 23 | const req = { 24 | jsonrpc: '2.0', 25 | method: 'METHOD_DOES_NOT_EXIST', 26 | params: ['0x1', true], 27 | id: 1, 28 | } 29 | 30 | request(server) 31 | .post('/') 32 | .set('Content-Type', 'application/json') 33 | .send(req) 34 | .expect((res: any) => { 35 | if (!res.body.error) { 36 | throw new Error('should return an error object') 37 | } 38 | if (res.body.error.code !== METHOD_NOT_FOUND) { 39 | throw new Error(`should have an error code ${METHOD_NOT_FOUND}`) 40 | } 41 | }) 42 | .end((err: any) => { 43 | closeRPC(server) 44 | t.end(err) 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/sync/sync.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { Config } from '../../lib/config' 5 | import { Chain } from '../../lib/blockchain' 6 | 7 | tape('[Synchronizer]', async (t) => { 8 | class PeerPool extends EventEmitter { 9 | open() {} 10 | close() {} 11 | } 12 | PeerPool.prototype.open = td.func() 13 | PeerPool.prototype.close = td.func() 14 | td.replace('../../lib/net/peerpool', { PeerPool }) 15 | 16 | const { Synchronizer } = await import('../../lib/sync/sync') 17 | 18 | t.test('should sync', async (t) => { 19 | const config = new Config({ loglevel: 'error', transports: [] }) 20 | const pool = new PeerPool() as any 21 | const chain = new Chain({ config }) 22 | const sync = new Synchronizer({ config, pool, chain }) 23 | ;(sync as any).sync = td.func() 24 | td.when((sync as any).sync()).thenResolve(true) 25 | sync.on('synchronized', async () => { 26 | t.ok('synchronized') 27 | await sync.stop() 28 | t.notOk((sync as any).running, 'stopped') 29 | t.end() 30 | }) 31 | await sync.start() 32 | }) 33 | 34 | t.test('should reset td', (t) => { 35 | td.reset() 36 | t.end() 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /docs/modules/_logging_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["logging"](_logging_.md) 2 | 3 | # Module: "logging" 4 | 5 | ## Index 6 | 7 | ### Type aliases 8 | 9 | * [Logger](_logging_.md#logger) 10 | 11 | ### Variables 12 | 13 | * [defaultLogger](_logging_.md#const-defaultlogger) 14 | 15 | ### Functions 16 | 17 | * [getLogger](_logging_.md#getlogger) 18 | 19 | ## Type aliases 20 | 21 | ### Logger 22 | 23 | Ƭ **Logger**: *WinstonLogger* 24 | 25 | *Defined in [lib/logging.ts:4](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/logging.ts#L4)* 26 | 27 | ## Variables 28 | 29 | ### `Const` defaultLogger 30 | 31 | • **defaultLogger**: *Logger‹›* = getLogger({ loglevel: 'info' }) 32 | 33 | *Defined in [lib/logging.ts:56](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/logging.ts#L56)* 34 | 35 | ## Functions 36 | 37 | ### getLogger 38 | 39 | ▸ **getLogger**(`options`: object): *Logger‹›* 40 | 41 | *Defined in [lib/logging.ts:39](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/logging.ts#L39)* 42 | 43 | **Parameters:** 44 | 45 | ▪`Default value` **options**: *object*= { loglevel: 'info' } 46 | 47 | Name | Type | Default | 48 | ------ | ------ | ------ | 49 | `loglevel` | string | "info" | 50 | 51 | **Returns:** *Logger‹›* 52 | -------------------------------------------------------------------------------- /docs/interfaces/_blockchain_chain_.chainblocks.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["blockchain/chain"](../modules/_blockchain_chain_.md) › [ChainBlocks](_blockchain_chain_.chainblocks.md) 2 | 3 | # Interface: ChainBlocks 4 | 5 | Returns properties of the canonical blockchain. 6 | 7 | ## Hierarchy 8 | 9 | * **ChainBlocks** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [height](_blockchain_chain_.chainblocks.md#height) 16 | * [latest](_blockchain_chain_.chainblocks.md#latest) 17 | * [td](_blockchain_chain_.chainblocks.md#td) 18 | 19 | ## Properties 20 | 21 | ### height 22 | 23 | • **height**: *BN* 24 | 25 | *Defined in [lib/blockchain/chain.ts:45](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L45)* 26 | 27 | The height of the blockchain 28 | 29 | ___ 30 | 31 | ### latest 32 | 33 | • **latest**: *Block | null* 34 | 35 | *Defined in [lib/blockchain/chain.ts:35](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L35)* 36 | 37 | The latest block in the chain 38 | 39 | ___ 40 | 41 | ### td 42 | 43 | • **td**: *BN* 44 | 45 | *Defined in [lib/blockchain/chain.ts:40](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L40)* 46 | 47 | The total difficulty of the blockchain 48 | -------------------------------------------------------------------------------- /docs/interfaces/_blockchain_chain_.chainheaders.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["blockchain/chain"](../modules/_blockchain_chain_.md) › [ChainHeaders](_blockchain_chain_.chainheaders.md) 2 | 3 | # Interface: ChainHeaders 4 | 5 | Returns properties of the canonical headerchain. 6 | 7 | ## Hierarchy 8 | 9 | * **ChainHeaders** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [height](_blockchain_chain_.chainheaders.md#height) 16 | * [latest](_blockchain_chain_.chainheaders.md#latest) 17 | * [td](_blockchain_chain_.chainheaders.md#td) 18 | 19 | ## Properties 20 | 21 | ### height 22 | 23 | • **height**: *BN* 24 | 25 | *Defined in [lib/blockchain/chain.ts:65](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L65)* 26 | 27 | The height of the headerchain 28 | 29 | ___ 30 | 31 | ### latest 32 | 33 | • **latest**: *BlockHeader | null* 34 | 35 | *Defined in [lib/blockchain/chain.ts:55](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L55)* 36 | 37 | The latest header in the chain 38 | 39 | ___ 40 | 41 | ### td 42 | 43 | • **td**: *BN* 44 | 45 | *Defined in [lib/blockchain/chain.ts:60](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L60)* 46 | 47 | The total difficulty of the headerchain 48 | -------------------------------------------------------------------------------- /docs/interfaces/_net_protocol_flowcontrol_.flowcontroloptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/flowcontrol"](../modules/_net_protocol_flowcontrol_.md) › [FlowControlOptions](_net_protocol_flowcontrol_.flowcontroloptions.md) 2 | 3 | # Interface: FlowControlOptions 4 | 5 | ## Hierarchy 6 | 7 | * **FlowControlOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [bl](_net_protocol_flowcontrol_.flowcontroloptions.md#optional-bl) 14 | * [mrc](_net_protocol_flowcontrol_.flowcontroloptions.md#optional-mrc) 15 | * [mrr](_net_protocol_flowcontrol_.flowcontroloptions.md#optional-mrr) 16 | 17 | ## Properties 18 | 19 | ### `Optional` bl 20 | 21 | • **bl**? : *undefined | number* 22 | 23 | *Defined in [lib/net/protocol/flowcontrol.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/flowcontrol.ts#L18)* 24 | 25 | ___ 26 | 27 | ### `Optional` mrc 28 | 29 | • **mrc**? : *Mrc* 30 | 31 | *Defined in [lib/net/protocol/flowcontrol.ts:19](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/flowcontrol.ts#L19)* 32 | 33 | ___ 34 | 35 | ### `Optional` mrr 36 | 37 | • **mrr**? : *undefined | number* 38 | 39 | *Defined in [lib/net/protocol/flowcontrol.ts:20](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/flowcontrol.ts#L20)* 40 | -------------------------------------------------------------------------------- /browser/libp2pnode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Libp2p Bundle 3 | * @memberof module:net/peer 4 | */ 5 | 6 | import LibP2pWebsockets from 'libp2p-websockets' 7 | import LibP2pBootstrap from 'libp2p-bootstrap' 8 | import mplex from 'libp2p-mplex' 9 | import secio from 'libp2p-secio' 10 | 11 | const libp2p = require('libp2p') 12 | const promisify = require('util-promisify') 13 | 14 | export class Libp2pNode extends libp2p { 15 | constructor(options: any) { 16 | super({ 17 | peerInfo: options.peerInfo, 18 | modules: { 19 | transport: [LibP2pWebsockets], 20 | streamMuxer: [mplex], 21 | connEncryption: [secio], 22 | peerDiscovery: [LibP2pBootstrap], 23 | }, 24 | config: { 25 | peerDiscovery: { 26 | bootstrap: { 27 | interval: 2000, 28 | enabled: options.bootnodes !== undefined, 29 | list: options.bootnodes ?? [], 30 | }, 31 | }, 32 | EXPERIMENTAL: { 33 | dht: false, 34 | pubsub: false, 35 | }, 36 | }, 37 | }) 38 | 39 | this.asyncStart = promisify(this.start.bind(this)) 40 | this.asyncStop = promisify(this.stop.bind(this)) 41 | this.asyncDial = promisify(this.dial.bind(this)) 42 | this.asyncDialProtocol = promisify(this.dialProtocol.bind(this)) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/net/protocol/rlpxsender.ts: -------------------------------------------------------------------------------- 1 | import { Sender } from './sender' 2 | import { rlp } from 'ethereumjs-util' 3 | 4 | /** 5 | * DevP2P/RLPx protocol sender 6 | * @emits message 7 | * @emits status 8 | * @memberof module:net/protocol 9 | */ 10 | export class RlpxSender extends Sender { 11 | private sender: any 12 | /** 13 | * Creates a new DevP2P/Rlpx protocol sender 14 | * @param {Object} rlpxProtocol protocol object from ethereumjs-devp2p 15 | */ 16 | constructor(rlpxProtocol: any) { 17 | super() 18 | 19 | this.sender = rlpxProtocol 20 | this.sender.on('status', (status: any) => { 21 | this.status = status 22 | }) 23 | this.sender.on('message', (code: number, payload: any) => { 24 | this.emit('message', { code, payload }) 25 | }) 26 | } 27 | 28 | /** 29 | * Send a status to peer 30 | * @param {Object} status 31 | */ 32 | sendStatus(status: any) { 33 | try { 34 | this.sender.sendStatus(status) 35 | } catch (err) { 36 | this.emit('error', err) 37 | } 38 | } 39 | 40 | /** 41 | * Send a message to peer 42 | * @param {number} code message code 43 | * @param {*} data message payload 44 | */ 45 | sendMessage(code: number, data: any) { 46 | try { 47 | this.sender._send(code, rlp.encode(data)) 48 | } catch (err) { 49 | this.emit('error', err) 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### App ### 2 | 3 | .cachedb 4 | 5 | 6 | # Created by https://www.gitignore.io/api/osx,node 7 | 8 | ### OSX ### 9 | .DS_Store 10 | .AppleDouble 11 | .LSOverride 12 | 13 | # Icon must end with two \r 14 | Icon 15 | 16 | 17 | # Thumbnails 18 | ._* 19 | 20 | # Files that might appear in the root of a volume 21 | .DocumentRevisions-V100 22 | .fseventsd 23 | .Spotlight-V100 24 | .TemporaryItems 25 | .Trashes 26 | .VolumeIcon.icns 27 | 28 | # Directories potentially created on remote AFP share 29 | .AppleDB 30 | .AppleDesktop 31 | Network Trash Folder 32 | Temporary Items 33 | .apdisk 34 | 35 | 36 | ### Node ### 37 | # Logs 38 | logs 39 | *.log 40 | npm-debug.log* 41 | 42 | # Runtime data 43 | pids 44 | *.pid 45 | *.seed 46 | 47 | # Directory for instrumented libs generated by jscoverage/JSCover 48 | lib-cov 49 | 50 | # Coverage directory used by tools like istanbul 51 | coverage 52 | 53 | # nyc test coverage 54 | .nyc_output 55 | 56 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 57 | .grunt 58 | 59 | # node-waf configuration 60 | .lock-wscript 61 | 62 | # Compiled binary addons (http://nodejs.org/api/addons.html) 63 | build/Release 64 | 65 | # Dependency directory 66 | # https://docs.npmjs.com/misc/faq#should-i-check-my-node-modules-folder-into-git 67 | node_modules 68 | package-lock.json 69 | pnpm-lock.yaml 70 | yarn.lock 71 | 72 | # ChainDB folder 73 | chaindata 74 | 75 | # Build folders 76 | dist 77 | dist.browser -------------------------------------------------------------------------------- /lib/service/lightethereumservice.ts: -------------------------------------------------------------------------------- 1 | import { EthereumService, EthereumServiceOptions } from './ethereumservice' 2 | import { Peer } from '../net/peer/peer' 3 | import { LightSynchronizer } from '../sync/lightsync' 4 | import { LesProtocol } from '../net/protocol/lesprotocol' 5 | 6 | /** 7 | * Ethereum service 8 | * @memberof module:service 9 | */ 10 | export class LightEthereumService extends EthereumService { 11 | public synchronizer: LightSynchronizer 12 | 13 | /** 14 | * Create new ETH service 15 | * @param {Object} options constructor parameters 16 | */ 17 | constructor(options: EthereumServiceOptions) { 18 | super(options) 19 | 20 | this.config.logger.info('Light sync mode') 21 | this.synchronizer = new LightSynchronizer({ 22 | config: this.config, 23 | pool: this.pool, 24 | chain: this.chain, 25 | flow: this.flow, 26 | interval: this.interval, 27 | }) 28 | } 29 | 30 | /** 31 | * Returns all protocols required by this service 32 | */ 33 | get protocols(): LesProtocol[] { 34 | return [new LesProtocol({ config: this.config, chain: this.chain, timeout: this.timeout })] 35 | } 36 | 37 | /** 38 | * Handles incoming message from connected peer 39 | * @param {Object} message message object 40 | * @param {string} protocol protocol name 41 | * @param {Peer} peer peer 42 | * @return {Promise} 43 | */ 44 | async handle(_message: any, _protocol: string, _peer: Peer) {} 45 | } 46 | -------------------------------------------------------------------------------- /docs/interfaces/_blockchain_chain_.chainoptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["blockchain/chain"](../modules/_blockchain_chain_.md) › [ChainOptions](_blockchain_chain_.chainoptions.md) 2 | 3 | # Interface: ChainOptions 4 | 5 | The options that the Blockchain constructor can receive. 6 | 7 | ## Hierarchy 8 | 9 | * **ChainOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [blockchain](_blockchain_chain_.chainoptions.md#optional-blockchain) 16 | * [config](_blockchain_chain_.chainoptions.md#config) 17 | * [db](_blockchain_chain_.chainoptions.md#optional-db) 18 | 19 | ## Properties 20 | 21 | ### `Optional` blockchain 22 | 23 | • **blockchain**? : *Blockchain* 24 | 25 | *Defined in [lib/blockchain/chain.ts:25](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L25)* 26 | 27 | Specify a blockchain which implements the Chain interface 28 | 29 | ___ 30 | 31 | ### config 32 | 33 | • **config**: *[Config](../classes/_config_.config.md)* 34 | 35 | *Defined in [lib/blockchain/chain.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L15)* 36 | 37 | Client configuration instance 38 | 39 | ___ 40 | 41 | ### `Optional` db 42 | 43 | • **db**? : *LevelUp* 44 | 45 | *Defined in [lib/blockchain/chain.ts:20](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/blockchain/chain.ts#L20)* 46 | 47 | Database to store blocks and metadata. Should be an abstract-leveldown compliant store. 48 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const { resolve } = require('path') 2 | 3 | module.exports = { 4 | mode: 'production', 5 | entry: './dist.browser/browser/index.js', 6 | module: { 7 | rules: [ 8 | { 9 | test: /\.tsx?$/, 10 | use: 'ts-loader', 11 | exclude: /node_modules/, 12 | }, 13 | { 14 | test: /\.js$/, 15 | loader: 'file-replace-loader', 16 | options: { 17 | condition: 'always', 18 | replacement(resourcePath) { 19 | const mapping = { 20 | [resolve('./dist.browser/lib/logging.js')]: resolve( 21 | './dist.browser/browser/logging.js' 22 | ), 23 | [resolve('./dist.browser/lib/net/peer/libp2pnode.js')]: resolve( 24 | './dist.browser/browser/libp2pnode.js' 25 | ), 26 | } 27 | return mapping[resourcePath] 28 | }, 29 | async: true, 30 | }, 31 | }, 32 | ], 33 | }, 34 | resolve: { 35 | extensions: ['.tsx', '.ts', '.js'], 36 | }, 37 | output: { 38 | filename: 'bundle.js', 39 | path: resolve(__dirname, 'dist'), 40 | library: 'ethereumjs', 41 | }, 42 | node: { 43 | dgram: 'empty', // used by: rlpxpeer via ethereumjs-devp2p 44 | net: 'empty', // used by: rlpxpeer 45 | fs: 'empty', // used by: FullSynchronizer via @ethereumjs/vm 46 | }, 47 | performance: { 48 | hints: false, // suppress maxAssetSize warnings etc.. 49 | }, 50 | } 51 | -------------------------------------------------------------------------------- /test/rpc/validation.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { startRPC } from './helpers' 3 | import { middleware } from '../../lib/rpc/validation' 4 | import { baseRequest } from './helpers' 5 | import { checkError } from './util' 6 | import { INVALID_PARAMS } from '../../lib/rpc/error-code' 7 | 8 | const prefix = 'rpc/validation:' 9 | 10 | tape(`${prefix} should work without \`params\` when it's optional`, (t) => { 11 | const mockMethodName = 'mock' 12 | const server = startRPC({ 13 | [mockMethodName]: middleware( 14 | (params: any, cb: any) => { 15 | cb() 16 | }, 17 | 0, 18 | [] 19 | ), 20 | }) 21 | 22 | const req = { 23 | jsonrpc: '2.0', 24 | method: mockMethodName, 25 | id: 1, 26 | } 27 | const expectRes = (res: any) => { 28 | t.equal(res.body.error, undefined, 'should not return an error object') 29 | } 30 | baseRequest(t, server, req, 200, expectRes) 31 | }) 32 | 33 | tape(`${prefix} should return error without \`params\` when it's required`, (t) => { 34 | const mockMethodName = 'mock' 35 | const server = startRPC({ 36 | [mockMethodName]: middleware( 37 | (params: any, cb: any) => { 38 | cb() 39 | }, 40 | 1, 41 | [] 42 | ), 43 | }) 44 | 45 | const req = { 46 | jsonrpc: '2.0', 47 | method: mockMethodName, 48 | id: 1, 49 | } 50 | 51 | const expectRes = checkError(t, INVALID_PARAMS, 'missing value for required argument 0') 52 | 53 | baseRequest(t, server, req, 200, expectRes) 54 | }) 55 | -------------------------------------------------------------------------------- /test/net/protocol/libp2psender.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | const pull = require('pull-stream') 3 | // TODO: investigate pull-pair implicit dependency... 4 | // eslint-disable-next-line implicit-dependencies/no-implicit 5 | const DuplexPair = require('pull-pair/duplex') 6 | const { Libp2pSender } = require('../../../lib/net/protocol') 7 | 8 | tape('[Libp2pSender]', (t) => { 9 | t.test('should send/receive status', (t) => { 10 | const conn = DuplexPair() 11 | const sender = new Libp2pSender(conn[0]) 12 | const receiver = new Libp2pSender(conn[1]) 13 | receiver.on('status', (status: any) => { 14 | t.equal(status.id.toString('hex'), '05', 'status received') 15 | t.equal(receiver.status.id.toString('hex'), '05', 'status getter') 16 | t.end() 17 | }) 18 | sender.sendStatus({ id: 5 }) 19 | }) 20 | 21 | t.test('should send/receive message', (t) => { 22 | const conn = DuplexPair() 23 | const sender = new Libp2pSender(conn[0]) 24 | const receiver = new Libp2pSender(conn[1]) 25 | receiver.on('message', (message: any) => { 26 | t.equal(message.code, 1, 'message received (code)') 27 | t.equal(message.payload.toString('hex'), '05', 'message received (payload)') 28 | t.end() 29 | }) 30 | sender.sendMessage(1, 5) 31 | }) 32 | 33 | t.test('should catch errors', (t) => { 34 | const err0 = { source: pull.error(new Error('err0')), sink: pull.drain() } 35 | t.throws(() => new Libp2pSender(err0), /err0/, 'catch error') 36 | t.end() 37 | }) 38 | }) 39 | -------------------------------------------------------------------------------- /test/rpc/net/listening.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { startRPC, createManager, createNode, params, baseRequest } from '../helpers' 3 | 4 | const method = 'net_listening' 5 | 6 | tape(`${method}: call while listening`, (t) => { 7 | const manager = createManager(createNode({ opened: true })) 8 | const server = startRPC(manager.getMethods()) 9 | 10 | const req = params(method, []) 11 | const expectRes = (res: any) => { 12 | const { result } = res.body 13 | let msg = 'result should be a boolean' 14 | if (typeof result !== 'boolean') { 15 | throw new Error(msg) 16 | } else { 17 | t.pass(msg) 18 | } 19 | 20 | msg = 'should be listening' 21 | if (result !== true) { 22 | throw new Error(msg) 23 | } else { 24 | t.pass(msg) 25 | } 26 | } 27 | baseRequest(t, server, req, 200, expectRes) 28 | }) 29 | 30 | tape(`${method}: call while not listening`, (t) => { 31 | const manager = createManager(createNode({ opened: false })) 32 | const server = startRPC(manager.getMethods()) 33 | 34 | const req = params(method, []) 35 | const expectRes = (res: any) => { 36 | const { result } = res.body 37 | let msg = 'result should be a boolean' 38 | if (typeof result !== 'boolean') { 39 | throw new Error(msg) 40 | } else { 41 | t.pass(msg) 42 | } 43 | 44 | msg = 'should not be listening' 45 | if (result !== false) { 46 | throw new Error(msg) 47 | } else { 48 | t.pass(msg) 49 | } 50 | } 51 | baseRequest(t, server, req, 200, expectRes) 52 | }) 53 | -------------------------------------------------------------------------------- /test/net/protocol/flowcontrol.spec.ts: -------------------------------------------------------------------------------- 1 | /// 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import timers from 'testdouble-timers' 5 | import { FlowControl } from '../../../lib/net/protocol' 6 | 7 | timers.use(td) 8 | 9 | tape('[FlowControl]', (t) => { 10 | const settings = { 11 | bl: 1000, 12 | mrc: { 13 | test: { base: 100, req: 100 }, 14 | }, 15 | mrr: 10, 16 | } 17 | const peer = { id: '1', les: { status: settings } } as any 18 | const clock = (td as any).timers() 19 | 20 | t.test('should handle incoming flow control', (t) => { 21 | const expected = [700, 700, 410, 120, -170] 22 | const flow = new FlowControl(settings) 23 | let correct = 0 24 | for (let count = 0; count < 5; count++) { 25 | const bv = flow.handleRequest(peer, 'test', 2) 26 | if (bv === expected[count]) correct++ 27 | clock.tick(1) 28 | } 29 | t.equals(correct, 5, 'correct bv values') 30 | t.notOk(flow.out.get(peer.id), 'peer should be dropped') 31 | t.end() 32 | }) 33 | 34 | t.test('should handle outgoing flow control', (t) => { 35 | const expected = [9, 6, 3, 0, 0] 36 | const flow = new FlowControl() 37 | let correct = 0 38 | for (let count = 0; count < 5; count++) { 39 | flow.handleReply(peer, 1000 - count * 300) 40 | const max = flow.maxRequestCount(peer, 'test') 41 | if (max === expected[count]) correct++ 42 | clock.tick(1) 43 | } 44 | t.equals(correct, 5, 'correct max values') 45 | t.end() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /docs/modules/_types_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["types"](_types_.md) 2 | 3 | # Module: "types" 4 | 5 | ## Index 6 | 7 | ### Interfaces 8 | 9 | * [Bootnode](../interfaces/_types_.bootnode.md) 10 | 11 | ### Type aliases 12 | 13 | * [BootnodeLike](_types_.md#bootnodelike) 14 | * [Key](_types_.md#key) 15 | * [KeyLike](_types_.md#keylike) 16 | * [Multiaddrs](_types_.md#multiaddrs) 17 | * [MultiaddrsLike](_types_.md#multiaddrslike) 18 | 19 | ## Type aliases 20 | 21 | ### BootnodeLike 22 | 23 | Ƭ **BootnodeLike**: *string | string[] | [Bootnode](../interfaces/_types_.bootnode.md) | [Bootnode](../interfaces/_types_.bootnode.md)[]* 24 | 25 | *Defined in [lib/types.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L12)* 26 | 27 | ___ 28 | 29 | ### Key 30 | 31 | Ƭ **Key**: *Buffer* 32 | 33 | *Defined in [lib/types.ts:1](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L1)* 34 | 35 | ___ 36 | 37 | ### KeyLike 38 | 39 | Ƭ **KeyLike**: *string | [Key](_types_.md#key)* 40 | 41 | *Defined in [lib/types.ts:2](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L2)* 42 | 43 | ___ 44 | 45 | ### Multiaddrs 46 | 47 | Ƭ **Multiaddrs**: *string[]* 48 | 49 | *Defined in [lib/types.ts:4](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L4)* 50 | 51 | ___ 52 | 53 | ### MultiaddrsLike 54 | 55 | Ƭ **MultiaddrsLike**: *string | [Multiaddrs](_types_.md#multiaddrs)* 56 | 57 | *Defined in [lib/types.ts:5](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/types.ts#L5)* 58 | -------------------------------------------------------------------------------- /docs/interfaces/_node_.nodeoptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["node"](../modules/_node_.md) › [NodeOptions](_node_.nodeoptions.md) 2 | 3 | # Interface: NodeOptions 4 | 5 | ## Hierarchy 6 | 7 | * **NodeOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [bootnodes](_node_.nodeoptions.md#optional-bootnodes) 14 | * [clientFilter](_node_.nodeoptions.md#optional-clientfilter) 15 | * [config](_node_.nodeoptions.md#config) 16 | * [db](_node_.nodeoptions.md#optional-db) 17 | * [refreshInterval](_node_.nodeoptions.md#optional-refreshinterval) 18 | 19 | ## Properties 20 | 21 | ### `Optional` bootnodes 22 | 23 | • **bootnodes**? : *[BootnodeLike](../modules/_types_.md#bootnodelike)[]* 24 | 25 | *Defined in [lib/node.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/node.ts#L15)* 26 | 27 | ___ 28 | 29 | ### `Optional` clientFilter 30 | 31 | • **clientFilter**? : *string[]* 32 | 33 | *Defined in [lib/node.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/node.ts#L18)* 34 | 35 | ___ 36 | 37 | ### config 38 | 39 | • **config**: *[Config](../classes/_config_.config.md)* 40 | 41 | *Defined in [lib/node.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/node.ts#L9)* 42 | 43 | ___ 44 | 45 | ### `Optional` db 46 | 47 | • **db**? : *LevelUp* 48 | 49 | *Defined in [lib/node.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/node.ts#L12)* 50 | 51 | ___ 52 | 53 | ### `Optional` refreshInterval 54 | 55 | • **refreshInterval**? : *undefined | number* 56 | 57 | *Defined in [lib/node.ts:21](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/node.ts#L21)* 58 | -------------------------------------------------------------------------------- /lib/logging.ts: -------------------------------------------------------------------------------- 1 | import chalk from 'chalk' 2 | import { createLogger, format, transports, Logger as WinstonLogger } from 'winston' 3 | 4 | export type Logger = WinstonLogger 5 | 6 | const levelColors = { 7 | error: 'red', 8 | warn: 'yellow', 9 | info: 'green', 10 | debug: 'white', 11 | } 12 | 13 | const { combine, timestamp, label, printf } = format 14 | 15 | const errorFormat = format((info: any) => { 16 | if (info.message instanceof Error && info.message.stack) { 17 | info.message = info.message.stack 18 | } 19 | if (info instanceof Error && info.stack) { 20 | return Object.assign({}, info, { message: info.stack }) 21 | } 22 | return info 23 | }) 24 | 25 | function logFormat() { 26 | return printf((info: any) => { 27 | // @ts-ignore: implicitly has an 'any' TODO 28 | const color = chalk[levelColors[info.level]].bind(chalk) 29 | const level = color(info.level.toUpperCase()) 30 | const re = /(\w+)=(.+?)(?:\s|$)/g 31 | info.message = info.message.replace( 32 | re, 33 | (_: any, tag: string, char: string) => `${color(tag)}=${char} ` 34 | ) 35 | return `${level} [${info.timestamp}] ${info.message}` 36 | }) 37 | } 38 | 39 | export function getLogger(options = { loglevel: 'info' }) { 40 | const loggerOptions: any = { 41 | format: combine( 42 | errorFormat(), 43 | format.splat(), 44 | label({ label: 'ethereumjs' }), 45 | timestamp({ format: 'MM-DD|HH:mm:ss' }), 46 | logFormat() 47 | ), 48 | level: options.loglevel, 49 | silent: options.loglevel === 'off', 50 | transports: [new transports.Console()], 51 | } 52 | 53 | const logger = createLogger(loggerOptions) 54 | return logger 55 | } 56 | -------------------------------------------------------------------------------- /lib/rpc/index.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '../config' 2 | import EthereumClient from '../client' 3 | import * as modules from './modules' 4 | 5 | /** 6 | * @module rpc 7 | */ 8 | 9 | /** 10 | * get all methods. e.g., getBlockByNumber in eth module 11 | * @private 12 | * @param {Object} mod 13 | * @return {string[]} 14 | */ 15 | function getMethodNames(mod: any): string[] { 16 | return Object.getOwnPropertyNames(mod.prototype) 17 | } 18 | 19 | /** 20 | * RPC server manager 21 | * @memberof module:rpc 22 | */ 23 | export class RPCManager { 24 | private _config: Config 25 | private _client: EthereumClient 26 | 27 | constructor(client: EthereumClient, config: Config) { 28 | this._config = config 29 | this._client = client 30 | } 31 | 32 | /** 33 | * gets methods for all modules which concat with underscore "_" 34 | * e.g. convert getBlockByNumber() in eth module to { eth_getBlockByNumber } 35 | * @return {Object} methods 36 | */ 37 | getMethods(): any { 38 | const methods: any = {} 39 | 40 | modules.list.forEach((modName: string) => { 41 | this._config.logger.debug(`Initialize ${modName} module`) 42 | const mod = new (modules as any)[modName](this._client) 43 | 44 | getMethodNames((modules as any)[modName]) 45 | .filter((methodName: string) => methodName !== 'constructor') 46 | .forEach((methodName: string) => { 47 | const concatedMethodName = `${modName.toLowerCase()}_${methodName}` 48 | 49 | this._config.logger.debug(`Setup module method '${concatedMethodName}' to RPC`) 50 | methods[concatedMethodName] = mod[methodName].bind(mod) 51 | }) 52 | }) 53 | 54 | return methods 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/sync/fetcher/headerfetcher.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { Config } from '../../../lib/config' 5 | 6 | tape('[HeaderFetcher]', async (t) => { 7 | class PeerPool extends EventEmitter { 8 | idle() {} 9 | ban() {} 10 | } 11 | PeerPool.prototype.idle = td.func() 12 | PeerPool.prototype.ban = td.func() 13 | td.replace('../../lib/net/peerpool', { PeerPool }) 14 | 15 | const { HeaderFetcher } = await import('../../../lib/sync/fetcher/headerfetcher') 16 | 17 | t.test('should process', (t) => { 18 | const config = new Config({ loglevel: 'error', transports: [] }) 19 | const pool = new PeerPool() 20 | const flow = td.object() 21 | const fetcher = new HeaderFetcher({ config, pool, flow }) 22 | const headers = [{ number: 1 }, { number: 2 }] 23 | t.deepEquals( 24 | fetcher.process({ task: { count: 2 }, peer: 'peer0' }, { headers, bv: 1 }), 25 | headers, 26 | 'got results' 27 | ) 28 | t.notOk(fetcher.process({ task: { count: 2 } }, { headers: [] }), 'bad results') 29 | td.verify((fetcher as any).flow.handleReply('peer0', 1)) 30 | t.end() 31 | }) 32 | 33 | t.test('should find a fetchable peer', async (t) => { 34 | const config = new Config({ loglevel: 'error', transports: [] }) 35 | const pool = new PeerPool() 36 | const fetcher = new HeaderFetcher({ config, pool }) 37 | td.when((fetcher as any).pool.idle(td.matchers.anything())).thenReturn('peer0') 38 | t.equals(fetcher.peer(undefined), 'peer0', 'found peer') 39 | t.end() 40 | }) 41 | 42 | t.test('should reset td', (t) => { 43 | td.reset() 44 | t.end() 45 | }) 46 | }) 47 | -------------------------------------------------------------------------------- /test/util/params.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rinkeby", 3 | "chainId": 4, 4 | "networkId": 4, 5 | "genesis": { 6 | "hash": { 7 | "type": "Buffer", 8 | "data": [99, 65, 253, 61, 175, 148, 183, 72, 199, 44, 237, 90, 91, 38, 2, 143, 36, 116, 245, 240, 13, 130, 69, 4, 228, 250, 55, 167, 87, 103, 225, 119] 9 | }, 10 | "timestamp": "0x58ee40ba", 11 | "gasLimit": "0x47b760", 12 | "difficulty": "0x1", 13 | "nonce": "0x0000000000000000", 14 | "extraData": "0x52657370656374206d7920617574686f7269746168207e452e436172746d616e42eb768f2244c8811c63729a21a3569731535f067ffc57839b00206d1ad20c69a1981b489f772031b279182d99e65703f0076e4812653aab85fca0f00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", 15 | "mixHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 16 | "coinbase": "0x0000000000000000000000000000000000000000", 17 | "stateRoot": { 18 | "type": "Buffer", 19 | "data": [83, 88, 5, 132, 129, 111, 97, 114, 149, 234, 38, 192, 225, 118, 65, 224, 18, 12, 171, 47, 10, 143, 251, 83, 168, 102, 253, 83, 170, 142, 140, 45] 20 | } 21 | }, 22 | "bootstrapNodes": [], 23 | "hardforks": [{ 24 | "name": "chainstart", 25 | "block": 0 26 | }, { 27 | "name": "homestead", 28 | "block": 1 29 | }, { 30 | "name": "dao", 31 | "block": null 32 | }, { 33 | "name": "tangerineWhistle", 34 | "block": 2 35 | }, { 36 | "name": "spuriousDragon", 37 | "block": 3 38 | }, { 39 | "name": "byzantium", 40 | "block": 1035301 41 | }, { 42 | "name": "constantinople", 43 | "block": null 44 | }, { 45 | "name": "hybridCasper", 46 | "block": null 47 | }] 48 | } 49 | -------------------------------------------------------------------------------- /test/integration/util.ts: -------------------------------------------------------------------------------- 1 | import { Config } from '../../lib/config' 2 | import { FullEthereumService, LightEthereumService } from '../../lib/service' 3 | import MockServer from './mocks/mockserver' 4 | import MockChain from './mocks/mockchain' 5 | 6 | interface SetupOptions { 7 | location?: string 8 | height?: number 9 | interval?: number 10 | syncmode?: string 11 | } 12 | 13 | export async function setup( 14 | options: SetupOptions = {} 15 | ): Promise<[MockServer, FullEthereumService | LightEthereumService]> { 16 | const { location, height, interval, syncmode } = options 17 | 18 | const loglevel = 'error' 19 | const lightserv = syncmode === 'full' 20 | const config = new Config({ loglevel, syncmode, lightserv }) 21 | 22 | const server = new MockServer({ config, location }) 23 | const chain = new MockChain({ config, height }) 24 | 25 | const servers = [server] as any 26 | const serviceConfig = new Config({ loglevel, syncmode, servers, lightserv, minPeers: 1 }) 27 | const serviceOpts = { 28 | config: serviceConfig, 29 | chain, 30 | interval: interval ?? 10, 31 | } 32 | 33 | const service = 34 | syncmode === 'light' 35 | ? new LightEthereumService(serviceOpts) 36 | : new FullEthereumService({ 37 | ...serviceOpts, 38 | lightserv: true, 39 | }) 40 | await service.open() 41 | await service.start() 42 | 43 | return [server, service] 44 | } 45 | 46 | export async function destroy( 47 | server: MockServer, 48 | service: FullEthereumService | LightEthereumService 49 | ): Promise { 50 | await server.stop() 51 | await service.stop() 52 | } 53 | 54 | export async function wait(delay: number) { 55 | await new Promise((resolve) => setTimeout(resolve, delay)) 56 | } 57 | -------------------------------------------------------------------------------- /docs/interfaces/_net_protocol_boundprotocol_.boundprotocoloptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/boundprotocol"](../modules/_net_protocol_boundprotocol_.md) › [BoundProtocolOptions](_net_protocol_boundprotocol_.boundprotocoloptions.md) 2 | 3 | # Interface: BoundProtocolOptions 4 | 5 | ## Hierarchy 6 | 7 | * **BoundProtocolOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [config](_net_protocol_boundprotocol_.boundprotocoloptions.md#config) 14 | * [peer](_net_protocol_boundprotocol_.boundprotocoloptions.md#peer) 15 | * [protocol](_net_protocol_boundprotocol_.boundprotocoloptions.md#protocol) 16 | * [sender](_net_protocol_boundprotocol_.boundprotocoloptions.md#sender) 17 | 18 | ## Properties 19 | 20 | ### config 21 | 22 | • **config**: *[Config](../classes/_config_.config.md)* 23 | 24 | *Defined in [lib/net/protocol/boundprotocol.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/boundprotocol.ts#L8)* 25 | 26 | ___ 27 | 28 | ### peer 29 | 30 | • **peer**: *[Peer](../classes/_net_peer_peer_.peer.md)* 31 | 32 | *Defined in [lib/net/protocol/boundprotocol.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/boundprotocol.ts#L12)* 33 | 34 | ___ 35 | 36 | ### protocol 37 | 38 | • **protocol**: *[Protocol](../classes/_net_protocol_protocol_.protocol.md)* 39 | 40 | *Defined in [lib/net/protocol/boundprotocol.ts:10](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/boundprotocol.ts#L10)* 41 | 42 | ___ 43 | 44 | ### sender 45 | 46 | • **sender**: *[Sender](../classes/_net_protocol_sender_.sender.md)* 47 | 48 | *Defined in [lib/net/protocol/boundprotocol.ts:14](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/boundprotocol.ts#L14)* 49 | -------------------------------------------------------------------------------- /docs/interfaces/_net_server_server_.serveroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/server"](../modules/_net_server_server_.md) › [ServerOptions](_net_server_server_.serveroptions.md) 2 | 3 | # Interface: ServerOptions 4 | 5 | ## Hierarchy 6 | 7 | * **ServerOptions** 8 | 9 | ↳ [RlpxServerOptions](_net_server_rlpxserver_.rlpxserveroptions.md) 10 | 11 | ↳ [Libp2pServerOptions](_net_server_libp2pserver_.libp2pserveroptions.md) 12 | 13 | ## Index 14 | 15 | ### Properties 16 | 17 | * [bootnodes](_net_server_server_.serveroptions.md#optional-bootnodes) 18 | * [config](_net_server_server_.serveroptions.md#config) 19 | * [key](_net_server_server_.serveroptions.md#optional-key) 20 | * [refreshInterval](_net_server_server_.serveroptions.md#optional-refreshinterval) 21 | 22 | ## Properties 23 | 24 | ### `Optional` bootnodes 25 | 26 | • **bootnodes**? : *[BootnodeLike](../modules/_types_.md#bootnodelike)* 27 | 28 | *Defined in [lib/net/server/server.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L18)* 29 | 30 | ___ 31 | 32 | ### config 33 | 34 | • **config**: *[Config](../classes/_config_.config.md)* 35 | 36 | *Defined in [lib/net/server/server.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L9)* 37 | 38 | ___ 39 | 40 | ### `Optional` key 41 | 42 | • **key**? : *[KeyLike](../modules/_types_.md#keylike)* 43 | 44 | *Defined in [lib/net/server/server.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L15)* 45 | 46 | ___ 47 | 48 | ### `Optional` refreshInterval 49 | 50 | • **refreshInterval**? : *undefined | number* 51 | 52 | *Defined in [lib/net/server/server.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L12)* 53 | -------------------------------------------------------------------------------- /docs/interfaces/_sync_sync_.synchronizeroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/sync"](../modules/_sync_sync_.md) › [SynchronizerOptions](_sync_sync_.synchronizeroptions.md) 2 | 3 | # Interface: SynchronizerOptions 4 | 5 | ## Hierarchy 6 | 7 | * **SynchronizerOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [chain](_sync_sync_.synchronizeroptions.md#chain) 14 | * [config](_sync_sync_.synchronizeroptions.md#config) 15 | * [flow](_sync_sync_.synchronizeroptions.md#optional-flow) 16 | * [interval](_sync_sync_.synchronizeroptions.md#optional-interval) 17 | * [pool](_sync_sync_.synchronizeroptions.md#pool) 18 | 19 | ## Properties 20 | 21 | ### chain 22 | 23 | • **chain**: *[Chain](../classes/_blockchain_chain_.chain.md)* 24 | 25 | *Defined in [lib/sync/sync.ts:16](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/sync.ts#L16)* 26 | 27 | ___ 28 | 29 | ### config 30 | 31 | • **config**: *[Config](../classes/_config_.config.md)* 32 | 33 | *Defined in [lib/sync/sync.ts:10](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/sync.ts#L10)* 34 | 35 | ___ 36 | 37 | ### `Optional` flow 38 | 39 | • **flow**? : *[FlowControl](../classes/_net_protocol_flowcontrol_.flowcontrol.md)* 40 | 41 | *Defined in [lib/sync/sync.ts:19](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/sync.ts#L19)* 42 | 43 | ___ 44 | 45 | ### `Optional` interval 46 | 47 | • **interval**? : *undefined | number* 48 | 49 | *Defined in [lib/sync/sync.ts:22](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/sync.ts#L22)* 50 | 51 | ___ 52 | 53 | ### pool 54 | 55 | • **pool**: *[PeerPool](../classes/_net_peerpool_.peerpool.md)* 56 | 57 | *Defined in [lib/sync/sync.ts:13](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/sync.ts#L13)* 58 | -------------------------------------------------------------------------------- /test/rpc/web3/clientVersion.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { platform } from 'os' 3 | import { baseSetup, params, baseRequest } from '../helpers' 4 | 5 | const method = 'web3_clientVersion' 6 | 7 | tape(`${method}: call`, (t) => { 8 | const server = baseSetup() 9 | 10 | const req = params(method, []) 11 | const expectRes = (res: any) => { 12 | const { result } = res.body 13 | const { version } = require('../../../package.json') 14 | const expectedClientTitle = 'EthereumJS' 15 | const expectedPackageVersion = version 16 | const expectedPlatform = platform() 17 | const expectedNodeVersion = `node${process.version.substring(1)}` 18 | 19 | let msg = 'result string should not be empty' 20 | if (result.length === 0) { 21 | throw new Error(msg) 22 | } else { 23 | t.pass(msg) 24 | } 25 | 26 | const [ 27 | actualClientTitle, 28 | actualPackageVersion, 29 | actualPlatform, 30 | actualNodeVersion, 31 | ] = result.split('/') 32 | 33 | msg = 'client title should be correct' 34 | if (actualClientTitle !== expectedClientTitle) { 35 | throw new Error(msg) 36 | } else { 37 | t.pass(msg) 38 | } 39 | msg = 'package version should be correct' 40 | if (actualPackageVersion !== expectedPackageVersion) { 41 | throw new Error(msg) 42 | } else { 43 | t.pass(msg) 44 | } 45 | msg = 'platform should be correct' 46 | if (actualPlatform !== expectedPlatform) { 47 | throw new Error(msg) 48 | } else { 49 | t.pass(msg) 50 | } 51 | msg = 'Node.js version should be correct' 52 | if (actualNodeVersion !== expectedNodeVersion) { 53 | throw new Error(msg) 54 | } else { 55 | t.pass(msg) 56 | } 57 | } 58 | baseRequest(t, server, req, 200, expectRes) 59 | }) 60 | -------------------------------------------------------------------------------- /lib/rpc/modules/web3.ts: -------------------------------------------------------------------------------- 1 | import { middleware, validators } from '../validation' 2 | import { addHexPrefix, keccak, toBuffer } from 'ethereumjs-util' 3 | import { getClientVersion } from '../../util' 4 | 5 | /** 6 | * web3_* RPC module 7 | * @memberof module:rpc/modules 8 | */ 9 | export class Web3 { 10 | private _chain: any 11 | 12 | /** 13 | * Create web3_* RPC module 14 | * @param {Node} Node to which the module binds 15 | */ 16 | constructor(node: any) { 17 | const service = node.services.find((s: any) => s.name === 'eth') 18 | this._chain = service.chain 19 | 20 | this.clientVersion = middleware(this.clientVersion.bind(this), 0, []) 21 | 22 | this.sha3 = middleware(this.sha3.bind(this), 1, [[validators.hex]]) 23 | } 24 | 25 | /** 26 | * Returns the current client version 27 | * @param {Array<*>} [params] An empty array 28 | * @param {Function} [cb] A function with an error object as the first argument and the 29 | * client version as the second argument 30 | */ 31 | clientVersion(_params = [], cb: (err: null, version: string) => void) { 32 | const ethJsVersion = getClientVersion() 33 | cb(null, ethJsVersion) 34 | } 35 | 36 | /** 37 | * Returns Keccak-256 (not the standardized SHA3-256) of the given data 38 | * @param {Array} [params] The data to convert into a SHA3 hash 39 | * @param {Function} [cb] A function with an error object as the first argument and the 40 | * Keccak-256 hash of the given data as the second argument 41 | */ 42 | sha3(params: string[], cb: (err: Error | null, hash?: string) => void) { 43 | try { 44 | const rawDigest = keccak(toBuffer(params[0])) 45 | const hexEncodedDigest = addHexPrefix(rawDigest.toString('hex')) 46 | cb(null, hexEncodedDigest) 47 | } catch (err) { 48 | cb(err) 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /lib/net/peer/libp2pnode.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Libp2p Bundle 3 | * @memberof module:net/peer 4 | */ 5 | const LibP2pTcp = require('libp2p-tcp') 6 | const LibP2pWebsockets = require('libp2p-websockets') 7 | const LibP2pBootstrap = require('libp2p-bootstrap') 8 | const LibP2pKadDht = require('libp2p-kad-dht') 9 | const mplex = require('libp2p-mplex') 10 | const secio = require('libp2p-secio') 11 | 12 | // TODO: Import errors with "class extends value undefined is not a constructor or null" 13 | // but LibP2p *is* default export and has a constructor 14 | const LibP2p = require('libp2p') 15 | const promisify = require('util-promisify') 16 | 17 | export class Libp2pNode extends LibP2p { 18 | public asyncStart: Function 19 | public asyncStop: Function 20 | public asyncDial: Function 21 | public asyncDialProtocol: Function 22 | 23 | constructor(options: any) { 24 | super({ 25 | peerInfo: options.peerInfo, 26 | modules: { 27 | transport: [LibP2pTcp, LibP2pWebsockets], 28 | streamMuxer: [mplex], 29 | connEncryption: [secio], 30 | peerDiscovery: [LibP2pBootstrap], 31 | dht: LibP2pKadDht, 32 | }, 33 | config: { 34 | peerDiscovery: { 35 | bootstrap: { 36 | interval: 2000, 37 | enabled: options.bootnodes !== undefined, 38 | list: options.bootnodes ?? [], 39 | }, 40 | }, 41 | dht: { 42 | kBucketSize: 20, 43 | }, 44 | EXPERIMENTAL: { 45 | dht: false, 46 | pubsub: false, 47 | }, 48 | }, 49 | }) 50 | 51 | this.asyncStart = promisify(this.start.bind(this)) 52 | this.asyncStop = promisify(this.stop.bind(this)) 53 | this.asyncDial = promisify(this.dial.bind(this)) 54 | this.asyncDialProtocol = promisify(this.dialProtocol.bind(this)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /test/rpc/eth/getBlockTransactionCountByHash.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { INVALID_PARAMS } from '../../../lib/rpc/error-code' 3 | import { baseSetup, params, baseRequest } from '../helpers' 4 | import { checkError } from '../util' 5 | 6 | const method = 'eth_getBlockTransactionCountByHash' 7 | 8 | tape(`${method}: call with valid arguments`, (t) => { 9 | const server = baseSetup() 10 | 11 | const req = params(method, ['0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf']) 12 | const expectRes = (res: any) => { 13 | const msg = 'transaction count should be 1' 14 | if (res.body.result !== `0x1`) { 15 | throw new Error(msg) 16 | } else { 17 | t.pass(msg) 18 | } 19 | } 20 | baseRequest(t, server, req, 200, expectRes) 21 | }) 22 | 23 | tape(`${method}: call with invalid block hash without 0x`, (t) => { 24 | const server = baseSetup() 25 | 26 | const req = params(method, ['WRONG BLOCK NUMBER']) 27 | const expectRes = checkError( 28 | t, 29 | INVALID_PARAMS, 30 | 'invalid argument 0: hex string without 0x prefix' 31 | ) 32 | baseRequest(t, server, req, 200, expectRes) 33 | }) 34 | 35 | tape(`${method}: call with invalid hex string as block hash`, (t) => { 36 | const server = baseSetup() 37 | 38 | const req = params(method, ['0xWRONG BLOCK NUMBER', true]) 39 | const expectRes = checkError(t, INVALID_PARAMS, 'invalid argument 0: invalid block hash') 40 | baseRequest(t, server, req, 200, expectRes) 41 | }) 42 | 43 | tape(`${method}: call without first parameter`, (t) => { 44 | const server = baseSetup() 45 | 46 | const req = params(method, []) 47 | const expectRes = checkError(t, INVALID_PARAMS, 'missing value for required argument 0') 48 | baseRequest(t, server, req, 200, expectRes) 49 | }) 50 | 51 | tape(`${method}: call with invalid second parameter`, (t) => { 52 | const server = baseSetup() 53 | 54 | const req = params(method, ['INVALID PARAMETER']) 55 | const expectRes = checkError(t, INVALID_PARAMS) 56 | baseRequest(t, server, req, 200, expectRes) 57 | }) 58 | -------------------------------------------------------------------------------- /test/net/protocol/rlpxsender.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import * as rlp from 'rlp' 5 | import { RlpxSender } from '../../../lib/net/protocol' 6 | 7 | tape('[RlpxSender]', (t) => { 8 | t.test('should send status', (t) => { 9 | const rlpxProtocol = td.object() as any 10 | const status = { id: 5 } 11 | const sender = new RlpxSender(rlpxProtocol) 12 | sender.sendStatus(status) 13 | td.verify(rlpxProtocol.sendStatus(status)) 14 | td.reset() 15 | t.pass('status sent') 16 | t.end() 17 | }) 18 | 19 | t.test('should send message', (t) => { 20 | const rlpxProtocol = td.object() as any 21 | const sender = new RlpxSender(rlpxProtocol) 22 | sender.sendMessage(1, 5) 23 | td.verify(rlpxProtocol._send(1, rlp.encode(5))) 24 | td.reset() 25 | t.pass('message sent') 26 | t.end() 27 | }) 28 | 29 | t.test('should receive status', (t) => { 30 | const rlpxProtocol = new EventEmitter() 31 | const sender = new RlpxSender(rlpxProtocol) 32 | sender.on('status', (status: any) => { 33 | t.equal(status.id, 5, 'status received') 34 | t.equal(sender.status.id, 5, 'status getter') 35 | t.end() 36 | }) 37 | rlpxProtocol.emit('status', { id: 5 }) 38 | }) 39 | 40 | t.test('should receive message', (t) => { 41 | const rlpxProtocol = new EventEmitter() 42 | const sender = new RlpxSender(rlpxProtocol) 43 | sender.on('message', (message: any) => { 44 | t.equal(message.code, 1, 'message received (code)') 45 | t.equal(message.payload, 5, 'message received (payload)') 46 | t.end() 47 | }) 48 | rlpxProtocol.emit('message', 1, 5) 49 | }) 50 | 51 | t.test('should catch errors', (t) => { 52 | const rlpxProtocol = new EventEmitter() 53 | const sender = new RlpxSender(rlpxProtocol) 54 | t.throws(() => sender.sendStatus({ id: 5 }), /not a function/, 'sendStatus error') 55 | t.throws(() => sender.sendMessage(1, 5), /not a function/, 'sendMessage error') 56 | t.end() 57 | }) 58 | }) 59 | -------------------------------------------------------------------------------- /docs/classes/_net_peer_libp2pnode_.libp2pnode.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/libp2pnode"](../modules/_net_peer_libp2pnode_.md) › [Libp2pNode](_net_peer_libp2pnode_.libp2pnode.md) 2 | 3 | # Class: Libp2pNode 4 | 5 | ## Hierarchy 6 | 7 | * any 8 | 9 | ↳ **Libp2pNode** 10 | 11 | ## Index 12 | 13 | ### Constructors 14 | 15 | * [constructor](_net_peer_libp2pnode_.libp2pnode.md#constructor) 16 | 17 | ### Properties 18 | 19 | * [asyncDial](_net_peer_libp2pnode_.libp2pnode.md#asyncdial) 20 | * [asyncDialProtocol](_net_peer_libp2pnode_.libp2pnode.md#asyncdialprotocol) 21 | * [asyncStart](_net_peer_libp2pnode_.libp2pnode.md#asyncstart) 22 | * [asyncStop](_net_peer_libp2pnode_.libp2pnode.md#asyncstop) 23 | 24 | ## Constructors 25 | 26 | ### constructor 27 | 28 | \+ **new Libp2pNode**(`options`: any): *[Libp2pNode](_net_peer_libp2pnode_.libp2pnode.md)* 29 | 30 | *Defined in [lib/net/peer/libp2pnode.ts:21](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2pnode.ts#L21)* 31 | 32 | **Parameters:** 33 | 34 | Name | Type | 35 | ------ | ------ | 36 | `options` | any | 37 | 38 | **Returns:** *[Libp2pNode](_net_peer_libp2pnode_.libp2pnode.md)* 39 | 40 | ## Properties 41 | 42 | ### asyncDial 43 | 44 | • **asyncDial**: *Function* 45 | 46 | *Defined in [lib/net/peer/libp2pnode.ts:20](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2pnode.ts#L20)* 47 | 48 | ___ 49 | 50 | ### asyncDialProtocol 51 | 52 | • **asyncDialProtocol**: *Function* 53 | 54 | *Defined in [lib/net/peer/libp2pnode.ts:21](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2pnode.ts#L21)* 55 | 56 | ___ 57 | 58 | ### asyncStart 59 | 60 | • **asyncStart**: *Function* 61 | 62 | *Defined in [lib/net/peer/libp2pnode.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2pnode.ts#L18)* 63 | 64 | ___ 65 | 66 | ### asyncStop 67 | 68 | • **asyncStop**: *Function* 69 | 70 | *Defined in [lib/net/peer/libp2pnode.ts:19](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/libp2pnode.ts#L19)* 71 | -------------------------------------------------------------------------------- /test/integration/mocks/mockpeer.ts: -------------------------------------------------------------------------------- 1 | import { Peer, PeerOptions } from '../../../lib/net/peer' 2 | import MockServer from './mockserver' 3 | import MockSender from './mocksender' 4 | import * as network from './network' 5 | import { EventEmitter } from 'events' 6 | 7 | // TODO: polkadot/ts types seem wrong (?) 8 | // "pull_pushable_1.default is not a function" 9 | const Pushable = require('pull-pushable') 10 | const pull = require('pull-stream') 11 | 12 | interface MockPeerOptions extends PeerOptions { 13 | location: string 14 | } 15 | 16 | export default class MockPeer extends Peer { 17 | public location: string 18 | public connected: boolean 19 | 20 | constructor(options: MockPeerOptions) { 21 | super({ ...options, transport: 'mock', address: options.location }) 22 | this.location = options.location 23 | this.connected = false 24 | } 25 | 26 | async connect() { 27 | if (this.connected) { 28 | return 29 | } 30 | await this.createConnection(this.location) 31 | this.emit('connected') 32 | } 33 | 34 | async accept(server: MockServer) { 35 | if (this.connected) { 36 | return 37 | } 38 | await this.createConnection(server.location) 39 | this.server = server 40 | this.inbound = true 41 | } 42 | 43 | async createConnection(location: string) { 44 | const protocols = this.protocols.map((p) => `${p.name}/${p.versions[0]}`) 45 | const connection = network.createConnection(this.id, location, protocols) 46 | await this.bindProtocols(connection) 47 | } 48 | 49 | async bindProtocols(connection: any) { 50 | const receiver = new EventEmitter() 51 | const pushable = new Pushable() 52 | pull(pushable, connection) 53 | pull( 54 | connection, 55 | pull.drain((data: any) => receiver.emit('data', data)) 56 | ) 57 | await Promise.all( 58 | this.protocols.map(async (p) => { 59 | if (!connection.protocols.includes(`${p.name}/${p.versions[0]}`)) return 60 | await p.open() 61 | await this.bindProtocol(p, new MockSender(p.name, pushable, receiver)) 62 | }) 63 | ) 64 | this.connected = true 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /docs/interfaces/_net_protocol_lesprotocol_.lesprotocoloptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/protocol/lesprotocol"](../modules/_net_protocol_lesprotocol_.md) › [LesProtocolOptions](_net_protocol_lesprotocol_.lesprotocoloptions.md) 2 | 3 | # Interface: LesProtocolOptions 4 | 5 | ## Hierarchy 6 | 7 | * [ProtocolOptions](_net_protocol_protocol_.protocoloptions.md) 8 | 9 | ↳ **LesProtocolOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [chain](_net_protocol_lesprotocol_.lesprotocoloptions.md#chain) 16 | * [config](_net_protocol_lesprotocol_.lesprotocoloptions.md#config) 17 | * [flow](_net_protocol_lesprotocol_.lesprotocoloptions.md#optional-flow) 18 | * [timeout](_net_protocol_lesprotocol_.lesprotocoloptions.md#optional-timeout) 19 | 20 | ## Properties 21 | 22 | ### chain 23 | 24 | • **chain**: *[Chain](../classes/_blockchain_chain_.chain.md)* 25 | 26 | *Defined in [lib/net/protocol/lesprotocol.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/lesprotocol.ts#L9)* 27 | 28 | ___ 29 | 30 | ### config 31 | 32 | • **config**: *[Config](../classes/_config_.config.md)* 33 | 34 | *Inherited from [LesProtocolOptions](_net_protocol_lesprotocol_.lesprotocoloptions.md).[config](_net_protocol_lesprotocol_.lesprotocoloptions.md#config)* 35 | 36 | *Defined in [lib/net/protocol/protocol.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/protocol.ts#L8)* 37 | 38 | ___ 39 | 40 | ### `Optional` flow 41 | 42 | • **flow**? : *[FlowControl](../classes/_net_protocol_flowcontrol_.flowcontrol.md)* 43 | 44 | *Defined in [lib/net/protocol/lesprotocol.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/lesprotocol.ts#L12)* 45 | 46 | ___ 47 | 48 | ### `Optional` timeout 49 | 50 | • **timeout**? : *undefined | number* 51 | 52 | *Inherited from [LesProtocolOptions](_net_protocol_lesprotocol_.lesprotocoloptions.md).[timeout](_net_protocol_lesprotocol_.lesprotocoloptions.md#optional-timeout)* 53 | 54 | *Defined in [lib/net/protocol/protocol.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/protocol/protocol.ts#L11)* 55 | -------------------------------------------------------------------------------- /lib/sync/fetcher/headerfetcher.ts: -------------------------------------------------------------------------------- 1 | import { BlockFetcher, BlockFetcherOptions } from './blockfetcher' 2 | import { Peer } from '../../net/peer' 3 | import { FlowControl } from '../../net/protocol' 4 | 5 | export interface HeaderFetcherOptions extends BlockFetcherOptions { 6 | /* Flow control manager */ 7 | flow: FlowControl 8 | } 9 | 10 | /** 11 | * Implements an les/1 based header fetcher 12 | * @memberof module:sync/fetcher 13 | */ 14 | export class HeaderFetcher extends BlockFetcher { 15 | private flow: FlowControl 16 | 17 | /** 18 | * Create new header fetcher 19 | * @param {HeaderFetcherOptions} 20 | */ 21 | constructor(options: any) { 22 | super(options) 23 | 24 | this.flow = options.flow 25 | } 26 | 27 | /** 28 | * Requests block headers for the given task 29 | * @param job 30 | * @return {Promise} 31 | */ 32 | async request(job: any): Promise { 33 | const { task, peer } = job 34 | if (this.flow.maxRequestCount(peer, 'GetBlockHeaders') < this.maxPerRequest) { 35 | // we reached our request limit. try with a different peer. 36 | return false 37 | } 38 | return peer.les.getBlockHeaders({ block: task.first, max: task.count }) 39 | } 40 | 41 | /** 42 | * Process fetch result 43 | * @param job fetch job 44 | * @param result fetch result 45 | * @return {*} results of processing job or undefined if job not finished 46 | */ 47 | process(job: any, result: any) { 48 | this.flow.handleReply(job.peer, result.bv) 49 | if (result.headers && result.headers.length === job.task.count) { 50 | return result.headers 51 | } 52 | } 53 | 54 | /** 55 | * Store fetch result. Resolves once store operation is complete. 56 | * @param {Header[]} headers fetch result 57 | * @return {Promise} 58 | */ 59 | async store(headers: any[]) { 60 | await this.chain.putHeaders(headers) 61 | } 62 | 63 | /** 64 | * Returns a peer that can process the given job 65 | * @param job job 66 | * @return {Peer} 67 | */ 68 | // TODO: what is job supposed to be? 69 | peer(_job: any): Peer { 70 | return this.pool.idle((p: any) => p.les && p.les.status.serveHeaders) 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /docs/interfaces/_service_ethereumservice_.ethereumserviceoptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["service/ethereumservice"](../modules/_service_ethereumservice_.md) › [EthereumServiceOptions](_service_ethereumservice_.ethereumserviceoptions.md) 2 | 3 | # Interface: EthereumServiceOptions 4 | 5 | ## Hierarchy 6 | 7 | * [ServiceOptions](_service_service_.serviceoptions.md) 8 | 9 | ↳ **EthereumServiceOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [chain](_service_ethereumservice_.ethereumserviceoptions.md#optional-chain) 16 | * [config](_service_ethereumservice_.ethereumserviceoptions.md#config) 17 | * [db](_service_ethereumservice_.ethereumserviceoptions.md#optional-db) 18 | * [interval](_service_ethereumservice_.ethereumserviceoptions.md#optional-interval) 19 | * [timeout](_service_ethereumservice_.ethereumserviceoptions.md#optional-timeout) 20 | 21 | ## Properties 22 | 23 | ### `Optional` chain 24 | 25 | • **chain**? : *[Chain](../classes/_blockchain_chain_.chain.md)* 26 | 27 | *Defined in [lib/service/ethereumservice.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/ethereumservice.ts#L9)* 28 | 29 | ___ 30 | 31 | ### config 32 | 33 | • **config**: *[Config](../classes/_config_.config.md)* 34 | 35 | *Inherited from [ServiceOptions](_service_service_.serviceoptions.md).[config](_service_service_.serviceoptions.md#config)* 36 | 37 | *Defined in [lib/service/service.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/service.ts#L9)* 38 | 39 | ___ 40 | 41 | ### `Optional` db 42 | 43 | • **db**? : *LevelUp* 44 | 45 | *Defined in [lib/service/ethereumservice.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/ethereumservice.ts#L12)* 46 | 47 | ___ 48 | 49 | ### `Optional` interval 50 | 51 | • **interval**? : *undefined | number* 52 | 53 | *Defined in [lib/service/ethereumservice.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/ethereumservice.ts#L18)* 54 | 55 | ___ 56 | 57 | ### `Optional` timeout 58 | 59 | • **timeout**? : *undefined | number* 60 | 61 | *Defined in [lib/service/ethereumservice.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/service/ethereumservice.ts#L15)* 62 | -------------------------------------------------------------------------------- /test/integration/fullsync.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { wait, setup, destroy } from './util' 3 | 4 | tape('[Integration:FullSync]', async (t) => { 5 | t.test('should sync blocks', async (t) => { 6 | const [remoteServer, remoteService] = await setup({ location: '127.0.0.2', height: 200 }) 7 | const [localServer, localService] = await setup({ location: '127.0.0.1', height: 0 }) 8 | localService.on('synchronized', async () => { 9 | t.equals(localService.chain.blocks.height.toNumber(), 200, 'synced') 10 | await destroy(localServer, localService) 11 | await destroy(remoteServer, remoteService) 12 | t.end() 13 | }) 14 | await localServer.discover('remotePeer', '127.0.0.2') 15 | }) 16 | 17 | t.test('should not sync with stale peers', async (t) => { 18 | const [remoteServer, remoteService] = await setup({ location: '127.0.0.2', height: 9 }) 19 | const [localServer, localService] = await setup({ location: '127.0.0.1', height: 10 }) 20 | localService.on('synchronized', async () => { 21 | t.fail('synced with a stale peer') 22 | }) 23 | await localServer.discover('remotePeer', '127.0.0.2') 24 | await wait(100) 25 | await destroy(localServer, localService) 26 | await destroy(remoteServer, remoteService) 27 | t.pass('did not sync') 28 | t.end() 29 | }) 30 | 31 | t.test('should sync with best peer', async (t) => { 32 | const [remoteServer1, remoteService1] = await setup({ location: '127.0.0.2', height: 9 }) 33 | const [remoteServer2, remoteService2] = await setup({ location: '127.0.0.3', height: 10 }) 34 | const [localServer, localService] = await setup({ location: '127.0.0.1', height: 0 }) 35 | await localService.synchronizer.stop() 36 | await localServer.discover('remotePeer1', '127.0.0.2') 37 | await localServer.discover('remotePeer2', '127.0.0.3') 38 | localService.on('synchronized', async () => { 39 | t.equals(localService.chain.blocks.height.toNumber(), 10, 'synced with best peer') 40 | await destroy(localServer, localService) 41 | await destroy(remoteServer1, remoteService1) 42 | await destroy(remoteServer2, remoteService2) 43 | t.end() 44 | }) 45 | await localService.synchronizer.start() 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /lib/rpc/modules/net.ts: -------------------------------------------------------------------------------- 1 | import { EthereumService } from '../../service/ethereumservice' 2 | import { middleware } from '../validation' 3 | import { addHexPrefix } from 'ethereumjs-util' 4 | 5 | /** 6 | * net_* RPC module 7 | * @memberof module:rpc/modules 8 | */ 9 | export class Net { 10 | private _chain: any 11 | private _node: any 12 | private _peerPool: any 13 | 14 | /** 15 | * Create net_* RPC module 16 | * @param {Node} Node to which the module binds 17 | */ 18 | constructor(node: any) { 19 | const service: EthereumService = node.services.find((s: any) => s.name === 'eth') 20 | this._chain = service.chain 21 | this._node = node 22 | this._peerPool = service.pool 23 | 24 | this.version = middleware(this.version.bind(this), 0, []) 25 | this.listening = middleware(this.listening.bind(this), 0, []) 26 | this.peerCount = middleware(this.peerCount.bind(this), 0, []) 27 | } 28 | 29 | /** 30 | * Returns the current network id 31 | * @param {Array<*>} [params] An empty array 32 | * @param {Function} [cb] A function with an error object as the first argument and the network 33 | * id as the second argument 34 | */ 35 | version(_params = [], cb: (err: Error | null, id: string) => void) { 36 | cb(null, `${this._node.common.chainId()}`) 37 | } 38 | 39 | /** 40 | * Returns true if client is actively listening for network connections 41 | * @param {Array<*>} [params] An empty array 42 | * @param {Function} [cb] A function with an error object as the first argument and a boolean 43 | * that's true when the client is listening and false when it's not as the second argument 44 | */ 45 | listening(_params = [], cb: (err: Error | null, isListening: boolean) => void) { 46 | cb(null, this._node.opened) 47 | } 48 | 49 | /** 50 | * Returns number of peers currently connected to the client 51 | * @param {Array<*>} [params] An empty array 52 | * @param {Function} [cb] A function with an error object as the first argument and the 53 | * number of peers connected to the client as the second argument 54 | */ 55 | peerCount(_params = [], cb: (err: Error | null, numberOfPeers: string) => void) { 56 | cb(null, addHexPrefix(this._peerPool.peers.length.toString(16))) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /docs/classes/_rpc_modules_web3_.web3.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/web3"](../modules/_rpc_modules_web3_.md) › [Web3](_rpc_modules_web3_.web3.md) 2 | 3 | # Class: Web3 4 | 5 | web3_* RPC module 6 | 7 | **`memberof`** module:rpc/modules 8 | 9 | ## Hierarchy 10 | 11 | * **Web3** 12 | 13 | ## Index 14 | 15 | ### Constructors 16 | 17 | * [constructor](_rpc_modules_web3_.web3.md#constructor) 18 | 19 | ### Methods 20 | 21 | * [clientVersion](_rpc_modules_web3_.web3.md#clientversion) 22 | * [sha3](_rpc_modules_web3_.web3.md#sha3) 23 | 24 | ## Constructors 25 | 26 | ### constructor 27 | 28 | \+ **new Web3**(`node`: any): *[Web3](_rpc_modules_web3_.web3.md)* 29 | 30 | *Defined in [lib/rpc/modules/web3.ts:10](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/web3.ts#L10)* 31 | 32 | Create web3_* RPC module 33 | 34 | **Parameters:** 35 | 36 | Name | Type | 37 | ------ | ------ | 38 | `node` | any | 39 | 40 | **Returns:** *[Web3](_rpc_modules_web3_.web3.md)* 41 | 42 | ## Methods 43 | 44 | ### clientVersion 45 | 46 | ▸ **clientVersion**(`_params`: never[], `cb`: function): *void* 47 | 48 | *Defined in [lib/rpc/modules/web3.ts:31](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/web3.ts#L31)* 49 | 50 | Returns the current client version 51 | 52 | **Parameters:** 53 | 54 | ▪`Default value` **_params**: *never[]*= [] 55 | 56 | ▪ **cb**: *function* 57 | 58 | ▸ (`err`: null, `version`: string): *void* 59 | 60 | **Parameters:** 61 | 62 | Name | Type | 63 | ------ | ------ | 64 | `err` | null | 65 | `version` | string | 66 | 67 | **Returns:** *void* 68 | 69 | ___ 70 | 71 | ### sha3 72 | 73 | ▸ **sha3**(`params`: string[], `cb`: function): *void* 74 | 75 | *Defined in [lib/rpc/modules/web3.ts:42](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/web3.ts#L42)* 76 | 77 | Returns Keccak-256 (not the standardized SHA3-256) of the given data 78 | 79 | **Parameters:** 80 | 81 | ▪ **params**: *string[]* 82 | 83 | ▪ **cb**: *function* 84 | 85 | ▸ (`err`: Error | null, `hash?`: undefined | string): *void* 86 | 87 | **Parameters:** 88 | 89 | Name | Type | 90 | ------ | ------ | 91 | `err` | Error | null | 92 | `hash?` | undefined | string | 93 | 94 | **Returns:** *void* 95 | -------------------------------------------------------------------------------- /test/integration/fullethereumservice.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { BN } from 'ethereumjs-util' 3 | import { Config } from '../../lib/config' 4 | import { FullEthereumService } from '../../lib/service' 5 | import MockServer from './mocks/mockserver' 6 | import MockChain from './mocks/mockchain' 7 | import { destroy } from './util' 8 | 9 | tape('[Integration:FullEthereumService]', async (t) => { 10 | async function setup(): Promise<[MockServer, FullEthereumService]> { 11 | const loglevel = 'error' 12 | const config = new Config({ loglevel }) 13 | const server = new MockServer({ config }) 14 | const chain = new MockChain({ config }) 15 | const serviceConfig = new Config({ loglevel, servers: [server as any], lightserv: true }) 16 | const service = new FullEthereumService({ 17 | config: serviceConfig, 18 | chain, 19 | }) 20 | await service.open() 21 | await server.start() 22 | await service.start() 23 | return [server, service] 24 | } 25 | 26 | t.test('should handle ETH requests', async (t) => { 27 | const [server, service] = await setup() 28 | const peer = await server.accept('peer0') 29 | const headers = await (peer.eth as any).getBlockHeaders({ block: 1, max: 2 }) 30 | const hash = Buffer.from( 31 | 'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069', 32 | 'hex' 33 | ) 34 | t.ok(headers[1].hash().equals(hash), 'handled GetBlockHeaders') 35 | const bodies = await (peer.eth as any).getBlockBodies([hash]) 36 | t.deepEquals(bodies, [[[], []]], 'handled GetBlockBodies') 37 | await (peer.eth as any).send('NewBlockHashes', [[hash, new BN(2)]]) 38 | t.pass('handled NewBlockHashes') 39 | await destroy(server, service) 40 | t.end() 41 | }) 42 | 43 | t.test('should handle LES requests', async (t) => { 44 | const [server, service] = await setup() 45 | const peer = await server.accept('peer0') 46 | const { headers } = await (peer.les as any).getBlockHeaders({ block: 1, max: 2 }) 47 | t.equals( 48 | headers[1].hash().toString('hex'), 49 | 'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069', 50 | 'handled GetBlockHeaders' 51 | ) 52 | await destroy(server, service) 53 | t.end() 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /test/net/peer/peer.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { Peer } from '../../../lib/net/peer' 5 | import { Config } from '../../../lib/config' 6 | 7 | tape('[Peer]', (t) => { 8 | const config = new Config({ transports: [], loglevel: 'error' }) 9 | const peer = new Peer({ 10 | config, 11 | id: '0123456789abcdef', 12 | address: 'address0', 13 | transport: 'transport0', 14 | inbound: true, 15 | }) 16 | 17 | t.test('should get/set idle state', (t) => { 18 | t.ok(peer.idle, 'is initially idle') 19 | peer.idle = false 20 | t.notOk(peer.idle, 'idle set to false') 21 | t.end() 22 | }) 23 | 24 | t.test('should bind protocol', async (t) => { 25 | const bound = new EventEmitter() as any 26 | const sender = 'sender' as any 27 | const protocol = td.object('Protocol') as any 28 | bound.name = 'bound0' 29 | protocol.name = 'proto0' 30 | 31 | t.plan(3) 32 | td.when(protocol.bind(peer, sender)).thenResolve(bound) 33 | await peer.bindProtocol(protocol, sender) 34 | t.equals(peer.bound.get('bound0'), bound, 'protocol bound') 35 | peer.on('message', (msg: string, name: string) => { 36 | t.ok(msg === 'msg0' && name === 'proto0', 'on message') 37 | }) 38 | peer.on('error', (err: Error, name: string) => { 39 | t.ok(err.message === 'err0' && name === 'proto0', 'on error') 40 | }) 41 | bound.emit('message', 'msg0') 42 | bound.emit('error', new Error('err0')) 43 | }) 44 | 45 | t.test('should understand protocols', (t) => { 46 | t.ok(peer.understands('bound0'), 'understands bound protocol') 47 | t.notOk(peer.understands('unknown'), 'does not understand unknown protocol') 48 | t.end() 49 | }) 50 | 51 | t.test('should convert to string', (t) => { 52 | t.equals( 53 | peer.toString(true), 54 | 'id=0123456789abcdef address=address0 transport=transport0 protocols=bound0 inbound=true', 55 | 'correct full id string' 56 | ) 57 | peer.inbound = false 58 | t.equals( 59 | peer.toString(), 60 | 'id=01234567 address=address0 transport=transport0 protocols=bound0 inbound=false', 61 | 'correct short id string' 62 | ) 63 | t.end() 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /test/util/parse.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import { parseBootnodes, parseTransports, parseParams } from '../../lib/util' 3 | 4 | tape('[Util/Parse]', (t) => { 5 | t.test('should parse bootnodes', (t) => { 6 | t.deepEquals(parseBootnodes(''), [], 'handle empty') 7 | t.deepEquals(parseBootnodes('10.0.0.1:1234'), [{ ip: '10.0.0.1', port: 1234 }], 'parse ip:port') 8 | t.deepEquals( 9 | parseBootnodes('enode://abc@10.0.0.1:1234'), 10 | [{ id: 'abc', ip: '10.0.0.1', port: 1234 }], 11 | 'parse url' 12 | ) 13 | t.deepEquals( 14 | parseBootnodes('10.0.0.1:1234,enode://abc@127.0.0.1:2345'), 15 | [ 16 | { ip: '10.0.0.1', port: 1234 }, 17 | { id: 'abc', ip: '127.0.0.1', port: 2345 }, 18 | ], 19 | 'parse multiple' 20 | ) 21 | t.throws(() => parseBootnodes((10) as string), /not a function/, 'throws error') 22 | t.end() 23 | }) 24 | 25 | t.test('should parse transports', (t) => { 26 | t.deepEquals( 27 | parseTransports(['t1']), 28 | [{ name: 't1', options: {} }], 29 | 'parsed transport without options' 30 | ) 31 | t.deepEquals( 32 | parseTransports(['t2:k1=v1,k:k=v2,k3="v3",k4,k5=']), 33 | [ 34 | { 35 | name: 't2', 36 | options: { k1: 'v1', 'k:k': 'v2', k3: '"v3"', k4: undefined, k5: '' }, 37 | }, 38 | ], 39 | 'parsed transport with options' 40 | ) 41 | t.end() 42 | }) 43 | 44 | t.test('should parse geth params file', async (t) => { 45 | const json = require('./rinkeby.json') 46 | const params = await parseParams(json, 'rinkeby') 47 | const expected = require('./params.json') 48 | expected.genesis.hash = Buffer.from(expected.genesis.hash) 49 | expected.genesis.stateRoot = Buffer.from(expected.genesis.stateRoot) 50 | t.deepEquals(params, expected, 'parsed params correctly') 51 | t.end() 52 | }) 53 | 54 | t.test('should parse contracts from geth params file', async (t) => { 55 | const json = require('./lisinski.json') 56 | const params = await parseParams(json, 'lisinsky') 57 | const expected = 'e7fd8db206dcaf066b7c97b8a42a0abc18653613560748557ab44868652a78b6' 58 | t.equals(params.genesis.hash.toString('hex'), expected, 'parsed contracts correctly') 59 | t.end() 60 | }) 61 | }) 62 | -------------------------------------------------------------------------------- /test/rpc/web3/sha3.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { baseSetup, params, baseRequest } from '../helpers' 3 | 4 | const method = 'web3_sha3' 5 | 6 | function compareErrorCode(t: any, error: any, errorCode: any) { 7 | const msg = `should return the correct error code (expected: ${errorCode}, received: ${error.code})` 8 | if (error.code !== errorCode) { 9 | throw new Error(msg) 10 | } else { 11 | t.pass(msg) 12 | } 13 | } 14 | 15 | function compareErrorMsg(t: any, error: any, errorMsg: any) { 16 | const msg = `should return "${errorMsg}" error message` 17 | if (error.message !== errorMsg) { 18 | throw new Error(msg) 19 | } else { 20 | t.pass(msg) 21 | } 22 | } 23 | 24 | tape(`${method}: call with one valid parameter`, (t) => { 25 | const server = baseSetup() 26 | 27 | const req = params(method, ['0x68656c6c6f20776f726c64']) 28 | const expectRes = (res: any) => { 29 | const { result } = res.body 30 | let msg = 'result string should not be empty' 31 | if (result.length === 0) { 32 | throw new Error(msg) 33 | } else { 34 | t.pass(msg) 35 | } 36 | 37 | msg = 'should return the correct hash value' 38 | if (result !== '0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad') { 39 | throw new Error(msg) 40 | } else { 41 | t.pass(msg) 42 | } 43 | } 44 | baseRequest(t, server, req, 200, expectRes) 45 | }) 46 | 47 | tape(`${method}: call with one non-hex parameter`, (t) => { 48 | const server = baseSetup() 49 | 50 | const req = params(method, ['hello world']) 51 | const expectRes = (res: any) => { 52 | const { error } = res.body 53 | 54 | compareErrorCode(t, error, -32602) 55 | 56 | const errorMsg = 'invalid argument 0: hex string without 0x prefix' 57 | compareErrorMsg(t, error, errorMsg) 58 | } 59 | baseRequest(t, server, req, 200, expectRes) 60 | }) 61 | 62 | tape(`${method}: call with no parameters`, (t) => { 63 | const server = baseSetup() 64 | 65 | const req = params(method, []) 66 | const expectRes = (res: any) => { 67 | const { error } = res.body 68 | 69 | compareErrorCode(t, error, -32602) 70 | 71 | const errorMsg = 'missing value for required argument 0' 72 | compareErrorMsg(t, error, errorMsg) 73 | } 74 | baseRequest(t, server, req, 200, expectRes) 75 | }) 76 | -------------------------------------------------------------------------------- /browser/index.ts: -------------------------------------------------------------------------------- 1 | import Common from '@ethereumjs/common' 2 | const level = require('level') 3 | 4 | // Blockchain 5 | export * from '../lib/blockchain/chain' 6 | 7 | // Peer 8 | export * from '../lib/net/peer/peer' 9 | export * from '../lib/net/peer/libp2ppeer' 10 | export * from './libp2pnode' 11 | 12 | // Peer Pool 13 | export * from '../lib/net/peerpool' 14 | 15 | // Protocol 16 | export * from '../lib/net/protocol/protocol' 17 | export * from '../lib/net/protocol/ethprotocol' 18 | export * from '../lib/net/protocol/lesprotocol' 19 | export * from '../lib/net/protocol/flowcontrol' 20 | 21 | // Server 22 | export * from '../lib/net/server/server' 23 | export * from '../lib/net/server/libp2pserver' 24 | 25 | // EthereumClient 26 | export * from '../lib/client' 27 | 28 | // Service 29 | export * from '../lib/service/service' 30 | export * from '../lib/service/fullethereumservice' 31 | export * from '../lib/service/lightethereumservice' 32 | 33 | // Synchronizer 34 | export * from '../lib/sync/sync' 35 | export * from '../lib/sync/fullsync' 36 | export * from '../lib/sync/lightsync' 37 | 38 | // Utilities 39 | export * from '../lib/util' 40 | 41 | // Logging 42 | export * from './logging' 43 | import { getLogger } from './logging' 44 | 45 | export function createClient(args: any) { 46 | const logger = getLogger({ loglevel: args.loglevel }) 47 | const options = { 48 | common: new Common({ chain: args.network ?? 'mainnet' }), 49 | servers: [new exports.Libp2pServer({ multiaddrs: [], ...args })], 50 | syncmode: args.syncmode ?? 'full', 51 | db: level(args.db ?? 'ethereumjs'), 52 | logger: logger, 53 | } 54 | return new exports.EthereumClient(options) 55 | } 56 | 57 | export function run(args: any) { 58 | const client = createClient(args) 59 | const logger = client.logger 60 | logger.info('Initializing Ethereumjs client...') 61 | logger.info(`Connecting to network: ${client.common.chainName()}`) 62 | client.on('error', (err: any) => logger.error(err)) 63 | client.on('listening', (details: any) => { 64 | logger.info(`Listener up transport=${details.transport} url=${details.url}`) 65 | }) 66 | client.on('synchronized', () => { 67 | logger.info('Synchronized') 68 | }) 69 | client.open().then(() => { 70 | logger.info('Synchronizing blockchain...') 71 | client.start() 72 | }) 73 | return client 74 | } 75 | -------------------------------------------------------------------------------- /docs/README.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](README.md) 2 | 3 | # ethereumjs-client 4 | 5 | ## Index 6 | 7 | ### Modules 8 | 9 | * ["blockchain/chain"](modules/_blockchain_chain_.md) 10 | * ["config"](modules/_config_.md) 11 | * ["logging"](modules/_logging_.md) 12 | * ["net/peer/libp2pnode"](modules/_net_peer_libp2pnode_.md) 13 | * ["net/peer/libp2ppeer"](modules/_net_peer_libp2ppeer_.md) 14 | * ["net/peer/peer"](modules/_net_peer_peer_.md) 15 | * ["net/peer/rlpxpeer"](modules/_net_peer_rlpxpeer_.md) 16 | * ["net/peerpool"](modules/_net_peerpool_.md) 17 | * ["net/protocol/boundprotocol"](modules/_net_protocol_boundprotocol_.md) 18 | * ["net/protocol/ethprotocol"](modules/_net_protocol_ethprotocol_.md) 19 | * ["net/protocol/flowcontrol"](modules/_net_protocol_flowcontrol_.md) 20 | * ["net/protocol/lesprotocol"](modules/_net_protocol_lesprotocol_.md) 21 | * ["net/protocol/libp2psender"](modules/_net_protocol_libp2psender_.md) 22 | * ["net/protocol/protocol"](modules/_net_protocol_protocol_.md) 23 | * ["net/protocol/rlpxsender"](modules/_net_protocol_rlpxsender_.md) 24 | * ["net/protocol/sender"](modules/_net_protocol_sender_.md) 25 | * ["net/server/libp2pserver"](modules/_net_server_libp2pserver_.md) 26 | * ["net/server/rlpxserver"](modules/_net_server_rlpxserver_.md) 27 | * ["net/server/server"](modules/_net_server_server_.md) 28 | * ["node"](modules/_node_.md) 29 | * ["rpc/modules/admin"](modules/_rpc_modules_admin_.md) 30 | * ["rpc/modules/eth"](modules/_rpc_modules_eth_.md) 31 | * ["rpc/modules/net"](modules/_rpc_modules_net_.md) 32 | * ["rpc/modules/web3"](modules/_rpc_modules_web3_.md) 33 | * ["rpc/validation"](modules/_rpc_validation_.md) 34 | * ["service/ethereumservice"](modules/_service_ethereumservice_.md) 35 | * ["service/fullethereumservice"](modules/_service_fullethereumservice_.md) 36 | * ["service/lightethereumservice"](modules/_service_lightethereumservice_.md) 37 | * ["service/service"](modules/_service_service_.md) 38 | * ["sync/fetcher/blockfetcher"](modules/_sync_fetcher_blockfetcher_.md) 39 | * ["sync/fetcher/fetcher"](modules/_sync_fetcher_fetcher_.md) 40 | * ["sync/fetcher/headerfetcher"](modules/_sync_fetcher_headerfetcher_.md) 41 | * ["sync/fullsync"](modules/_sync_fullsync_.md) 42 | * ["sync/lightsync"](modules/_sync_lightsync_.md) 43 | * ["sync/sync"](modules/_sync_sync_.md) 44 | * ["types"](modules/_types_.md) 45 | * ["util/parse"](modules/_util_parse_.md) 46 | -------------------------------------------------------------------------------- /docs/interfaces/_net_peer_peer_.peeroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/peer/peer"](../modules/_net_peer_peer_.md) › [PeerOptions](_net_peer_peer_.peeroptions.md) 2 | 3 | # Interface: PeerOptions 4 | 5 | ## Hierarchy 6 | 7 | * **PeerOptions** 8 | 9 | ## Index 10 | 11 | ### Properties 12 | 13 | * [address](_net_peer_peer_.peeroptions.md#address) 14 | * [config](_net_peer_peer_.peeroptions.md#config) 15 | * [id](_net_peer_peer_.peeroptions.md#optional-id) 16 | * [inbound](_net_peer_peer_.peeroptions.md#optional-inbound) 17 | * [protocols](_net_peer_peer_.peeroptions.md#optional-protocols) 18 | * [server](_net_peer_peer_.peeroptions.md#optional-server) 19 | * [transport](_net_peer_peer_.peeroptions.md#transport) 20 | 21 | ## Properties 22 | 23 | ### address 24 | 25 | • **address**: *string* 26 | 27 | *Defined in [lib/net/peer/peer.ts:14](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L14)* 28 | 29 | ___ 30 | 31 | ### config 32 | 33 | • **config**: *[Config](../classes/_config_.config.md)* 34 | 35 | *Defined in [lib/net/peer/peer.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L8)* 36 | 37 | ___ 38 | 39 | ### `Optional` id 40 | 41 | • **id**? : *undefined | string* 42 | 43 | *Defined in [lib/net/peer/peer.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L11)* 44 | 45 | ___ 46 | 47 | ### `Optional` inbound 48 | 49 | • **inbound**? : *undefined | false | true* 50 | 51 | *Defined in [lib/net/peer/peer.ts:20](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L20)* 52 | 53 | ___ 54 | 55 | ### `Optional` protocols 56 | 57 | • **protocols**? : *[Protocol](../classes/_net_protocol_protocol_.protocol.md)[]* 58 | 59 | *Defined in [lib/net/peer/peer.ts:23](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L23)* 60 | 61 | ___ 62 | 63 | ### `Optional` server 64 | 65 | • **server**? : *[Server](../classes/_net_server_server_.server.md)* 66 | 67 | *Defined in [lib/net/peer/peer.ts:26](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L26)* 68 | 69 | ___ 70 | 71 | ### transport 72 | 73 | • **transport**: *string* 74 | 75 | *Defined in [lib/net/peer/peer.ts:17](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/peer/peer.ts#L17)* 76 | -------------------------------------------------------------------------------- /test/net/protocol/ethprotocol.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import { BN } from 'ethereumjs-util' 3 | import { Chain } from '../../../lib/blockchain/chain' 4 | import { Config } from '../../../lib/config' 5 | import { EthProtocol } from '../../../lib/net/protocol' 6 | 7 | tape('[EthProtocol]', (t) => { 8 | t.test('should get properties', (t) => { 9 | const config = new Config({ transports: [], loglevel: 'error' }) 10 | const chain = new Chain({ config }) 11 | const p = new EthProtocol({ config, chain }) 12 | t.ok(typeof p.name === 'string', 'get name') 13 | t.ok(Array.isArray(p.versions), 'get versions') 14 | t.ok(Array.isArray(p.messages), 'get messages') 15 | t.end() 16 | }) 17 | 18 | t.test('should open correctly', async (t) => { 19 | const config = new Config({ transports: [], loglevel: 'error' }) 20 | const chain = new Chain({ config }) 21 | const p = new EthProtocol({ config, chain }) 22 | await p.open() 23 | t.ok(p.opened, 'opened is true') 24 | t.notOk(await p.open(), 'repeat open') 25 | t.end() 26 | }) 27 | 28 | t.test('should encode/decode status', (t) => { 29 | const config = new Config({ transports: [], loglevel: 'error' }) 30 | const chain = new Chain({ config }) 31 | const p = new EthProtocol({ config, chain }) 32 | Object.defineProperty(chain, 'networkId', { 33 | get: () => { 34 | return 1 35 | }, 36 | }) 37 | Object.defineProperty(chain, 'blocks', { 38 | get: () => { 39 | return { 40 | td: new BN(100), 41 | latest: { hash: () => '0xaa' }, 42 | } 43 | }, 44 | }) 45 | Object.defineProperty(chain, 'genesis', { 46 | get: () => { 47 | return { hash: '0xbb' } 48 | }, 49 | }) 50 | t.deepEquals( 51 | p.encodeStatus(), 52 | { 53 | networkId: 1, 54 | td: Buffer.from('64', 'hex'), 55 | bestHash: '0xaa', 56 | genesisHash: '0xbb', 57 | }, 58 | 'encode status' 59 | ) 60 | const status = p.decodeStatus({ 61 | networkId: [0x01], 62 | td: Buffer.from('64', 'hex'), 63 | bestHash: '0xaa', 64 | genesisHash: '0xbb', 65 | }) 66 | t.ok( 67 | status.networkId === 1 && 68 | status.td.toNumber() === 100 && 69 | status.bestHash === '0xaa' && 70 | status.genesisHash === '0xbb', 71 | 'decode status' 72 | ) 73 | t.end() 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /lib/net/protocol/libp2psender.ts: -------------------------------------------------------------------------------- 1 | import { Sender } from './sender' 2 | import { bufferToInt, rlp } from 'ethereumjs-util' 3 | import { Pushable } from 'pull-pushable' 4 | 5 | // TODO: polkadot/ts types seem wrong (?) 6 | // "pull_pushable_1.default is not a function" 7 | const pushable = require('pull-pushable') 8 | const catcher = require('pull-catch') 9 | const pull = require('pull-stream') 10 | 11 | /** 12 | * Libp2p protocol sender 13 | * @emits message 14 | * @emits status 15 | * @memberof module:net/protocol 16 | */ 17 | export class Libp2pSender extends Sender { 18 | private connection: any 19 | private pushableStream: Pushable 20 | /** 21 | * Creates a new Libp2p protocol sender 22 | * @param {Connection} connection connection to libp2p peer 23 | */ 24 | constructor(connection: any) { 25 | super() 26 | 27 | this.connection = connection 28 | this.pushableStream = pushable() 29 | this.init() 30 | } 31 | 32 | init() { 33 | // outgoing stream 34 | pull( 35 | this.pushableStream, 36 | catcher((e: Error) => this.error(e)), 37 | this.connection 38 | ) 39 | 40 | // incoming stream 41 | pull( 42 | this.connection, 43 | catcher((e: Error) => this.error(e)), 44 | pull.drain((message: any) => { 45 | // eslint-disable-next-line prefer-const 46 | let [code, payload]: any = rlp.decode(message) 47 | code = bufferToInt(code) 48 | if (code === 0) { 49 | const status: any = {} 50 | payload.forEach(([k, v]: any) => { 51 | status[k.toString()] = v 52 | }) 53 | this.status = status 54 | } else { 55 | this.emit('message', { code, payload }) 56 | } 57 | }) 58 | ) 59 | } 60 | 61 | /** 62 | * Send a status to peer 63 | * @param {Object} status 64 | */ 65 | sendStatus(status: any) { 66 | const payload: any = Object.entries(status).map(([k, v]) => [k, v]) 67 | this.pushableStream.push(rlp.encode([0, payload])) 68 | } 69 | 70 | /** 71 | * Send a message to peer 72 | * @param {number} code message code 73 | * @param {*} data message payload 74 | */ 75 | sendMessage(code: number, data: any) { 76 | this.pushableStream.push(rlp.encode([code, data])) 77 | } 78 | 79 | /** 80 | * Handle pull stream errors 81 | * @param error error 82 | */ 83 | error(error: Error) { 84 | this.emit('error', error) 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/index_old.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Define a library component for lazy loading. Borrowed from 3 | * https://github.com/bcoin-org/bcoin/blob/master/lib/bcoin.js 4 | * @param {string} name 5 | * @param {string} path 6 | */ 7 | exports.define = function define(name: string, path: string) { 8 | let cache: any = null 9 | Object.defineProperty(exports, name, { 10 | enumerable: true, 11 | get() { 12 | if (!cache) { 13 | cache = require(path) 14 | } 15 | return cache 16 | }, 17 | }) 18 | } 19 | 20 | // Blockchain 21 | exports.define('blockchain', './blockchain') 22 | exports.define('Chain', './blockchain/chain') 23 | 24 | // Handler 25 | exports.define('handler', './handler') 26 | exports.define('Handler', './handler/handler') 27 | exports.define('EthHandler', './handler/ethhandler') 28 | exports.define('LesHandler', './handler/leshandler') 29 | 30 | // Peer 31 | exports.define('peer', './net/peer') 32 | exports.define('Peer', './net/peer/peer') 33 | exports.define('RlpxPeer', './net/peer/rlpxpeer') 34 | exports.define('Libp2pPeer', './net/peer/libp2ppeer') 35 | 36 | // Peer Pool 37 | exports.define('PeerPool', './net/peerpool') 38 | 39 | // Protocol 40 | exports.define('protocol', './net/protocol') 41 | exports.define('Protocol', './net/protocol/protocol') 42 | exports.define('EthProtocol', './net/protocol/ethprotocol') 43 | exports.define('LesProtocol', './net/protocol/lesprotocol') 44 | exports.define('FlowControl', './net/protocol/flowcontrol') 45 | 46 | // Server 47 | exports.define('server', './net/server') 48 | exports.define('Server', './net/server/server') 49 | exports.define('RlpxServer', './net/server/rlpxserver') 50 | exports.define('Libp2pServer', './net/server/libp2pserver') 51 | 52 | // EthereumClient 53 | exports.define('EthereumClient', './client') 54 | 55 | // RPC Manager 56 | exports.define('RPCManager', './rpc') 57 | 58 | // Config 59 | exports.define('Config', 'config') 60 | 61 | // Service 62 | exports.define('service', './service') 63 | exports.define('Service', './service/service') 64 | exports.define('EthereumService', './service/ethereumservice') 65 | 66 | // Synchronizer 67 | exports.define('sync', './sync') 68 | exports.define('Synchronizer', './sync/sync') 69 | exports.define('FullSynchronizer', './sync/fullsync') 70 | exports.define('LightSynchronizer', './sync/lightsync') 71 | 72 | // Utilities 73 | exports.define('util', './util') 74 | 75 | // Logging 76 | exports.define('logging', './logging') 77 | 78 | export = exports 79 | -------------------------------------------------------------------------------- /docs/classes/_rpc_modules_admin_.admin.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/admin"](../modules/_rpc_modules_admin_.md) › [Admin](_rpc_modules_admin_.admin.md) 2 | 3 | # Class: Admin 4 | 5 | admin_* RPC module 6 | 7 | **`memberof`** module:rpc/modules 8 | 9 | ## Hierarchy 10 | 11 | * **Admin** 12 | 13 | ## Index 14 | 15 | ### Constructors 16 | 17 | * [constructor](_rpc_modules_admin_.admin.md#constructor) 18 | 19 | ### Properties 20 | 21 | * [_chain](_rpc_modules_admin_.admin.md#_chain) 22 | * [_ethProtocol](_rpc_modules_admin_.admin.md#_ethprotocol) 23 | * [_node](_rpc_modules_admin_.admin.md#_node) 24 | 25 | ### Methods 26 | 27 | * [nodeInfo](_rpc_modules_admin_.admin.md#nodeinfo) 28 | 29 | ## Constructors 30 | 31 | ### constructor 32 | 33 | \+ **new Admin**(`node`: [Node](_node_.node.md)): *[Admin](_rpc_modules_admin_.admin.md)* 34 | 35 | *Defined in [lib/rpc/modules/admin.ts:17](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/admin.ts#L17)* 36 | 37 | Create admin_* RPC module 38 | 39 | **Parameters:** 40 | 41 | Name | Type | 42 | ------ | ------ | 43 | `node` | [Node](_node_.node.md) | 44 | 45 | **Returns:** *[Admin](_rpc_modules_admin_.admin.md)* 46 | 47 | ## Properties 48 | 49 | ### _chain 50 | 51 | • **_chain**: *[Chain](_blockchain_chain_.chain.md)* 52 | 53 | *Defined in [lib/rpc/modules/admin.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/admin.ts#L15)* 54 | 55 | ___ 56 | 57 | ### _ethProtocol 58 | 59 | • **_ethProtocol**: *[EthProtocol](_net_protocol_ethprotocol_.ethprotocol.md)* 60 | 61 | *Defined in [lib/rpc/modules/admin.ts:17](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/admin.ts#L17)* 62 | 63 | ___ 64 | 65 | ### _node 66 | 67 | • **_node**: *[Node](_node_.node.md)* 68 | 69 | *Defined in [lib/rpc/modules/admin.ts:16](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/admin.ts#L16)* 70 | 71 | ## Methods 72 | 73 | ### nodeInfo 74 | 75 | ▸ **nodeInfo**(`params`: any, `cb`: Function): *Promise‹any›* 76 | 77 | *Defined in [lib/rpc/modules/admin.ts:38](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/admin.ts#L38)* 78 | 79 | Returns information about the currently running node. 80 | see for reference: https://geth.ethereum.org/docs/rpc/ns-admin#admin_nodeinfo 81 | 82 | **Parameters:** 83 | 84 | Name | Type | 85 | ------ | ------ | 86 | `params` | any | 87 | `cb` | Function | 88 | 89 | **Returns:** *Promise‹any›* 90 | -------------------------------------------------------------------------------- /docs/modules/_util_parse_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["util/parse"](_util_parse_.md) 2 | 3 | # Module: "util/parse" 4 | 5 | ## Index 6 | 7 | ### Functions 8 | 9 | * [parseBootnodes](_util_parse_.md#parsebootnodes) 10 | * [parseKey](_util_parse_.md#parsekey) 11 | * [parseMultiaddrs](_util_parse_.md#parsemultiaddrs) 12 | * [parseParams](_util_parse_.md#parseparams) 13 | * [parseTransports](_util_parse_.md#parsetransports) 14 | 15 | ## Functions 16 | 17 | ### parseBootnodes 18 | 19 | ▸ **parseBootnodes**(`input`: [BootnodeLike](_types_.md#bootnodelike)): *[Bootnode](../interfaces/_types_.bootnode.md)[]* 20 | 21 | *Defined in [lib/util/parse.ts:7](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/util/parse.ts#L7)* 22 | 23 | **Parameters:** 24 | 25 | Name | Type | 26 | ------ | ------ | 27 | `input` | [BootnodeLike](_types_.md#bootnodelike) | 28 | 29 | **Returns:** *[Bootnode](../interfaces/_types_.bootnode.md)[]* 30 | 31 | ___ 32 | 33 | ### parseKey 34 | 35 | ▸ **parseKey**(`input`: string | Buffer): *Buffer‹›* 36 | 37 | *Defined in [lib/util/parse.ts:180](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/util/parse.ts#L180)* 38 | 39 | **Parameters:** 40 | 41 | Name | Type | 42 | ------ | ------ | 43 | `input` | string | Buffer | 44 | 45 | **Returns:** *Buffer‹›* 46 | 47 | ___ 48 | 49 | ### parseMultiaddrs 50 | 51 | ▸ **parseMultiaddrs**(`input`: [MultiaddrsLike](_types_.md#multiaddrslike)): *[Multiaddrs](_types_.md#multiaddrs)* 52 | 53 | *Defined in [lib/util/parse.ts:173](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/util/parse.ts#L173)* 54 | 55 | **Parameters:** 56 | 57 | Name | Type | 58 | ------ | ------ | 59 | `input` | [MultiaddrsLike](_types_.md#multiaddrslike) | 60 | 61 | **Returns:** *[Multiaddrs](_types_.md#multiaddrs)* 62 | 63 | ___ 64 | 65 | ### parseParams 66 | 67 | ▸ **parseParams**(`json`: any, `name?`: undefined | string): *Promise‹any›* 68 | 69 | *Defined in [lib/util/parse.ts:157](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/util/parse.ts#L157)* 70 | 71 | **Parameters:** 72 | 73 | Name | Type | 74 | ------ | ------ | 75 | `json` | any | 76 | `name?` | undefined | string | 77 | 78 | **Returns:** *Promise‹any›* 79 | 80 | ___ 81 | 82 | ### parseTransports 83 | 84 | ▸ **parseTransports**(`transports`: string[]): *object[]* 85 | 86 | *Defined in [lib/util/parse.ts:34](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/util/parse.ts#L34)* 87 | 88 | **Parameters:** 89 | 90 | Name | Type | 91 | ------ | ------ | 92 | `transports` | string[] | 93 | 94 | **Returns:** *object[]* 95 | -------------------------------------------------------------------------------- /lib/rpc/modules/admin.ts: -------------------------------------------------------------------------------- 1 | import { bufferToHex } from 'ethereumjs-util' 2 | import { Chain } from '../../blockchain' 3 | import { EthProtocol } from '../../net/protocol' 4 | import { RlpxServer } from '../../net/server' 5 | import EthereumClient from '../../client' 6 | import { EthereumService } from '../../service' 7 | import { getClientVersion } from '../../util' 8 | import { middleware } from '../validation' 9 | 10 | /** 11 | * admin_* RPC module 12 | * @memberof module:rpc/modules 13 | */ 14 | export class Admin { 15 | readonly _chain: Chain 16 | readonly _client: EthereumClient 17 | readonly _ethProtocol: EthProtocol 18 | 19 | /** 20 | * Create admin_* RPC module 21 | * @param {client} EthereumClient to which the module binds 22 | */ 23 | constructor(client: EthereumClient) { 24 | const service = client.services.find((s) => s.name === 'eth') as EthereumService 25 | this._chain = service.chain 26 | this._client = client 27 | this._ethProtocol = service.protocols.find((p) => p.name === 'eth') as EthProtocol 28 | 29 | this.nodeInfo = middleware(this.nodeInfo.bind(this), 0, []) 30 | } 31 | 32 | /** 33 | * Returns information about the currently running node. 34 | * see for reference: https://geth.ethereum.org/docs/rpc/ns-admin#admin_nodeinfo 35 | * @param {*} [params] An empty array 36 | * @param {*} [cb] A function with an error object as the first argument and the result as the second 37 | */ 38 | async nodeInfo(params: any, cb: Function) { 39 | const rlpxInfo = (this._client.server('rlpx') as RlpxServer).getRlpxInfo() 40 | const { enode, id, ip, listenAddr, ports } = rlpxInfo 41 | const { discovery, listener } = ports 42 | const clientName = getClientVersion() 43 | 44 | // TODO version not present in reference.. 45 | // const ethVersion = Math.max.apply(Math, this._ethProtocol.versions) 46 | const latestHeader = (this._chain as any)._headers.latest 47 | const difficulty = latestHeader?.difficulty ?? 0 // should be number 48 | const genesis = bufferToHex(this._chain.genesis.hash) 49 | const head = bufferToHex(latestHeader?.mixHash ?? null) 50 | const network = this._chain.networkId 51 | 52 | const nodeInfo = { 53 | name: clientName, 54 | enode, 55 | id, 56 | ip, 57 | listenAddr, 58 | ports: { 59 | discovery, 60 | listener, 61 | }, 62 | protocols: { 63 | eth: { 64 | difficulty, 65 | genesis, 66 | head, 67 | network, 68 | }, 69 | }, 70 | } 71 | return cb(null, nodeInfo) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /test/rpc/eth/getBlockByHash.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { INVALID_PARAMS } from '../../../lib/rpc/error-code' 3 | import { baseSetup, params, baseRequest } from '../helpers' 4 | import { checkError } from '../util' 5 | 6 | const method = 'eth_getBlockByHash' 7 | 8 | tape(`${method}: call with valid arguments`, (t) => { 9 | const server = baseSetup() 10 | 11 | const req = params(method, [ 12 | '0x910abca1728c53e8d6df870dd7af5352e974357dc58205dea1676be17ba6becf', 13 | true, 14 | ]) 15 | const expectRes = (res: any) => { 16 | const msg = 'should return the correct number' 17 | t.equal(res.body.result.number, 444444, msg) 18 | } 19 | baseRequest(t, server, req, 200, expectRes) 20 | }) 21 | 22 | tape(`${method}: call with false for second argument`, (t) => { 23 | const server = baseSetup() 24 | 25 | const req = params(method, [ 26 | '0xdc0818cf78f21a8e70579cb46a43643f78291264dda342ae31049421c82d21ae', 27 | false, 28 | ]) 29 | const expectRes = (res: any) => { 30 | let msg = 'should return the correct number' 31 | t.equal(res.body.result.number, 444444, msg) 32 | msg = 'should return only the hashes of the transactions' 33 | t.equal(typeof res.body.result.transactions[0], 'string', msg) 34 | } 35 | baseRequest(t, server, req, 200, expectRes) 36 | }) 37 | 38 | tape(`${method}: call with invalid block hash without 0x`, (t) => { 39 | const server = baseSetup() 40 | 41 | const req = params(method, ['WRONG BLOCK NUMBER', true]) 42 | const expectRes = checkError( 43 | t, 44 | INVALID_PARAMS, 45 | 'invalid argument 0: hex string without 0x prefix' 46 | ) 47 | baseRequest(t, server, req, 200, expectRes) 48 | }) 49 | 50 | tape(`${method}: call with invalid hex string as block hash`, (t) => { 51 | const server = baseSetup() 52 | 53 | const req = params(method, ['0xWRONG BLOCK NUMBER', true]) 54 | const expectRes = checkError(t, INVALID_PARAMS, 'invalid argument 0: invalid block hash') 55 | baseRequest(t, server, req, 200, expectRes) 56 | }) 57 | 58 | tape('call eth_getBlockByHash without second parameter', (t) => { 59 | const server = baseSetup() 60 | 61 | const req = params(method, ['0x0']) 62 | const expectRes = checkError(t, INVALID_PARAMS, 'missing value for required argument 1') 63 | baseRequest(t, server, req, 200, expectRes) 64 | }) 65 | 66 | tape('call eth_getBlockByHash with invalid second parameter', (t) => { 67 | const server = baseSetup() 68 | 69 | const req = params(method, ['0x0', 'INVALID PARAMETER']) 70 | const expectRes = checkError(t, INVALID_PARAMS) 71 | baseRequest(t, server, req, 200, expectRes) 72 | }) 73 | -------------------------------------------------------------------------------- /test/sync/fetcher/fetcher.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import td from 'testdouble' 3 | import { Config } from '../../../lib/config' 4 | import { Fetcher } from '../../../lib/sync/fetcher/fetcher' 5 | 6 | tape('[Fetcher]', (t) => { 7 | t.test('should handle bad result', (t) => { 8 | t.plan(2) 9 | const config = new Config({ loglevel: 'error', transports: [] }) 10 | const fetcher = new Fetcher({ config, pool: td.object() }) 11 | const job: any = { peer: {}, state: 'active' } 12 | ;(fetcher as any).running = true 13 | fetcher.next = td.func() 14 | fetcher.wait = td.func() 15 | td.when(fetcher.wait()).thenResolve(undefined) 16 | fetcher.success(job, undefined) 17 | t.equals((fetcher as any).in.size(), 1, 'enqueued job') 18 | setTimeout(() => t.ok(job.peer.idle, 'peer idled'), 10) 19 | }) 20 | 21 | t.test('should handle failure', (t) => { 22 | t.plan(2) 23 | const config = new Config({ loglevel: 'error', transports: [] }) 24 | const fetcher = new Fetcher({ config, pool: td.object() }) 25 | const job = { peer: {}, state: 'active' } 26 | ;(fetcher as any).running = true 27 | fetcher.next = td.func() 28 | fetcher.on('error', (err: Error) => t.equals(err.message, 'err0', 'got error')) 29 | fetcher.failure(job, new Error('err0')) 30 | t.equals((fetcher as any).in.size(), 1, 'enqueued job') 31 | }) 32 | 33 | t.test('should handle expiration', (t) => { 34 | t.plan(2) 35 | const config = new Config({ loglevel: 'error', transports: [] }) 36 | const fetcher = new Fetcher({ 37 | config, 38 | pool: td.object(), 39 | timeout: 5, 40 | }) 41 | const job = { index: 0 } 42 | const peer = { idle: true } 43 | fetcher.peer = td.func() 44 | fetcher.request = td.func() 45 | td.when(fetcher.peer()).thenReturn(peer) 46 | td.when(fetcher.request(td.matchers.anything(), { idle: false }), { delay: 10 }).thenReject( 47 | new Error('err0') 48 | ) 49 | td.when((fetcher as any).pool.contains({ idle: false })).thenReturn(true) 50 | ;(fetcher as any).in.insert(job) 51 | ;(fetcher as any)._readableState = [] 52 | ;(fetcher as any).running = true 53 | ;(fetcher as any).total = 10 54 | fetcher.next() 55 | setTimeout(() => { 56 | t.deepEquals(job, { index: 0, peer: { idle: false }, state: 'expired' }, 'expired job') 57 | t.equals((fetcher as any).in.size(), 1, 'enqueued job') 58 | }, 20) 59 | }) 60 | 61 | t.test('should reset td', (t) => { 62 | td.reset() 63 | t.end() 64 | }) 65 | }) 66 | -------------------------------------------------------------------------------- /lib/service/ethereumservice.ts: -------------------------------------------------------------------------------- 1 | import { LevelUp } from 'levelup' 2 | import { FlowControl } from '../net/protocol/flowcontrol' 3 | import { Chain } from '../blockchain' 4 | import { Service, ServiceOptions } from './service' 5 | import { Synchronizer } from '../sync' 6 | 7 | export interface EthereumServiceOptions extends ServiceOptions { 8 | /* Blockchain */ 9 | chain?: Chain 10 | 11 | /* Blockchain database */ 12 | db?: LevelUp 13 | 14 | /* Protocol timeout in ms (default: 8000) */ 15 | timeout?: number 16 | 17 | /* Sync retry interval in ms (default: 1000) */ 18 | interval?: number 19 | } 20 | 21 | /** 22 | * Ethereum service 23 | * @memberof module:service 24 | */ 25 | export class EthereumService extends Service { 26 | public flow: FlowControl 27 | public chain: Chain 28 | public interval: number 29 | public timeout: number 30 | public synchronizer!: Synchronizer 31 | 32 | /** 33 | * Create new ETH service 34 | * @param {EthereumServiceOptions} 35 | */ 36 | constructor(options: EthereumServiceOptions) { 37 | super(options) 38 | 39 | this.flow = new FlowControl() 40 | this.chain = options.chain ?? new Chain(options) 41 | this.interval = options.interval ?? 8000 42 | this.timeout = options.timeout ?? 1000 43 | } 44 | 45 | /** 46 | * Service name 47 | * @protected 48 | * @type {string} 49 | */ 50 | get name() { 51 | return 'eth' 52 | } 53 | 54 | /** 55 | * Open eth service. Must be called before service is started 56 | * @return {Promise} 57 | */ 58 | async open() { 59 | if (this.opened) { 60 | return false 61 | } 62 | await super.open() 63 | this.synchronizer.on('synchronized', () => this.emit('synchronized')) 64 | this.synchronizer.on('error', (error: Error) => this.emit('error', error)) 65 | await this.chain.open() 66 | await this.synchronizer.open() 67 | } 68 | 69 | /** 70 | * Starts service and ensures blockchain is synchronized. Returns a promise 71 | * that resolves once the service is started and blockchain is in sync. 72 | * @return {Promise} 73 | */ 74 | async start(): Promise { 75 | if (this.running) { 76 | return false 77 | } 78 | await super.start() 79 | this.synchronizer.start() // eslint-disable-line @typescript-eslint/no-floating-promises 80 | } 81 | 82 | /** 83 | * Stop service. Interrupts blockchain synchronization if its in progress. 84 | * @return {Promise} 85 | */ 86 | async stop(): Promise { 87 | if (!this.running) { 88 | return false 89 | } 90 | await this.synchronizer.stop() 91 | await super.stop() 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /docs/interfaces/_sync_fetcher_fetcher_.fetcheroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["sync/fetcher/fetcher"](../modules/_sync_fetcher_fetcher_.md) › [FetcherOptions](_sync_fetcher_fetcher_.fetcheroptions.md) 2 | 3 | # Interface: FetcherOptions 4 | 5 | ## Hierarchy 6 | 7 | * **FetcherOptions** 8 | 9 | ↳ [BlockFetcherOptions](_sync_fetcher_blockfetcher_.blockfetcheroptions.md) 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [banTime](_sync_fetcher_fetcher_.fetcheroptions.md#optional-bantime) 16 | * [config](_sync_fetcher_fetcher_.fetcheroptions.md#config) 17 | * [interval](_sync_fetcher_fetcher_.fetcheroptions.md#optional-interval) 18 | * [maxPerRequest](_sync_fetcher_fetcher_.fetcheroptions.md#optional-maxperrequest) 19 | * [maxQueue](_sync_fetcher_fetcher_.fetcheroptions.md#optional-maxqueue) 20 | * [pool](_sync_fetcher_fetcher_.fetcheroptions.md#pool) 21 | * [timeout](_sync_fetcher_fetcher_.fetcheroptions.md#optional-timeout) 22 | 23 | ## Properties 24 | 25 | ### `Optional` banTime 26 | 27 | • **banTime**? : *undefined | number* 28 | 29 | *Defined in [lib/sync/fetcher/fetcher.ts:17](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L17)* 30 | 31 | ___ 32 | 33 | ### config 34 | 35 | • **config**: *[Config](../classes/_config_.config.md)* 36 | 37 | *Defined in [lib/sync/fetcher/fetcher.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L8)* 38 | 39 | ___ 40 | 41 | ### `Optional` interval 42 | 43 | • **interval**? : *undefined | number* 44 | 45 | *Defined in [lib/sync/fetcher/fetcher.ts:26](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L26)* 46 | 47 | ___ 48 | 49 | ### `Optional` maxPerRequest 50 | 51 | • **maxPerRequest**? : *undefined | number* 52 | 53 | *Defined in [lib/sync/fetcher/fetcher.ts:23](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L23)* 54 | 55 | ___ 56 | 57 | ### `Optional` maxQueue 58 | 59 | • **maxQueue**? : *undefined | number* 60 | 61 | *Defined in [lib/sync/fetcher/fetcher.ts:20](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L20)* 62 | 63 | ___ 64 | 65 | ### pool 66 | 67 | • **pool**: *[PeerPool](../classes/_net_peerpool_.peerpool.md)* 68 | 69 | *Defined in [lib/sync/fetcher/fetcher.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L11)* 70 | 71 | ___ 72 | 73 | ### `Optional` timeout 74 | 75 | • **timeout**? : *undefined | number* 76 | 77 | *Defined in [lib/sync/fetcher/fetcher.ts:14](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/sync/fetcher/fetcher.ts#L14)* 78 | -------------------------------------------------------------------------------- /test/integration/lightsync.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { wait, setup, destroy } from './util' 3 | 4 | tape('[Integration:LightSync]', async (t) => { 5 | t.test('should sync headers', async (t) => { 6 | const [remoteServer, remoteService] = await setup({ 7 | location: '127.0.0.2', 8 | height: 200, 9 | syncmode: 'full', 10 | }) 11 | const [localServer, localService] = await setup({ 12 | location: '127.0.0.1', 13 | height: 0, 14 | syncmode: 'light', 15 | }) 16 | localService.on('synchronized', async () => { 17 | t.equals(localService.chain.headers.height.toNumber(), 200, 'synced') 18 | await destroy(localServer, localService) 19 | await destroy(remoteServer, remoteService) 20 | t.end() 21 | }) 22 | await localServer.discover('remotePeer', '127.0.0.2') 23 | }) 24 | 25 | t.test('should not sync with stale peers', async (t) => { 26 | const [remoteServer, remoteService] = await setup({ 27 | location: '127.0.0.2', 28 | height: 9, 29 | syncmode: 'full', 30 | }) 31 | const [localServer, localService] = await setup({ 32 | location: '127.0.0.1', 33 | height: 10, 34 | syncmode: 'light', 35 | }) 36 | localService.on('synchronized', async () => { 37 | t.fail('synced with a stale peer') 38 | }) 39 | await localServer.discover('remotePeer', '127.0.0.2') 40 | await wait(100) 41 | await destroy(localServer, localService) 42 | await destroy(remoteServer, remoteService) 43 | t.pass('did not sync') 44 | t.end() 45 | }) 46 | 47 | t.test('should sync with best peer', async (t) => { 48 | const [remoteServer1, remoteService1] = await setup({ 49 | location: '127.0.0.2', 50 | height: 9, 51 | syncmode: 'full', 52 | }) 53 | const [remoteServer2, remoteService2] = await setup({ 54 | location: '127.0.0.3', 55 | height: 10, 56 | syncmode: 'full', 57 | }) 58 | const [localServer, localService] = await setup({ 59 | location: '127.0.0.1', 60 | height: 0, 61 | syncmode: 'light', 62 | }) 63 | await localService.synchronizer.stop() 64 | await localServer.discover('remotePeer1', '127.0.0.2') 65 | await localServer.discover('remotePeer2', '127.0.0.3') 66 | localService.on('synchronized', async () => { 67 | t.equals(localService.chain.headers.height.toNumber(), 10, 'synced with best peer') 68 | await destroy(localServer, localService) 69 | await destroy(remoteServer1, remoteService1) 70 | await destroy(remoteServer2, remoteService2) 71 | t.end() 72 | }) 73 | await localService.synchronizer.start() 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /docs/modules/_rpc_validation_.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/validation"](_rpc_validation_.md) 2 | 3 | # Module: "rpc/validation" 4 | 5 | ## Index 6 | 7 | ### Functions 8 | 9 | * [middleware](_rpc_validation_.md#middleware) 10 | 11 | ### Object literals 12 | 13 | * [validators](_rpc_validation_.md#const-validators) 14 | 15 | ## Functions 16 | 17 | ### middleware 18 | 19 | ▸ **middleware**(`method`: any, `requiredParamsCount`: number, `validators`: any[]): *any* 20 | 21 | *Defined in [lib/rpc/validation.ts:10](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/validation.ts#L10)* 22 | 23 | middleware for parameters validation 24 | 25 | **`memberof`** module:rpc 26 | 27 | **Parameters:** 28 | 29 | Name | Type | Default | Description | 30 | ------ | ------ | ------ | ------ | 31 | `method` | any | - | function to add middleware | 32 | `requiredParamsCount` | number | - | required parameters count | 33 | `validators` | any[] | [] | array of validator | 34 | 35 | **Returns:** *any* 36 | 37 | ## Object literals 38 | 39 | ### `Const` validators 40 | 41 | ### ▪ **validators**: *object* 42 | 43 | *Defined in [lib/rpc/validation.ts:38](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/validation.ts#L38)* 44 | 45 | **`memberof`** module:rpc 46 | 47 | ### blockHash 48 | 49 | ▸ **blockHash**(`params`: any[], `index`: number): *any* 50 | 51 | *Defined in [lib/rpc/validation.ts:68](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/validation.ts#L68)* 52 | 53 | hex validator to validate block hash 54 | 55 | **Parameters:** 56 | 57 | Name | Type | Description | 58 | ------ | ------ | ------ | 59 | `params` | any[] | parameters of method | 60 | `index` | number | index of parameter | 61 | 62 | **Returns:** *any* 63 | 64 | ### bool 65 | 66 | ▸ **bool**(`params`: any[], `index`: number): *any* 67 | 68 | *Defined in [lib/rpc/validation.ts:95](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/validation.ts#L95)* 69 | 70 | bool validator to check if type is boolean 71 | 72 | **Parameters:** 73 | 74 | Name | Type | Description | 75 | ------ | ------ | ------ | 76 | `params` | any[] | parameters of method | 77 | `index` | number | index of parameter | 78 | 79 | **Returns:** *any* 80 | 81 | ### hex 82 | 83 | ▸ **hex**(`params`: any[], `index`: number): *any* 84 | 85 | *Defined in [lib/rpc/validation.ts:44](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/validation.ts#L44)* 86 | 87 | hex validator to ensure has "0x" prefix 88 | 89 | **Parameters:** 90 | 91 | Name | Type | Description | 92 | ------ | ------ | ------ | 93 | `params` | any[] | parameters of method | 94 | `index` | number | index of parameter | 95 | 96 | **Returns:** *any* 97 | -------------------------------------------------------------------------------- /docs/interfaces/_net_server_libp2pserver_.libp2pserveroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/libp2pserver"](../modules/_net_server_libp2pserver_.md) › [Libp2pServerOptions](_net_server_libp2pserver_.libp2pserveroptions.md) 2 | 3 | # Interface: Libp2pServerOptions 4 | 5 | ## Hierarchy 6 | 7 | * [ServerOptions](_net_server_server_.serveroptions.md) 8 | 9 | ↳ **Libp2pServerOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [bootnodes](_net_server_libp2pserver_.libp2pserveroptions.md#optional-bootnodes) 16 | * [config](_net_server_libp2pserver_.libp2pserveroptions.md#config) 17 | * [key](_net_server_libp2pserver_.libp2pserveroptions.md#optional-key) 18 | * [multiaddrs](_net_server_libp2pserver_.libp2pserveroptions.md#optional-multiaddrs) 19 | * [refreshInterval](_net_server_libp2pserver_.libp2pserveroptions.md#optional-refreshinterval) 20 | 21 | ## Properties 22 | 23 | ### `Optional` bootnodes 24 | 25 | • **bootnodes**? : *[BootnodeLike](../modules/_types_.md#bootnodelike)* 26 | 27 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[bootnodes](_net_server_server_.serveroptions.md#optional-bootnodes)* 28 | 29 | *Defined in [lib/net/server/server.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L18)* 30 | 31 | ___ 32 | 33 | ### config 34 | 35 | • **config**: *[Config](../classes/_config_.config.md)* 36 | 37 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[config](_net_server_server_.serveroptions.md#config)* 38 | 39 | *Defined in [lib/net/server/server.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L9)* 40 | 41 | ___ 42 | 43 | ### `Optional` key 44 | 45 | • **key**? : *[KeyLike](../modules/_types_.md#keylike)* 46 | 47 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[key](_net_server_server_.serveroptions.md#optional-key)* 48 | 49 | *Defined in [lib/net/server/server.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L15)* 50 | 51 | ___ 52 | 53 | ### `Optional` multiaddrs 54 | 55 | • **multiaddrs**? : *[MultiaddrsLike](../modules/_types_.md#multiaddrslike)* 56 | 57 | *Defined in [lib/net/server/libp2pserver.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/libp2pserver.ts#L11)* 58 | 59 | ___ 60 | 61 | ### `Optional` refreshInterval 62 | 63 | • **refreshInterval**? : *undefined | number* 64 | 65 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[refreshInterval](_net_server_server_.serveroptions.md#optional-refreshinterval)* 66 | 67 | *Defined in [lib/net/server/server.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L12)* 68 | -------------------------------------------------------------------------------- /test/rpc/net/version.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import Common from '@ethereumjs/common' 3 | import { startRPC, createManager, createNode, baseSetup, params, baseRequest } from '../helpers' 4 | 5 | const method = 'net_version' 6 | 7 | function compareResult(t: any, result: any, chainId: any) { 8 | let msg = 'result should be a string' 9 | if (typeof result !== 'string') { 10 | throw new Error(msg) 11 | } else { 12 | t.pass(msg) 13 | } 14 | 15 | msg = 'result string should not be empty' 16 | if (result.length === 0) { 17 | throw new Error(msg) 18 | } else { 19 | t.pass(msg) 20 | } 21 | 22 | msg = `should be the correct chain ID (expected: ${chainId}, received: ${result})` 23 | if (result !== chainId) { 24 | throw new Error(msg) 25 | } else { 26 | t.pass(msg) 27 | } 28 | } 29 | 30 | tape(`${method}: call on ropsten`, (t) => { 31 | const manager = createManager( 32 | createNode({ opened: true, commonChain: new Common({ chain: 'ropsten' }) }) 33 | ) 34 | const server = startRPC(manager.getMethods()) 35 | 36 | const req = params(method, []) 37 | const expectRes = (res: any) => { 38 | const { result } = res.body 39 | compareResult(t, result, '3') 40 | } 41 | baseRequest(t, server, req, 200, expectRes) 42 | }) 43 | 44 | tape(`${method}: call on mainnet`, (t) => { 45 | const server = baseSetup() 46 | 47 | const req = params(method, []) 48 | const expectRes = (res: any) => { 49 | const { result } = res.body 50 | compareResult(t, result, '1') 51 | } 52 | baseRequest(t, server, req, 200, expectRes) 53 | }) 54 | 55 | tape(`${method}: call on rinkeby`, (t) => { 56 | const manager = createManager( 57 | createNode({ opened: true, commonChain: new Common({ chain: 'rinkeby' }) }) 58 | ) 59 | const server = startRPC(manager.getMethods()) 60 | 61 | const req = params(method, []) 62 | const expectRes = (res: any) => { 63 | const { result } = res.body 64 | compareResult(t, result, '4') 65 | } 66 | baseRequest(t, server, req, 200, expectRes) 67 | }) 68 | 69 | tape(`${method}: call on kovan`, (t) => { 70 | const manager = createManager( 71 | createNode({ opened: true, commonChain: new Common({ chain: 'kovan' }) }) 72 | ) 73 | const server = startRPC(manager.getMethods()) 74 | 75 | const req = params(method, []) 76 | const expectRes = (res: any) => { 77 | const { result } = res.body 78 | compareResult(t, result, '42') 79 | } 80 | baseRequest(t, server, req, 200, expectRes) 81 | }) 82 | 83 | tape(`${method}: call on goerli`, (t) => { 84 | const manager = createManager( 85 | createNode({ opened: true, commonChain: new Common({ chain: 'goerli' }) }) 86 | ) 87 | const server = startRPC(manager.getMethods()) 88 | 89 | const req = params(method, []) 90 | const expectRes = (res: any) => { 91 | const { result } = res.body 92 | compareResult(t, result, '5') 93 | } 94 | baseRequest(t, server, req, 200, expectRes) 95 | }) 96 | -------------------------------------------------------------------------------- /docs/interfaces/_net_server_rlpxserver_.rlpxserveroptions.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["net/server/rlpxserver"](../modules/_net_server_rlpxserver_.md) › [RlpxServerOptions](_net_server_rlpxserver_.rlpxserveroptions.md) 2 | 3 | # Interface: RlpxServerOptions 4 | 5 | ## Hierarchy 6 | 7 | * [ServerOptions](_net_server_server_.serveroptions.md) 8 | 9 | ↳ **RlpxServerOptions** 10 | 11 | ## Index 12 | 13 | ### Properties 14 | 15 | * [bootnodes](_net_server_rlpxserver_.rlpxserveroptions.md#optional-bootnodes) 16 | * [clientFilter](_net_server_rlpxserver_.rlpxserveroptions.md#optional-clientfilter) 17 | * [config](_net_server_rlpxserver_.rlpxserveroptions.md#config) 18 | * [key](_net_server_rlpxserver_.rlpxserveroptions.md#optional-key) 19 | * [port](_net_server_rlpxserver_.rlpxserveroptions.md#optional-port) 20 | * [refreshInterval](_net_server_rlpxserver_.rlpxserveroptions.md#optional-refreshinterval) 21 | 22 | ## Properties 23 | 24 | ### `Optional` bootnodes 25 | 26 | • **bootnodes**? : *[BootnodeLike](../modules/_types_.md#bootnodelike)* 27 | 28 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[bootnodes](_net_server_server_.serveroptions.md#optional-bootnodes)* 29 | 30 | *Defined in [lib/net/server/server.ts:18](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L18)* 31 | 32 | ___ 33 | 34 | ### `Optional` clientFilter 35 | 36 | • **clientFilter**? : *string[]* 37 | 38 | *Defined in [lib/net/server/rlpxserver.ts:11](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/rlpxserver.ts#L11)* 39 | 40 | ___ 41 | 42 | ### config 43 | 44 | • **config**: *[Config](../classes/_config_.config.md)* 45 | 46 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[config](_net_server_server_.serveroptions.md#config)* 47 | 48 | *Defined in [lib/net/server/server.ts:9](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L9)* 49 | 50 | ___ 51 | 52 | ### `Optional` key 53 | 54 | • **key**? : *[KeyLike](../modules/_types_.md#keylike)* 55 | 56 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[key](_net_server_server_.serveroptions.md#optional-key)* 57 | 58 | *Defined in [lib/net/server/server.ts:15](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L15)* 59 | 60 | ___ 61 | 62 | ### `Optional` port 63 | 64 | • **port**? : *undefined | number* 65 | 66 | *Defined in [lib/net/server/rlpxserver.ts:8](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/rlpxserver.ts#L8)* 67 | 68 | ___ 69 | 70 | ### `Optional` refreshInterval 71 | 72 | • **refreshInterval**? : *undefined | number* 73 | 74 | *Inherited from [ServerOptions](_net_server_server_.serveroptions.md).[refreshInterval](_net_server_server_.serveroptions.md#optional-refreshinterval)* 75 | 76 | *Defined in [lib/net/server/server.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/net/server/server.ts#L12)* 77 | -------------------------------------------------------------------------------- /test/sync/fetcher/blockfetcher.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { BN } from 'ethereumjs-util' 5 | import { Config } from '../../../lib/config' 6 | import { Chain } from '../../../lib/blockchain/chain' 7 | 8 | async function wait(delay?: number) { 9 | await new Promise((resolve) => setTimeout(resolve, delay ?? 10)) 10 | } 11 | 12 | tape('[BlockFetcher]', async (t) => { 13 | class PeerPool extends EventEmitter { 14 | idle() {} 15 | ban() {} 16 | } 17 | PeerPool.prototype.idle = td.func() 18 | PeerPool.prototype.ban = td.func() 19 | td.replace('../../lib/net/peerpool', { PeerPool }) 20 | 21 | const { BlockFetcher } = await import('../../../lib/sync/fetcher/blockfetcher') 22 | 23 | t.test('should start/stop', async (t) => { 24 | const config = new Config({ loglevel: 'error', transports: [] }) 25 | const pool = new PeerPool() as any 26 | const chain = new Chain({ config }) 27 | const fetcher = new BlockFetcher({ 28 | config, 29 | pool, 30 | chain, 31 | first: new BN(1), 32 | count: new BN(10), 33 | maxPerRequest: 5, 34 | timeout: 5, 35 | }) 36 | fetcher.next = () => false 37 | t.notOk((fetcher as any).running, 'not started') 38 | // eslint-disable-next-line @typescript-eslint/no-floating-promises 39 | fetcher.fetch() 40 | t.equals((fetcher as any).in.size(), 2, 'added 2 tasks') 41 | await wait() 42 | t.ok((fetcher as any).running, 'started') 43 | fetcher.destroy() 44 | await wait() 45 | t.notOk((fetcher as any).running, 'stopped') 46 | t.end() 47 | }) 48 | 49 | t.test('should process', (t) => { 50 | const config = new Config({ loglevel: 'error', transports: [] }) 51 | const pool = new PeerPool() as any 52 | const chain = new Chain({ config }) 53 | const fetcher = new BlockFetcher({ 54 | config, 55 | pool, 56 | chain, 57 | first: new BN(0), 58 | count: new BN(0), 59 | }) 60 | const blocks = [{ header: { number: 1 } }, { header: { number: 2 } }] 61 | t.deepEquals(fetcher.process({ task: { count: 2 } }, { blocks }), blocks, 'got results') 62 | t.notOk(fetcher.process({ task: { count: 2 } }, { blocks: [] }), 'bad results') 63 | t.end() 64 | }) 65 | 66 | t.test('should find a fetchable peer', async (t) => { 67 | const config = new Config({ loglevel: 'error', transports: [] }) 68 | const pool = new PeerPool() as any 69 | const chain = new Chain({ config }) 70 | const fetcher = new BlockFetcher({ 71 | config, 72 | pool, 73 | chain, 74 | first: new BN(0), 75 | count: new BN(0), 76 | }) 77 | td.when((fetcher as any).pool.idle(td.matchers.anything())).thenReturn('peer0') 78 | t.equals(fetcher.peer(undefined), 'peer0', 'found peer') 79 | t.end() 80 | }) 81 | 82 | t.test('should reset td', (t) => { 83 | td.reset() 84 | t.end() 85 | }) 86 | }) 87 | -------------------------------------------------------------------------------- /test/integration/peerpool.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { Config } from '../../lib/config' 3 | import { EthProtocol } from '../../lib/net/protocol' 4 | import { PeerPool } from '../../lib/net/peerpool' 5 | import MockServer from './mocks/mockserver' 6 | import MockChain from './mocks/mockchain' 7 | 8 | tape('[Integration:PeerPool]', async (t) => { 9 | async function setup(protocols: EthProtocol[] = []): Promise<[MockServer, PeerPool]> { 10 | const serverConfig = new Config({ loglevel: 'error' }) 11 | const server = new MockServer({ config: serverConfig }) as any 12 | server.addProtocols(protocols) 13 | const config = new Config({ servers: [server] }) 14 | await server.start() 15 | const pool = new PeerPool({ config }) 16 | await pool.open() 17 | return [server, pool] 18 | } 19 | 20 | async function destroy(server: MockServer, pool: PeerPool) { 21 | await server.stop() 22 | await pool.close() 23 | } 24 | 25 | t.test('should open', async (t) => { 26 | const [server, pool] = await setup() 27 | t.ok((pool as any).opened, 'opened') 28 | await destroy(server, pool) 29 | t.end() 30 | }) 31 | 32 | t.test('should add/remove peer', async (t) => { 33 | const [server, pool] = await setup() 34 | pool.on('added', (peer: any) => t.equal(peer.id, 'peer0', 'added peer')) 35 | pool.on('removed', (peer: any) => t.equal(peer.id, 'peer0', 'removed peer')) 36 | await server.accept('peer0') 37 | setTimeout(async () => { 38 | server.disconnect('peer0') 39 | await destroy(server, pool) 40 | t.pass('destroyed') 41 | t.end() 42 | }, 100) 43 | }) 44 | 45 | t.test('should ban peer', async (t) => { 46 | const [server, pool] = await setup() 47 | pool.on('added', (peer: any) => t.equal(peer.id, 'peer0', 'added peer')) 48 | pool.on('banned', (peer: any) => t.equal(peer.id, 'peer0', 'banned peer')) 49 | await server.accept('peer0') 50 | setTimeout(async () => { 51 | pool.ban(pool.peers[0]) 52 | await destroy(server, pool) 53 | t.pass('destroyed') 54 | t.end() 55 | }, 100) 56 | }) 57 | 58 | t.test('should handle peer messages', async (t) => { 59 | const config = new Config({ transports: [], loglevel: 'error' }) 60 | const chain = new MockChain({ config }) 61 | await chain.open() 62 | const protocols = [ 63 | new EthProtocol({ 64 | config, 65 | chain, 66 | }), 67 | ] 68 | const [server, pool] = await setup(protocols) 69 | pool.on('added', (peer: any) => t.equal(peer.id, 'peer0', 'added peer')) 70 | pool.on('message', (msg: any, proto: any, peer: any) => { 71 | t.deepEqual([msg, proto, peer.id], ['msg0', 'proto0', 'peer0'], 'got message') 72 | }) 73 | await server.accept('peer0') 74 | setTimeout(async () => { 75 | pool.peers[0].emit('message', 'msg0', 'proto0') 76 | await destroy(server, pool) 77 | t.pass('destroyed') 78 | t.end() 79 | }, 100) 80 | }) 81 | }) 82 | -------------------------------------------------------------------------------- /test/integration/mocks/mockserver.ts: -------------------------------------------------------------------------------- 1 | import { Server, ServerOptions } from '../../../lib/net/server' 2 | import MockPeer from './mockpeer' 3 | import * as network from './network' 4 | 5 | interface MockServerOptions extends ServerOptions { 6 | location?: string 7 | } 8 | 9 | export default class MockServer extends Server { 10 | public location: string 11 | public server: Server | null 12 | public peers: { [key: string]: MockPeer } 13 | 14 | constructor(options: MockServerOptions) { 15 | super(options) 16 | this.location = options.location ?? '127.0.0.1' 17 | this.server = null 18 | this.peers = {} 19 | } 20 | 21 | get name() { 22 | return 'mock' 23 | } 24 | 25 | async start(): Promise { 26 | if (this.started) { 27 | return false 28 | } 29 | await super.start() 30 | await this.wait(1) 31 | 32 | this.server = network.createServer(this.location) 33 | this.server!.on('listening', () => { 34 | this.emit('listening', { 35 | transport: this.name, 36 | url: `mock://${this.location}`, 37 | }) 38 | }) 39 | 40 | this.server!.on('connection', async (connection: any) => { 41 | await this.connect(connection) 42 | }) 43 | return true 44 | } 45 | 46 | async stop(): Promise { 47 | await new Promise((resolve) => { 48 | setTimeout(() => { 49 | network.destroyServer(this.location) 50 | resolve() 51 | }, 20) 52 | }) 53 | await super.stop() 54 | return this.started 55 | } 56 | 57 | async discover(id: string, location: string) { 58 | const opts = { config: this.config, address: 'mock', transport: 'mock' } 59 | const peer = new MockPeer({ id, location, protocols: Array.from(this.protocols), ...opts }) 60 | await peer.connect() 61 | this.peers[id] = peer 62 | this.emit('connected', peer) 63 | return peer 64 | } 65 | 66 | async accept(id: string) { 67 | const opts = { config: this.config, address: 'mock', transport: 'mock', location: 'mock' } 68 | const peer = new MockPeer({ id, protocols: Array.from(this.protocols), ...opts }) 69 | await peer.accept(this) 70 | return peer 71 | } 72 | 73 | async connect(connection: any) { 74 | const opts = { config: this.config, address: 'mock', transport: 'mock', location: 'mock' } 75 | const id = connection.remoteId 76 | const peer = new MockPeer({ 77 | id, 78 | inbound: true, 79 | server: this, 80 | protocols: Array.from(this.protocols), 81 | ...opts, 82 | }) 83 | await peer.bindProtocols(connection) 84 | this.peers[id] = peer 85 | this.emit('connected', peer) 86 | } 87 | 88 | disconnect(id: string) { 89 | const peer = this.peers[id] 90 | // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition 91 | if (peer) this.emit('disconnected', peer) 92 | } 93 | 94 | async wait(delay?: number) { 95 | await new Promise((resolve) => setTimeout(resolve, delay ?? 100)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /test/integration/mocks/network.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | 3 | const Pipe = function () { 4 | const buffer: any[] = [] 5 | let closed = false 6 | 7 | return { 8 | source: (end: any, cb: any) => { 9 | if (closed) end = true 10 | if (end) return cb(end) 11 | const next = function () { 12 | if (closed) return 13 | if (buffer.length) { 14 | setTimeout(() => { 15 | const data = buffer.shift() 16 | cb(null, data) 17 | }, 1) 18 | } else { 19 | setTimeout(next, 1) 20 | } 21 | } 22 | next() 23 | }, 24 | sink: (read: any) => { 25 | read(null, function next(end: any, data: any) { 26 | if (end === true || closed === true) return 27 | if (end) throw end 28 | buffer.push(data) 29 | read(null, next) 30 | }) 31 | }, 32 | close: () => { 33 | closed = true 34 | }, 35 | } 36 | } as any 37 | 38 | const Connection = function (protocols: any) { 39 | const outgoing = new Pipe() 40 | const incoming = new Pipe() 41 | 42 | return { 43 | local: (remoteId: any) => ({ 44 | source: incoming.source, 45 | sink: outgoing.sink, 46 | remoteId, 47 | protocols, 48 | }), 49 | remote: (location: any) => ({ 50 | source: outgoing.source, 51 | sink: incoming.sink, 52 | location, 53 | protocols, 54 | }), 55 | close: () => { 56 | incoming.close() 57 | outgoing.close() 58 | }, 59 | } 60 | } as any 61 | 62 | export const servers: any = {} 63 | 64 | export function createServer(location: any) { 65 | if (servers[location]) { 66 | throw new Error(`Already running a server at ${location}`) 67 | } 68 | servers[location] = { 69 | server: new EventEmitter(), 70 | connections: {}, 71 | } 72 | setTimeout(() => servers[location].server.emit('listening'), 10) 73 | return servers[location].server 74 | } 75 | 76 | export function destroyConnection(id: any, location: any) { 77 | if (servers[location]) { 78 | const conn = servers[location].connections[id] 79 | if (conn) { 80 | conn.close() 81 | delete servers[location].connections[id] 82 | } 83 | } 84 | } 85 | 86 | export function destroyServer(location: any) { 87 | if (servers[location]) { 88 | for (const id of Object.keys(servers[location].connections)) { 89 | destroyConnection(id, location) 90 | } 91 | } 92 | delete servers[location] 93 | } 94 | 95 | export function createConnection(id: any, location: any, protocols: any) { 96 | if (!servers[location]) { 97 | throw new Error(`There is no server at ${location}`) 98 | } 99 | const connection = new Connection(protocols) 100 | servers[location].connections[id] = connection 101 | setTimeout(() => servers[location].server.emit('connection', connection.local(id)), 10) 102 | return connection.remote(location) 103 | } 104 | -------------------------------------------------------------------------------- /test/rpc/helpers.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | const jayson = require('jayson') 3 | const request = require('supertest') 4 | import Common from '@ethereumjs/common' 5 | import { RPCManager as Manager } from '../../lib/rpc' 6 | import * as Logger from '../../lib/logging' 7 | import { blockChain } from './blockChainStub' 8 | import { Chain } from '../../lib/blockchain/chain' 9 | import { RlpxServer } from '../../lib/net/server/rlpxserver' 10 | import Blockchain from '@ethereumjs/blockchain' 11 | import { Config } from '../../lib/config' 12 | 13 | const config: any = { loglevel: 'error' } 14 | config.logger = Logger.getLogger(config) 15 | 16 | export function startRPC(methods: any, port: number = 3000) { 17 | const server = jayson.server(methods) 18 | const httpServer = server.http() 19 | httpServer.listen(port) 20 | return httpServer 21 | } 22 | 23 | export function closeRPC(server: any) { 24 | server.close() 25 | } 26 | 27 | export function createManager(node: any) { 28 | return new Manager(node, config) 29 | } 30 | 31 | export function createNode(nodeConfig?: any) { 32 | const config = new Config({ transports: [] }) 33 | const chain = new Chain({ 34 | config, 35 | blockchain: (blockChain({})) as Blockchain, 36 | }) 37 | chain.opened = true 38 | const defaultNodeConfig = { 39 | blockchain: chain, 40 | opened: true, 41 | commonChain: new Common({ chain: 'mainnet' }), 42 | ethProtocolVersions: [63], 43 | } 44 | const trueNodeConfig = { ...defaultNodeConfig, ...nodeConfig } 45 | const servers = [ 46 | new RlpxServer({ 47 | config, 48 | bootnodes: '10.0.0.1:1234,10.0.0.2:1234', 49 | }), 50 | ] 51 | return { 52 | services: [ 53 | { 54 | name: 'eth', 55 | chain: trueNodeConfig.blockchain, 56 | pool: { peers: [1, 2, 3] }, 57 | protocols: [ 58 | { 59 | name: 'eth', 60 | versions: trueNodeConfig.ethProtocolVersions, 61 | }, 62 | ], 63 | }, 64 | ], 65 | servers, 66 | common: trueNodeConfig.commonChain, 67 | opened: trueNodeConfig.opened, 68 | server: (name: string) => { 69 | return servers.find((s) => s.name === name) 70 | }, 71 | } 72 | } 73 | 74 | export function baseSetup() { 75 | const manager = createManager(createNode()) 76 | const server = startRPC(manager.getMethods()) 77 | return server 78 | } 79 | 80 | export function params(method: any, params: any[] = []) { 81 | const req = { 82 | jsonrpc: '2.0', 83 | method: method, 84 | params: params, 85 | id: 1, 86 | } 87 | return req 88 | } 89 | 90 | export function baseRequest(t: tape.Test, server: any, req: any, expect: any, expectRes: any) { 91 | request(server) 92 | .post('/') 93 | .set('Content-Type', 'application/json') 94 | .send(req) 95 | .expect(expect) 96 | .expect(expectRes) 97 | .end((err: any) => { 98 | closeRPC(server) 99 | t.end(err) 100 | }) 101 | } 102 | -------------------------------------------------------------------------------- /docs/classes/_rpc_modules_net_.net.md: -------------------------------------------------------------------------------- 1 | [ethereumjs-client](../README.md) › ["rpc/modules/net"](../modules/_rpc_modules_net_.md) › [Net](_rpc_modules_net_.net.md) 2 | 3 | # Class: Net 4 | 5 | net_* RPC module 6 | 7 | **`memberof`** module:rpc/modules 8 | 9 | ## Hierarchy 10 | 11 | * **Net** 12 | 13 | ## Index 14 | 15 | ### Constructors 16 | 17 | * [constructor](_rpc_modules_net_.net.md#constructor) 18 | 19 | ### Methods 20 | 21 | * [listening](_rpc_modules_net_.net.md#listening) 22 | * [peerCount](_rpc_modules_net_.net.md#peercount) 23 | * [version](_rpc_modules_net_.net.md#version) 24 | 25 | ## Constructors 26 | 27 | ### constructor 28 | 29 | \+ **new Net**(`node`: any): *[Net](_rpc_modules_net_.net.md)* 30 | 31 | *Defined in [lib/rpc/modules/net.ts:12](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/net.ts#L12)* 32 | 33 | Create net_* RPC module 34 | 35 | **Parameters:** 36 | 37 | Name | Type | 38 | ------ | ------ | 39 | `node` | any | 40 | 41 | **Returns:** *[Net](_rpc_modules_net_.net.md)* 42 | 43 | ## Methods 44 | 45 | ### listening 46 | 47 | ▸ **listening**(`_params`: never[], `cb`: function): *void* 48 | 49 | *Defined in [lib/rpc/modules/net.ts:45](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/net.ts#L45)* 50 | 51 | Returns true if client is actively listening for network connections 52 | 53 | **Parameters:** 54 | 55 | ▪`Default value` **_params**: *never[]*= [] 56 | 57 | ▪ **cb**: *function* 58 | 59 | ▸ (`err`: Error | null, `isListening`: boolean): *void* 60 | 61 | **Parameters:** 62 | 63 | Name | Type | 64 | ------ | ------ | 65 | `err` | Error | null | 66 | `isListening` | boolean | 67 | 68 | **Returns:** *void* 69 | 70 | ___ 71 | 72 | ### peerCount 73 | 74 | ▸ **peerCount**(`_params`: never[], `cb`: function): *void* 75 | 76 | *Defined in [lib/rpc/modules/net.ts:55](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/net.ts#L55)* 77 | 78 | Returns number of peers currently connected to the client 79 | 80 | **Parameters:** 81 | 82 | ▪`Default value` **_params**: *never[]*= [] 83 | 84 | ▪ **cb**: *function* 85 | 86 | ▸ (`err`: Error | null, `numberOfPeers`: string): *void* 87 | 88 | **Parameters:** 89 | 90 | Name | Type | 91 | ------ | ------ | 92 | `err` | Error | null | 93 | `numberOfPeers` | string | 94 | 95 | **Returns:** *void* 96 | 97 | ___ 98 | 99 | ### version 100 | 101 | ▸ **version**(`_params`: never[], `cb`: function): *void* 102 | 103 | *Defined in [lib/rpc/modules/net.ts:35](https://github.com/ethereumjs/ethereumjs-client/blob/master/lib/rpc/modules/net.ts#L35)* 104 | 105 | Returns the current network id 106 | 107 | **Parameters:** 108 | 109 | ▪`Default value` **_params**: *never[]*= [] 110 | 111 | ▪ **cb**: *function* 112 | 113 | ▸ (`err`: Error | null, `id`: string): *void* 114 | 115 | **Parameters:** 116 | 117 | Name | Type | 118 | ------ | ------ | 119 | `err` | Error | null | 120 | `id` | string | 121 | 122 | **Returns:** *void* 123 | -------------------------------------------------------------------------------- /test/client.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { Config } from '../lib/config' 5 | import { PeerPool } from '../lib/net/peerpool' 6 | 7 | tape('[EthereumClient]', async (t) => { 8 | const config = new Config({ transports: [], loglevel: 'error' }) 9 | class FullEthereumService extends EventEmitter { 10 | open() {} 11 | start() {} 12 | stop() {} 13 | config = config 14 | pool = new PeerPool({ config }) 15 | } 16 | FullEthereumService.prototype.open = td.func() 17 | FullEthereumService.prototype.start = td.func() 18 | FullEthereumService.prototype.stop = td.func() 19 | td.replace('../lib/service', { FullEthereumService }) 20 | td.when(FullEthereumService.prototype.open()).thenResolve() 21 | td.when(FullEthereumService.prototype.start()).thenResolve() 22 | td.when(FullEthereumService.prototype.stop()).thenResolve() 23 | 24 | class Server extends EventEmitter { 25 | open() {} 26 | start() {} 27 | stop() {} 28 | } 29 | Server.prototype.open = td.func() 30 | Server.prototype.start = td.func() 31 | Server.prototype.stop = td.func() 32 | td.replace('../lib/net/server/server', { Server }) 33 | td.when(Server.prototype.start()).thenResolve() 34 | td.when(Server.prototype.stop()).thenResolve() 35 | 36 | const { default: EthereumClient } = await import('../lib/client') 37 | 38 | t.test('should initialize correctly', (t) => { 39 | const config = new Config({ transports: [], loglevel: 'error' }) 40 | const client = new EthereumClient({ config }) 41 | t.ok(client.services[0] instanceof FullEthereumService, 'added service') 42 | t.end() 43 | }) 44 | 45 | t.test('should open', async (t) => { 46 | t.plan(6) 47 | const servers = [new Server()] as any 48 | const config = new Config({ servers }) 49 | const client = new EthereumClient({ config }) 50 | client.on('error', (err: string) => { 51 | if (err === 'err0') t.pass('got err0') 52 | if (err === 'err1') t.pass('got err1') 53 | }) 54 | client.on('listening', (details: string) => t.equals(details, 'details0', 'got listening')) 55 | client.on('synchronized', () => t.ok('got synchronized')) 56 | await client.open() 57 | servers[0].emit('error', 'err0') 58 | servers[0].emit('listening', 'details0') 59 | client.services[0].emit('error', 'err1') 60 | client.services[0].emit('synchronized') 61 | t.ok(client.opened, 'opened') 62 | t.equals(await client.open(), false, 'already opened') 63 | }) 64 | 65 | t.test('should start/stop', async (t) => { 66 | const servers = [new Server()] as any 67 | const config = new Config({ servers }) 68 | const client = new EthereumClient({ config }) 69 | await client.start() 70 | t.ok(client.started, 'started') 71 | t.equals(await client.start(), false, 'already started') 72 | await client.stop() 73 | t.notOk(client.started, 'stopped') 74 | t.equals(await client.stop(), false, 'already stopped') 75 | t.end() 76 | }) 77 | 78 | t.test('should reset td', (t) => { 79 | td.reset() 80 | t.end() 81 | }) 82 | }) 83 | -------------------------------------------------------------------------------- /test/rpc/eth/getBlockByNumber.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape' 2 | import { INVALID_PARAMS } from '../../../lib/rpc/error-code' 3 | import { startRPC, createManager, createNode, params, baseRequest } from '../helpers' 4 | import { checkError } from '../util' 5 | 6 | function createBlockchain() { 7 | const transactions = [ 8 | { 9 | hash: '0xc6ef2fc5426d6ad6fd9e2a26abeab0aa2411b7ab17f30a99d3cb96aed1d1055b', 10 | }, 11 | ] 12 | const block = { 13 | toJSON: () => ({ number: 1, transactions }), 14 | } 15 | return { 16 | getBlock: () => block, 17 | } 18 | } 19 | 20 | const method = 'eth_getBlockByNumber' 21 | 22 | tape(`${method}: call with valid arguments`, (t) => { 23 | const manager = createManager(createNode({ blockchain: createBlockchain() })) 24 | const server = startRPC(manager.getMethods()) 25 | 26 | const req = params(method, ['0x1', true]) 27 | const expectRes = (res: any) => { 28 | const msg = 'should return the correct number' 29 | if (res.body.result.number !== 1) { 30 | throw new Error(msg) 31 | } else { 32 | t.pass(msg) 33 | } 34 | } 35 | baseRequest(t, server, req, 200, expectRes) 36 | }) 37 | 38 | tape(`${method}: call with false for second argument`, (t) => { 39 | const manager = createManager(createNode({ blockchain: createBlockchain() })) 40 | const server = startRPC(manager.getMethods()) 41 | 42 | const req = params(method, ['0x1', false]) 43 | const expectRes = (res: any) => { 44 | let msg = 'should return the correct number' 45 | if (res.body.result.number !== 1) { 46 | throw new Error(msg) 47 | } else { 48 | t.pass(msg) 49 | } 50 | msg = 'should return only the hashes of the transactions' 51 | if (typeof res.body.result.transactions[0] !== 'string') { 52 | throw new Error(msg) 53 | } else { 54 | t.pass(msg) 55 | } 56 | } 57 | baseRequest(t, server, req, 200, expectRes) 58 | }) 59 | 60 | tape(`${method}: call with invalid block number`, (t) => { 61 | const manager = createManager(createNode({ blockchain: createBlockchain() })) 62 | const server = startRPC(manager.getMethods()) 63 | 64 | const req = params(method, ['WRONG BLOCK NUMBER', true]) 65 | const expectRes = checkError( 66 | t, 67 | INVALID_PARAMS, 68 | 'invalid argument 0: hex string without 0x prefix' 69 | ) 70 | baseRequest(t, server, req, 200, expectRes) 71 | }) 72 | 73 | tape(`${method}: call without second parameter`, (t) => { 74 | const manager = createManager(createNode({ blockchain: createBlockchain() })) 75 | const server = startRPC(manager.getMethods()) 76 | 77 | const req = params(method, ['0x0']) 78 | const expectRes = checkError(t, INVALID_PARAMS, 'missing value for required argument 1') 79 | baseRequest(t, server, req, 200, expectRes) 80 | }) 81 | 82 | tape(`${method}: call with invalid second parameter`, (t) => { 83 | const manager = createManager(createNode({ blockchain: createBlockchain() })) 84 | const server = startRPC(manager.getMethods()) 85 | 86 | const req = params(method, ['0x0', 'INVALID PARAMETER']) 87 | const expectRes = checkError(t, INVALID_PARAMS) 88 | baseRequest(t, server, req, 200, expectRes) 89 | }) 90 | -------------------------------------------------------------------------------- /lib/sync/sync.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import { PeerPool } from '../net/peerpool' 3 | import { Peer } from '../net/peer/peer' 4 | import { FlowControl } from '../net/protocol' 5 | import { Config } from '../config' 6 | import { Chain } from '../blockchain' 7 | 8 | export interface SynchronizerOptions { 9 | /* Config */ 10 | config: Config 11 | 12 | /* Peer pool */ 13 | pool: PeerPool 14 | 15 | /* Blockchain */ 16 | chain: Chain 17 | 18 | /* Flow control manager */ 19 | flow?: FlowControl 20 | 21 | /* Refresh interval in ms (default: 1000) */ 22 | interval?: number 23 | } 24 | 25 | /** 26 | * Base class for blockchain synchronizers 27 | * @memberof module:sync 28 | */ 29 | export class Synchronizer extends EventEmitter { 30 | public config: Config 31 | 32 | protected pool: PeerPool 33 | protected chain: Chain 34 | protected flow: FlowControl 35 | protected interval: number 36 | protected running: boolean 37 | protected forceSync: boolean 38 | 39 | /** 40 | * Create new node 41 | * @param {SynchronizerOptions} 42 | */ 43 | constructor(options: SynchronizerOptions) { 44 | super() 45 | 46 | this.config = options.config 47 | 48 | this.pool = options.pool 49 | this.chain = options.chain 50 | this.flow = options.flow ?? new FlowControl() 51 | this.interval = options.interval ?? 1000 52 | this.running = false 53 | this.forceSync = false 54 | 55 | this.pool.on('added', (peer: Peer) => { 56 | if (this.syncable(peer)) { 57 | this.config.logger.debug(`Found ${this.type} peer: ${peer}`) 58 | } 59 | }) 60 | } 61 | 62 | /** 63 | * Returns synchronizer type 64 | */ 65 | get type(): string { 66 | return 'sync' 67 | } 68 | 69 | /** 70 | * Open synchronizer. Must be called before sync() is called 71 | * @return {Promise} 72 | */ 73 | async open() {} 74 | 75 | /** 76 | * Returns true if peer can be used for syncing 77 | * @return {boolean} 78 | */ 79 | // TODO: evaluate syncability of peer 80 | syncable(_peer: any): boolean { 81 | return true 82 | } 83 | 84 | /** 85 | * Start synchronization 86 | */ 87 | async start(): Promise { 88 | if (this.running) { 89 | return false 90 | } 91 | this.running = true 92 | const timeout = setTimeout(() => { 93 | this.forceSync = true 94 | }, this.interval * 30) 95 | while (this.running) { 96 | try { 97 | if (await (this as any).sync()) this.emit('synchronized') 98 | } catch (error) { 99 | this.emit('error', error) 100 | } 101 | await new Promise((resolve) => setTimeout(resolve, this.interval)) 102 | } 103 | this.running = false 104 | clearTimeout(timeout) 105 | } 106 | 107 | /** 108 | * Stop synchronization. Returns a promise that resolves once stopped. 109 | */ 110 | async stop(): Promise { 111 | if (!this.running) { 112 | return false 113 | } 114 | await new Promise((resolve) => setTimeout(resolve, this.interval)) 115 | this.running = false 116 | return true 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /lib/rpc/validation.ts: -------------------------------------------------------------------------------- 1 | import { INVALID_PARAMS } from './error-code' 2 | 3 | /** 4 | * middleware for parameters validation 5 | * @memberof module:rpc 6 | * @param {Function} method function to add middleware 7 | * @param {number} requiredParamsCount required parameters count 8 | * @param {Function[]} validators array of validator 9 | */ 10 | export function middleware(method: any, requiredParamsCount: number, validators: any[] = []): any { 11 | return function (params: any[] = [], cb: (err: any, val?: any) => void) { 12 | if (params.length < requiredParamsCount) { 13 | const err = { 14 | code: INVALID_PARAMS, 15 | message: `missing value for required argument ${params.length}`, 16 | } 17 | return cb(err) 18 | } 19 | 20 | for (let i = 0; i < validators.length; i++) { 21 | if (validators[i]) { 22 | for (let j = 0; j < validators[i].length; j++) { 23 | const err = validators[i][j](params, i) 24 | if (err) { 25 | return cb(err) 26 | } 27 | } 28 | } 29 | } 30 | 31 | method(params, cb) 32 | } 33 | } 34 | 35 | /** 36 | * @memberof module:rpc 37 | */ 38 | export const validators = { 39 | /** 40 | * hex validator to ensure has "0x" prefix 41 | * @param {any[]} params parameters of method 42 | * @param {number} index index of parameter 43 | */ 44 | hex(params: any[], index: number): any { 45 | let err 46 | if (typeof params[index] !== 'string') { 47 | return { 48 | code: INVALID_PARAMS, 49 | message: `invalid argument ${index}: argument must be a hex string`, 50 | } 51 | } 52 | 53 | if (params[index].substr(0, 2) !== '0x') { 54 | err = { 55 | code: INVALID_PARAMS, 56 | message: `invalid argument ${index}: hex string without 0x prefix`, 57 | } 58 | } 59 | 60 | return err 61 | }, 62 | 63 | /** 64 | * hex validator to validate block hash 65 | * @param {any[]} params parameters of method 66 | * @param {number} index index of parameter 67 | */ 68 | blockHash(params: any[], index: number): any { 69 | let err 70 | 71 | if (typeof params[index] !== 'string') { 72 | return { 73 | code: INVALID_PARAMS, 74 | message: `invalid argument ${index}: argument must be a hex string`, 75 | } 76 | } 77 | 78 | const blockHash = params[index].substring(2) 79 | 80 | if (!/^[0-9a-fA-F]+$/.test(blockHash) || blockHash.length !== 64) { 81 | err = { 82 | code: INVALID_PARAMS, 83 | message: `invalid argument ${index}: invalid block hash`, 84 | } 85 | } 86 | 87 | return err 88 | }, 89 | 90 | /** 91 | * bool validator to check if type is boolean 92 | * @param {any[]} params parameters of method 93 | * @param {number} index index of parameter 94 | */ 95 | bool(params: any[], index: number): any { 96 | let err 97 | if (typeof params[index] !== 'boolean') { 98 | err = { 99 | code: INVALID_PARAMS, 100 | message: `invalid argument ${index}: argument is not boolean`, 101 | } 102 | } 103 | 104 | return err 105 | }, 106 | } 107 | -------------------------------------------------------------------------------- /lib/sync/fetcher/blockfetcher.ts: -------------------------------------------------------------------------------- 1 | import { Fetcher, FetcherOptions } from './fetcher' 2 | import { Block, BlockBodyBuffer } from '@ethereumjs/block' 3 | import { BN } from 'ethereumjs-util' 4 | import { Peer } from '../../net/peer' 5 | import { Chain } from '../../blockchain' 6 | 7 | export interface BlockFetcherOptions extends FetcherOptions { 8 | /* Blockchain */ 9 | chain: Chain 10 | 11 | /* Block number to start fetching from */ 12 | first: BN 13 | 14 | /* How many blocks to fetch */ 15 | count: BN 16 | } 17 | 18 | /** 19 | * Implements an eth/62 based block fetcher 20 | * @memberof module:sync/fetcher 21 | */ 22 | export class BlockFetcher extends Fetcher { 23 | protected chain: Chain 24 | protected first: BN 25 | protected count: BN 26 | 27 | /** 28 | * Create new block fetcher 29 | * @param {BlockFetcherOptions} 30 | */ 31 | constructor(options: BlockFetcherOptions) { 32 | super(options) 33 | 34 | this.chain = options.chain 35 | this.maxPerRequest = options.maxPerRequest ?? 128 36 | this.first = options.first 37 | this.count = options.count 38 | } 39 | 40 | /** 41 | * Generate list of tasks to fetch 42 | * @return {Object[]} tasks 43 | */ 44 | tasks(): object[] { 45 | const { first, count } = this 46 | const max = this.maxPerRequest 47 | const tasks = [] 48 | while (count.gten(max)) { 49 | tasks.push({ first: first.clone(), count: max }) 50 | first.iaddn(max) 51 | count.isubn(max) 52 | } 53 | if (count.gtn(0)) { 54 | tasks.push({ first: first.clone(), count: count.toNumber() }) 55 | } 56 | return tasks 57 | } 58 | 59 | /** 60 | * Requests blocks associated with this job 61 | * @param job 62 | */ 63 | async request(job: any): Promise { 64 | const { task, peer } = job 65 | const { first, count } = task 66 | const headers = await peer.eth.getBlockHeaders({ block: first, max: count }) 67 | const bodies = await peer.eth.getBlockBodies(headers.map((h: any) => h.hash())) 68 | const blocks = bodies.map(([txsData, unclesData]: BlockBodyBuffer, i: number) => 69 | Block.fromValuesArray([headers[i].raw(), txsData, unclesData], { common: this.config.common }) 70 | ) 71 | return { blocks } 72 | } 73 | 74 | /** 75 | * Process fetch result 76 | * @param job fetch job 77 | * @param result fetch result 78 | * @return {*} results of processing job or undefined if job not finished 79 | */ 80 | process(job: any, result: any) { 81 | if (result.blocks && result.blocks.length === job.task.count) { 82 | return result.blocks 83 | } 84 | } 85 | 86 | /** 87 | * Store fetch result. Resolves once store operation is complete. 88 | * @param {Block[]} blocks fetch result 89 | * @return {Promise} 90 | */ 91 | async store(blocks: Array) { 92 | await this.chain.putBlocks(blocks) 93 | } 94 | 95 | /** 96 | * Returns a peer that can process the given job 97 | * @param job job 98 | * @return {Peer} 99 | */ 100 | // TODO: find out what _job is supposed to be doing here... 101 | peer(_job: any): Peer { 102 | return this.pool.idle((p: any) => p.eth) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /test/sync/lightsync.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { BN } from 'ethereumjs-util' 5 | import { Config } from '../../lib/config' 6 | import { Chain } from '../../lib/blockchain' 7 | 8 | tape('[LightSynchronizer]', async (t) => { 9 | class PeerPool extends EventEmitter { 10 | open() {} 11 | close() {} 12 | } 13 | PeerPool.prototype.open = td.func() 14 | PeerPool.prototype.close = td.func() 15 | td.replace('../../lib/net/peerpool', { PeerPool }) 16 | class HeaderFetcher extends EventEmitter { 17 | fetch() {} 18 | } 19 | HeaderFetcher.prototype.fetch = td.func() 20 | td.replace('../../lib/sync/fetcher/headerfetcher', { HeaderFetcher }) 21 | 22 | const { LightSynchronizer } = await import('../../lib/sync/lightsync') 23 | 24 | t.test('should initialize correctly', async (t) => { 25 | const config = new Config({ loglevel: 'error', transports: [] }) 26 | const pool = new PeerPool() as any 27 | const chain = new Chain({ config }) 28 | const sync = new LightSynchronizer({ config, pool, chain }) 29 | pool.emit('added', { les: { status: { serveHeaders: true } } }) 30 | t.equals(sync.type, 'light', 'light type') 31 | t.end() 32 | }) 33 | 34 | t.test('should find best', async (t) => { 35 | const config = new Config({ loglevel: 'error', transports: [] }) 36 | const pool = new PeerPool() as any 37 | const chain = new Chain({ config }) 38 | const sync = new LightSynchronizer({ 39 | config, 40 | interval: 1, 41 | pool, 42 | chain, 43 | }) 44 | ;(sync as any).running = true 45 | ;(sync as any).chain = { headers: { td: new BN(1) } } 46 | const peers = [ 47 | { 48 | les: { status: { headTd: new BN(1), headNum: new BN(1), serveHeaders: 1 } }, 49 | inbound: false, 50 | }, 51 | { 52 | les: { status: { headTd: new BN(2), headNum: new BN(2), serveHeaders: 1 } }, 53 | inbound: false, 54 | }, 55 | ] 56 | ;(sync as any).pool = { peers } 57 | ;(sync as any).forceSync = true 58 | t.equals(sync.best(), peers[1], 'found best') 59 | t.end() 60 | }) 61 | 62 | t.test('should sync', async (t) => { 63 | t.plan(3) 64 | const config = new Config({ loglevel: 'error', transports: [] }) 65 | const pool = new PeerPool() as any 66 | const chain = new Chain({ config }) 67 | const sync = new LightSynchronizer({ 68 | config, 69 | interval: 1, 70 | pool, 71 | chain, 72 | }) 73 | sync.best = td.func() 74 | td.when(sync.best()).thenReturn({ les: { status: { headNum: new BN(2) } } } as any) 75 | td.when(HeaderFetcher.prototype.fetch(), { delay: 20 }).thenResolve(undefined) 76 | ;(sync as any).chain = { headers: { height: new BN(3) } } 77 | t.notOk(await sync.sync(), 'local height > remote height') 78 | ;(sync as any).chain = { headers: { height: new BN(0) } } 79 | t.ok(await sync.sync(), 'local height < remote height') 80 | td.when(HeaderFetcher.prototype.fetch()).thenReject(new Error('err0')) 81 | try { 82 | await sync.sync() 83 | } catch (err) { 84 | t.equals(err.message, 'err0', 'got error') 85 | } 86 | }) 87 | 88 | t.test('should reset td', (t) => { 89 | td.reset() 90 | t.end() 91 | }) 92 | }) 93 | -------------------------------------------------------------------------------- /lib/net/server/server.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import { Config } from '../../config' 3 | import { Bootnode, BootnodeLike, KeyLike } from '../../types' 4 | import { parseKey, parseBootnodes } from '../../util/parse' 5 | import { Protocol } from '../protocol/protocol' 6 | 7 | export interface ServerOptions { 8 | /* Config */ 9 | config: Config 10 | 11 | /* How often (in ms) to discover new peers (default: 30000) */ 12 | refreshInterval?: number 13 | 14 | /* Private key to use for server */ 15 | key?: KeyLike 16 | 17 | /* List of bootnodes to use for discovery */ 18 | bootnodes?: BootnodeLike 19 | } 20 | 21 | /** 22 | * Base class for transport specific server implementations. 23 | * @memberof module:net/server 24 | */ 25 | export class Server extends EventEmitter { 26 | public config: Config 27 | public key: Buffer | undefined 28 | public bootnodes: Bootnode[] = [] 29 | 30 | protected refreshInterval: number 31 | protected protocols: Set 32 | 33 | public started: boolean 34 | 35 | /** 36 | * Create new server 37 | * @param {ServerOptions} 38 | */ 39 | constructor(options: ServerOptions) { 40 | super() 41 | 42 | this.config = options.config 43 | this.key = options.key ? parseKey(options.key) : undefined 44 | this.bootnodes = options.bootnodes ? parseBootnodes(options.bootnodes) : [] 45 | this.refreshInterval = options.refreshInterval ?? 30000 46 | 47 | this.protocols = new Set() 48 | this.started = false 49 | } 50 | 51 | get name(): string { 52 | return this.constructor.name 53 | } 54 | 55 | /** 56 | * Check if server is running 57 | */ 58 | get running(): boolean { 59 | return this.started 60 | } 61 | 62 | /** 63 | * Start server. Returns a promise that resolves once server has been started. 64 | * @return Resolves with true if server successfully started 65 | */ 66 | async start(): Promise { 67 | if (this.started) { 68 | return false 69 | } 70 | const protocols: Protocol[] = Array.from(this.protocols) 71 | await Promise.all(protocols.map((p) => p.open())) 72 | this.started = true 73 | this.config.logger.info(`Started ${this.name} server.`) 74 | 75 | return true 76 | } 77 | 78 | /** 79 | * Stop server. Returns a promise that resolves once server has been stopped. 80 | */ 81 | async stop(): Promise { 82 | this.started = false 83 | this.config.logger.info(`Stopped ${this.name} server.`) 84 | return this.started 85 | } 86 | 87 | /** 88 | * Specify which protocols the server must support 89 | * @param protocols protocol classes 90 | * @return True if protocol added successfully 91 | */ 92 | addProtocols(protocols: Protocol[]): boolean { 93 | if (this.started) { 94 | this.config.logger.error('Cannot require protocols after server has been started') 95 | return false 96 | } 97 | protocols.forEach((p) => this.protocols.add(p)) 98 | return true 99 | } 100 | 101 | /** 102 | * Ban peer for a specified time 103 | * @protected 104 | * @param peerId id of peer 105 | * @param maxAge how long to ban peer 106 | * @return {Promise} 107 | */ 108 | ban(_peerId: string, _maxAge: number) { 109 | // don't do anything by default 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /test/service/lightethereumservice.spec.ts: -------------------------------------------------------------------------------- 1 | import { EventEmitter } from 'events' 2 | import tape from 'tape-catch' 3 | import td from 'testdouble' 4 | import { Config } from '../../lib/config' 5 | 6 | tape('[LightEthereumService]', async (t) => { 7 | class PeerPool extends EventEmitter { 8 | open() {} 9 | close() {} 10 | } 11 | PeerPool.prototype.open = td.func() 12 | PeerPool.prototype.close = td.func() 13 | td.replace('../../lib/net/peerpool', { PeerPool }) 14 | const Chain = td.constructor([] as any) 15 | Chain.prototype.open = td.func() 16 | td.replace('../../lib/blockchain', { Chain }) 17 | const LesProtocol = td.constructor([] as any) 18 | td.replace('../../lib/net/protocol/lesprotocol', { LesProtocol }) 19 | class LightSynchronizer extends EventEmitter { 20 | start() {} 21 | stop() {} 22 | open() {} 23 | } 24 | LightSynchronizer.prototype.start = td.func() 25 | LightSynchronizer.prototype.stop = td.func() 26 | LightSynchronizer.prototype.open = td.func() 27 | td.replace('../../lib/sync/lightsync', { LightSynchronizer }) 28 | 29 | const { LightEthereumService } = await import('../../lib/service/lightethereumservice') 30 | 31 | t.test('should initialize correctly', async (t) => { 32 | const config = new Config({ transports: [], loglevel: 'error' }) 33 | const service = new LightEthereumService({ config }) 34 | t.ok(service.synchronizer instanceof LightSynchronizer, 'light sync') 35 | t.equals(service.name, 'eth', 'got name') 36 | t.end() 37 | }) 38 | 39 | t.test('should get protocols', async (t) => { 40 | const config = new Config({ transports: [], loglevel: 'error' }) 41 | const service = new LightEthereumService({ config }) 42 | t.ok(service.protocols[0] instanceof LesProtocol, 'light protocols') 43 | t.end() 44 | }) 45 | 46 | t.test('should open', async (t) => { 47 | t.plan(3) 48 | const server = td.object() as any 49 | const config = new Config({ servers: [server], loglevel: 'error' }) 50 | const service = new LightEthereumService({ config }) 51 | await service.open() 52 | td.verify(service.chain.open()) 53 | td.verify(service.synchronizer.open()) 54 | td.verify(server.addProtocols(td.matchers.anything())) 55 | service.on('synchronized', () => t.pass('synchronized')) 56 | service.once('error', (err: string) => t.equals(err, 'error0', 'got error 1')) 57 | service.synchronizer.emit('synchronized') 58 | service.synchronizer.emit('error', 'error0') 59 | service.once('error', (err: string) => t.equals(err, 'error1', 'got error 2')) 60 | service.pool.emit('banned', 'peer0') 61 | service.pool.emit('added', 'peer0') 62 | service.pool.emit('removed', 'peer0') 63 | service.pool.emit('error', 'error1') 64 | }) 65 | 66 | t.test('should start/stop', async (t) => { 67 | const server = td.object() as any 68 | const config = new Config({ servers: [server], loglevel: 'error' }) 69 | const service = new LightEthereumService({ config }) 70 | await service.start() 71 | td.verify(service.synchronizer.start()) 72 | t.notOk(await service.start(), 'already started') 73 | await service.stop() 74 | td.verify(service.synchronizer.stop()) 75 | td.verify(server.start()) 76 | t.notOk(await service.stop(), 'already stopped') 77 | t.end() 78 | }) 79 | 80 | t.test('should reset td', (t) => { 81 | td.reset() 82 | t.end() 83 | }) 84 | }) 85 | -------------------------------------------------------------------------------- /lib/net/protocol/ethprotocol.ts: -------------------------------------------------------------------------------- 1 | import { BN, bufferToInt } from 'ethereumjs-util' 2 | import { BlockHeader, BlockHeaderBuffer } from '@ethereumjs/block' 3 | import { Chain } from './../../blockchain' 4 | import { Message, Protocol, ProtocolOptions } from './protocol' 5 | 6 | interface EthProtocolOptions extends ProtocolOptions { 7 | /* Blockchain */ 8 | chain: Chain 9 | } 10 | 11 | const messages: Message[] = [ 12 | { 13 | name: 'NewBlockHashes', 14 | code: 0x01, 15 | encode: (hashes: any[]) => hashes.map((hn) => [hn[0], hn[1].toArrayLike(Buffer)]), 16 | decode: (hashes: any[]) => hashes.map((hn) => [hn[0], new BN(hn[1])]), 17 | }, 18 | { 19 | name: 'GetBlockHeaders', 20 | code: 0x03, 21 | response: 0x04, 22 | encode: ({ block, max, skip = 0, reverse = 0 }: any) => [ 23 | BN.isBN(block) ? block.toArrayLike(Buffer) : block, 24 | max, 25 | skip, 26 | reverse, 27 | ], 28 | decode: ([block, max, skip, reverse]: any) => ({ 29 | block: block.length === 32 ? block : new BN(block), 30 | max: bufferToInt(max), 31 | skip: bufferToInt(skip), 32 | reverse: bufferToInt(reverse), 33 | }), 34 | }, 35 | { 36 | name: 'BlockHeaders', 37 | code: 0x04, 38 | encode: (headers: BlockHeader[]) => headers.map((h) => h.raw()), 39 | decode: (headers: BlockHeaderBuffer[]) => 40 | headers.map((h) => BlockHeader.fromValuesArray(h, {})), 41 | }, 42 | { 43 | name: 'GetBlockBodies', 44 | code: 0x05, 45 | response: 0x06, 46 | }, 47 | { 48 | name: 'BlockBodies', 49 | code: 0x06, 50 | }, 51 | ] 52 | 53 | /** 54 | * Implements eth/62 and eth/63 protocols 55 | * @memberof module:net/protocol 56 | */ 57 | export class EthProtocol extends Protocol { 58 | private chain: Chain 59 | 60 | /** 61 | * Create eth protocol 62 | * @param {EthProtocolOptions} 63 | */ 64 | constructor(options: EthProtocolOptions) { 65 | super(options) 66 | 67 | this.chain = options.chain 68 | } 69 | 70 | /** 71 | * Name of protocol 72 | * @type {string} 73 | */ 74 | get name(): string { 75 | return 'eth' 76 | } 77 | 78 | /** 79 | * Protocol versions supported 80 | * @type {number[]} 81 | */ 82 | get versions(): number[] { 83 | return [63, 62] 84 | } 85 | 86 | /** 87 | * Messages defined by this protocol 88 | * @type {Protocol~Message[]} 89 | */ 90 | get messages(): Message[] { 91 | return messages 92 | } 93 | 94 | /** 95 | * Opens protocol and any associated dependencies 96 | * @return {Promise} 97 | */ 98 | async open(): Promise { 99 | if (this.opened) { 100 | return false 101 | } 102 | await this.chain.open() 103 | this.opened = true 104 | } 105 | 106 | /** 107 | * Encodes status into ETH status message payload 108 | * @return {Object} 109 | */ 110 | encodeStatus(): any { 111 | return { 112 | networkId: this.chain.networkId, 113 | td: this.chain.blocks.td.toArrayLike(Buffer), 114 | bestHash: this.chain.blocks.latest!.hash(), 115 | genesisHash: this.chain.genesis.hash, 116 | } 117 | } 118 | 119 | /** 120 | * Decodes ETH status message payload into a status object 121 | * @param {Object} status status message payload 122 | * @return {Object} 123 | */ 124 | decodeStatus(status: any): any { 125 | return { 126 | networkId: bufferToInt(status.networkId), 127 | td: new BN(status.td), 128 | bestHash: status.bestHash, 129 | genesisHash: status.genesisHash, 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /test/net/protocol/lesprotocol.spec.ts: -------------------------------------------------------------------------------- 1 | import tape from 'tape-catch' 2 | import { BN } from 'ethereumjs-util' 3 | import { Chain } from '../../../lib/blockchain' 4 | import { Config } from '../../../lib/config' 5 | import { FlowControl, LesProtocol } from '../../../lib/net/protocol' 6 | 7 | tape('[LesProtocol]', (t) => { 8 | t.test('should get properties', (t) => { 9 | const config = new Config({ transports: [], loglevel: 'error' }) 10 | const chain = new Chain({ config }) 11 | const p = new LesProtocol({ config, chain }) 12 | t.ok(typeof p.name === 'string', 'get name') 13 | t.ok(Array.isArray(p.versions), 'get versions') 14 | t.ok(Array.isArray(p.messages), 'get messages') 15 | t.end() 16 | }) 17 | 18 | t.test('should open correctly', async (t) => { 19 | const config = new Config({ transports: [], loglevel: 'error' }) 20 | const chain = new Chain({ config }) 21 | const p = new LesProtocol({ config, chain }) 22 | await p.open() 23 | t.ok(p.opened, 'opened is true') 24 | t.notOk(await p.open(), 'repeat open') 25 | t.end() 26 | }) 27 | 28 | t.test('should encode/decode status', (t) => { 29 | const config = new Config({ transports: [], loglevel: 'error' }) 30 | const chain = new Chain({ config }) 31 | const flow = new FlowControl({ 32 | bl: 1000, 33 | mrr: 10, 34 | mrc: { GetBlockHeaders: { base: 10, req: 10 } }, 35 | }) 36 | const p = new LesProtocol({ config, chain, flow }) 37 | Object.defineProperty(chain, 'networkId', { 38 | get: () => { 39 | return 1 40 | }, 41 | }) 42 | Object.defineProperty(chain, 'blocks', { 43 | get: () => { 44 | return { 45 | td: new BN(100), 46 | latest: { hash: () => '0xaa' }, 47 | } 48 | }, 49 | }) 50 | Object.defineProperty(chain, 'genesis', { 51 | get: () => { 52 | return { hash: '0xbb' } 53 | }, 54 | }) 55 | Object.defineProperty(chain, 'headers', { 56 | get: () => { 57 | return { 58 | td: new BN(100), 59 | latest: { 60 | hash: () => '0xaa', 61 | number: new BN(100), 62 | }, 63 | } 64 | }, 65 | }) 66 | let status = p.encodeStatus() 67 | t.ok( 68 | status.networkId === 1 && 69 | status.headTd.toString('hex') === '64' && 70 | status.headHash === '0xaa' && 71 | status.headNum.toNumber() === 100 && 72 | status.genesisHash === '0xbb' && 73 | status.serveHeaders === 1 && 74 | status.serveChainSince === 0 && 75 | status.serveStateSince === 0 && 76 | status.txRelay === 1 && 77 | status['flowControl/BL'].toString('hex') === '03e8' && 78 | status['flowControl/MRR'].toString('hex') === '0a' && 79 | status['flowControl/MRC'][0].toString() === '2,10,10', 80 | 'encode status' 81 | ) 82 | status = { ...status, networkId: [0x01] } 83 | status = p.decodeStatus(status) 84 | t.ok( 85 | status.networkId === 1 && 86 | status.headTd.toString('hex') === '64' && 87 | status.headHash === '0xaa' && 88 | status.headNum.toNumber() === 100 && 89 | status.genesisHash === '0xbb' && 90 | status.serveHeaders === true && 91 | status.serveChainSince === 0 && 92 | status.serveStateSince === 0 && 93 | status.txRelay === true && 94 | status.bl === 1000 && 95 | status.mrr === 10 && 96 | status.mrc['2'].base === 10 && 97 | status.mrc['2'].req === 10 && 98 | status.mrc.GetBlockHeaders.base === 10 && 99 | status.mrc.GetBlockHeaders.req === 10, 100 | 'decode status' 101 | ) 102 | t.end() 103 | }) 104 | }) 105 | --------------------------------------------------------------------------------