Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 0 additions & 2 deletions .github/workflows/trie-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,6 @@ jobs:
output-file-path: ${{ env.cwd }}/output.txt
# Location of data in gh-pages branch
benchmark-data-dir-path: dev/bench/trie
# Enable alert commit comment
comment-on-alert: true
# GitHub API token to make a commit comment
github-token: ${{ secrets.GITHUB_TOKEN }}
# Push and deploy to GitHub pages branch automatically (if on master)
Expand Down
5 changes: 4 additions & 1 deletion packages/client/bin/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,14 @@ async function run() {
}

const common = new Common({ chain, hardfork: 'chainstart' })
const datadir = args.datadir ?? Config.DATADIR_DEFAULT
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't make this a blocker, but this shouldn't be necessary since datadir has a local CLI-specific default (first was afraid that this would actually change the default, but this is not the case)? 🤔

const key = await Config.getClientKey(datadir, common)
const config = new Config({
common,
syncmode: args.syncmode,
lightserv: args.lightserv,
datadir: args.datadir,
datadir,
key,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder though if we want to make the name of this option a bit more expressive?

transports: args.transports,
bootnodes: args.bootnodes ? parseMultiaddrs(args.bootnodes) : undefined,
multiaddrs: args.multiaddrs ? parseMultiaddrs(args.multiaddrs) : undefined,
Expand Down
12 changes: 8 additions & 4 deletions packages/client/browser/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,23 @@ import { Config } from '../lib/config'
export * from './logging'
import { getLogger } from './logging'

export function createClient(args: any) {
export async function createClient(args: any) {
const logger = getLogger({ loglevel: args.loglevel ?? 'info' })
const datadir = args.datadir ?? Config.DATADIR_DEFAULT
const common = new Common({ chain: args.network ?? 'mainnet' })
const key = await Config.getClientKey(datadir, common)
const config = new Config({
common: new Common({ chain: args.network ?? 'mainnet' }),
servers: [new Libp2pServer({ multiaddrs: [], config: new Config({ logger }), ...args })],
common,
key,
servers: [new Libp2pServer({ multiaddrs: [], config: new Config({ key, logger }), ...args })],
syncmode: args.syncmode ?? 'full',
logger,
})
return new EthereumClient({ config })
}

export async function run(args: any) {
const client = createClient(args)
const client = await createClient(args)
const { logger, chainCommon: common } = client.config
logger.info('Initializing Ethereumjs client...')
logger.info(`Connecting to network: ${common.chainName()}`)
Expand Down
62 changes: 54 additions & 8 deletions packages/client/lib/config.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
import Common from '@ethereumjs/common'
import VM from '@ethereumjs/vm'
import { genPrivateKey } from '@ethereumjs/devp2p'
import Multiaddr from 'multiaddr'
import { getLogger, Logger } from './logging'
import { Libp2pServer, RlpxServer } from './net/server'
import { parseTransports } from './util'
import type { LevelUp } from 'levelup'
const level = require('level')

export interface ConfigOptions {
/**
Expand Down Expand Up @@ -40,6 +43,13 @@ export interface ConfigOptions {
*/
datadir?: string

/**
* Private key for the client.
* Use return value of `await Config.getClientKey(datadir, common)`
* If left blank, a random key will be generated and used.
*/
key?: Buffer

/**
* Network transports ('rlpx' and/or 'libp2p')
*
Expand Down Expand Up @@ -172,6 +182,7 @@ export class Config {
public readonly vm?: VM
public readonly lightserv: boolean
public readonly datadir: string
public readonly key: Buffer
public readonly transports: string[]
public readonly bootnodes?: Multiaddr[]
public readonly multiaddrs?: Multiaddr[]
Expand Down Expand Up @@ -199,6 +210,7 @@ export class Config {
this.bootnodes = options.bootnodes
this.multiaddrs = options.multiaddrs
this.datadir = options.datadir ?? Config.DATADIR_DEFAULT
this.key = options.key ?? genPrivateKey()
this.rpc = options.rpc ?? Config.RPC_DEFAULT
this.rpcport = options.rpcport ?? Config.RPCPORT_DEFAULT
this.rpcaddr = options.rpcaddr ?? Config.RPCADDR_DEFAULT
Expand Down Expand Up @@ -243,25 +255,33 @@ export class Config {
const bootnodes = this.bootnodes ?? this.chainCommon.bootstrapNodes()
const dnsNetworks = options.dnsNetworks ?? this.chainCommon.dnsNetworks()
return new RlpxServer({ config: this, bootnodes, dnsNetworks })
} else {
// t.name === 'libp2p'
} else if (t.name === 'libp2p') {
const multiaddrs = this.multiaddrs
const bootnodes = this.bootnodes
return new Libp2pServer({ config: this, multiaddrs, bootnodes })
} else {
throw new Error(`unknown transport: ${t.name}`)
}
})
}
}

/**
* Returns the network directory for the chain.
*/
getNetworkDirectory(): string {
const networkDirName = this.chainCommon.chainName()
const dataDir = `${this.datadir}/${networkDirName}`
return dataDir
}

/**
* Returns the directory for storing the client chain data
* based on syncmode and selected chain (subdirectory of 'datadir')
*/
getChainDataDirectory(): string {
const networkDirName = this.chainCommon.chainName()
const chainDataDirName = this.syncmode === 'light' ? 'lightchain' : 'chain'

const dataDir = `${this.datadir}/${networkDirName}/${chainDataDirName}`
const dataDir = `${this.getNetworkDirectory()}/${chainDataDirName}`
return dataDir
}

Expand All @@ -270,10 +290,36 @@ export class Config {
* based selected chain (subdirectory of 'datadir')
*/
getStateDataDirectory(): string {
const networkDirName = this.chainCommon.chainName()
return `${this.getNetworkDirectory()}/state`
}

const dataDir = `${this.datadir}/${networkDirName}/state`
return dataDir
/**
* Returns the config level db.
*/
static getConfigDB(networkDir: string): LevelUp {
const db = level(`${networkDir}/config` as any)
return db
}

/**
* Gets the client private key from the config db.
*/
static async getClientKey(datadir: string, common: Common) {
const networkDir = `${datadir}/${common.chainName()}`
const db = this.getConfigDB(networkDir)
const encodingOpts = { keyEncoding: 'utf8', valueEncoding: 'binary' }
const dbKey = 'config:client_key'
let key
try {
key = await db.get(dbKey, encodingOpts)
} catch (error) {
if (error.type === 'NotFoundError') {
// generate and save a new key
key = genPrivateKey()
await db.put(dbKey, key, encodingOpts)
}
}
return key
}

/**
Expand Down
9 changes: 6 additions & 3 deletions packages/client/lib/net/server/libp2pserver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PeerId from 'peer-id'
import crypto from 'libp2p-crypto'
import multiaddr from 'multiaddr'
import { Libp2pConnection as Connection } from '../../types'
import { Libp2pNode } from '../peer/libp2pnode'
Expand Down Expand Up @@ -55,7 +56,7 @@ export class Libp2pServer extends Server {
let peerId: PeerId
await super.start()
if (!this.node) {
peerId = await this.createPeerId()
peerId = await this.getPeerId()
const addresses = { listen: this.multiaddrs.map((ma) => ma.toString()) }
this.node = new Libp2pNode({
peerId,
Expand Down Expand Up @@ -153,8 +154,10 @@ export class Libp2pServer extends Server {
this.emit('error', error)
}

async createPeerId() {
return this.key ? PeerId.createFromPrivKey(this.key) : PeerId.create()
async getPeerId() {
const privKey = await crypto.keys.generateKeyPairFromSeed('ed25519', this.key, 512)
const protoBuf = crypto.keys.marshalPrivateKey(privKey)
return PeerId.createFromPrivKey(protoBuf)
}

getPeerInfo(connection: Connection): [PeerId, multiaddr] {
Expand Down
5 changes: 2 additions & 3 deletions packages/client/lib/net/server/rlpxserver.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { randomBytes } from 'crypto'
import { RLPx as Devp2pRLPx, Peer as Devp2pRLPxPeer, DPT as Devp2pDPT } from '@ethereumjs/devp2p'
import { RlpxPeer } from '../peer/rlpxpeer'
import { Server, ServerOptions } from './server'
Expand Down Expand Up @@ -215,7 +214,7 @@ export class RlpxServer extends Server {
* @private
*/
initDpt() {
this.dpt = new Devp2pDPT(this.key ?? randomBytes(32), {
this.dpt = new Devp2pDPT(this.key, {
refreshInterval: this.refreshInterval,
endpoint: {
address: '0.0.0.0',
Expand All @@ -241,7 +240,7 @@ export class RlpxServer extends Server {
* @private
*/
initRlpx() {
this.rlpx = new Devp2pRLPx(this.key ?? randomBytes(32), {
this.rlpx = new Devp2pRLPx(this.key, {
dpt: this.dpt!,
maxPeers: this.config.maxPeers,
capabilities: RlpxPeer.capabilities(Array.from(this.protocols)),
Expand Down
4 changes: 2 additions & 2 deletions packages/client/lib/net/server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export interface ServerOptions {
*/
export class Server extends EventEmitter {
public config: Config
public key: Buffer | undefined
public key: Buffer
public bootnodes: multiaddr[] = []
public dnsNetworks: DnsNetwork[]

Expand All @@ -45,7 +45,7 @@ export class Server extends EventEmitter {
super()

this.config = options.config
this.key = options.key ? parseKey(options.key) : undefined
this.key = options.key ? parseKey(options.key) : this.config.key
this.bootnodes = options.bootnodes ? parseMultiaddrs(options.bootnodes) : []
this.dnsNetworks = options.dnsNetworks ?? []
this.refreshInterval = options.refreshInterval ?? 30000
Expand Down
34 changes: 3 additions & 31 deletions packages/client/test/net/server/libp2pserver.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import multiaddr from 'multiaddr'
import { Config } from '../../../lib/config'

tape('[Libp2pServer]', async (t) => {
const PeerId = td.replace('peer-id')

const Libp2pPeer = td.replace('../../../lib/net/peer/libp2ppeer')
Libp2pPeer.id = 'id0'

Expand Down Expand Up @@ -36,16 +34,6 @@ tape('[Libp2pServer]', async (t) => {
td.when(Libp2pNode.prototype.start()).thenResolve()
td.when(Libp2pNode.prototype.stop()).thenResolve()

td.when(PeerId.create()).thenResolve('id0')
td.when(PeerId.createFromPrivKey(Buffer.from('1'))).thenResolve('id1')
td.when(PeerId.createFromPrivKey(Buffer.from('2'))).thenResolve('id2')
td.when(PeerId.createFromPrivKey(Buffer.from('3'))).thenReject(new Error('err0'))
td.when(PeerId.createFromPrivKey(Buffer.from('4'))).thenResolve({
toB58String: () => {
return 'id4'
},
})

const { Libp2pServer } = await import('../../../lib/net/server/libp2pserver')

t.test('should initialize correctly', async (t) => {
Expand All @@ -71,24 +59,6 @@ tape('[Libp2pServer]', async (t) => {
t.end()
})

t.test('should create peer id', async (t) => {
const config = new Config({ transports: [] })
const multiaddrs = [multiaddr('/ip4/6.6.6.6')]
let server = new Libp2pServer({ config, multiaddrs })
t.equals(await server.createPeerId(), 'id0', 'created')
server = new Libp2pServer({ config, multiaddrs, key: Buffer.from('1') })
t.equals(await server.createPeerId(), 'id1', 'created with id')
server = new Libp2pServer({ config, multiaddrs, key: Buffer.from('2') })
t.equals(await server.createPeerId(), 'id2', 'created with id')
server = new Libp2pServer({ config, multiaddrs, key: Buffer.from('3') })
try {
await server.createPeerId()
} catch (err) {
t.equals(err.message, 'err0', 'handle error')
}
t.end()
})

t.test('should get peer info', async (t) => {
const config = new Config({ transports: [] })
const server = new Libp2pServer({ config })
Expand Down Expand Up @@ -130,6 +100,7 @@ tape('[Libp2pServer]', async (t) => {
})
server.createPeer = td.func<typeof server['createPeer']>()
server.getPeerInfo = td.func<typeof server['getPeerInfo']>()
server.getPeerId = td.func<typeof server['getPeerId']>()
const peerId = {
toB58String() {
return 'id'
Expand All @@ -145,14 +116,15 @@ tape('[Libp2pServer]', async (t) => {
return 'id3'
},
} as any
td.when(server.getPeerId()).thenResolve(peerId)
td.when(server.getPeerInfo(conn0)).thenReturn([peerId])
td.when(server.getPeerInfo(conn1)).thenReturn([peerId2])
td.when(server.createPeer(peerId2)).thenReturn(peer2)
td.when(peer.accept(protos[0], 'conn0', server)).thenResolve(null)
;(server as any).peers.set('id', peer)
server.addProtocols(protos)
server.on('listening', (info: any) =>
t.deepEquals(info, { transport: 'libp2p', url: 'ma0/p2p/id4' }, 'listening')
t.deepEquals(info, { transport: 'libp2p', url: 'ma0/p2p/id' }, 'listening')
)
server.once('connected', (p: any) => t.equals(p, peer, 'peer connected'))
server.on('error', (err: Error) => t.equals(err.message, 'err0', 'got err0'))
Expand Down