Skip to content

Commit 504afef

Browse files
committed
Peer & BoundProtocol: add type safety with EthProtocolMethods and LesProtocolMethods
1 parent b3b1baa commit 504afef

File tree

11 files changed

+57
-36
lines changed

11 files changed

+57
-36
lines changed

packages/client/lib/net/peer/peer.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
import * as events from 'events'
2-
import { Protocol, BoundProtocol, Sender } from '../protocol'
2+
import {
3+
Protocol,
4+
BoundProtocol,
5+
EthProtocolMethods,
6+
LesProtocolMethods,
7+
Sender,
8+
} from '../protocol'
39
import { Server } from '../server'
410
import { Config } from '../../config'
511

@@ -42,8 +48,8 @@ export class Peer extends events.EventEmitter {
4248
private _idle: boolean
4349

4450
// Dynamically bound protocol properties
45-
public les: BoundProtocol | undefined
46-
public eth: BoundProtocol | undefined
51+
public eth: (BoundProtocol & EthProtocolMethods) | undefined
52+
public les: (BoundProtocol & LesProtocolMethods) | undefined
4753

4854
/**
4955
* Create new peer

packages/client/lib/net/protocol/boundprotocol.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,16 @@ import { Sender } from './sender'
55
import { Config } from '../../config'
66

77
export interface BoundProtocolOptions {
8+
/* Config */
89
config: Config
910

11+
/* Protocol */
1012
protocol: Protocol
1113

14+
/* Peer */
1215
peer: Peer
1316

17+
/* Sender */
1418
sender: Sender
1519
}
1620

@@ -155,19 +159,15 @@ export class BoundProtocol extends EventEmitter {
155159
}
156160

157161
/**
158-
* Add a methods to the bound protocol for each protocol message that has a
159-
* corresponding response message
162+
* Add methods to the bound protocol for each protocol message that has a
163+
* corresponding response message.
160164
*/
161165
addMethods() {
162166
const messages = this.protocol.messages.filter((m: any) => m.response)
163167
for (const message of messages) {
164168
const name = message.name as string
165169
const camel = name[0].toLowerCase() + name.slice(1)
166-
167-
// Problem: How to dynamically add class properties in TS
168-
// https://stackoverflow.com/questions/41038812
169-
// @ts-ignore: implicit 'any'
170-
this[name] = this[camel] = async (args: any[]) => this.request(name, args)
170+
;(this as any)[camel] = async (args: any[]) => this.request(name, args)
171171
}
172172
}
173173
}

packages/client/lib/net/protocol/ethprotocol.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { BN, bufferToInt } from 'ethereumjs-util'
2-
import { BlockHeader, BlockHeaderBuffer } from '@ethereumjs/block'
2+
import { Block, BlockHeader, BlockHeaderBuffer } from '@ethereumjs/block'
33
import { Chain } from './../../blockchain'
44
import { Message, Protocol, ProtocolOptions } from './protocol'
55

@@ -8,6 +8,17 @@ interface EthProtocolOptions extends ProtocolOptions {
88
chain: Chain
99
}
1010

11+
/* Messages with responses that are added as methods in camelCase to BoundProtocol. */
12+
export interface EthProtocolMethods {
13+
getBlockHeaders: (opts: {
14+
block: BN | Buffer
15+
max: number
16+
skip?: number
17+
reverse?: number
18+
}) => Promise<BlockHeader[]>
19+
getBlockBodies: (hashes: Buffer[]) => Promise<Block[]>
20+
}
21+
1122
const messages: Message[] = [
1223
{
1324
name: 'NewBlockHashes',

packages/client/lib/net/protocol/flowcontrol.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Peer } from '../peer/peer'
2-
import { BoundProtocol } from './boundprotocol'
32

43
interface Mrc {
54
[key: string]: {
@@ -62,10 +61,10 @@ export class FlowControl {
6261
*/
6362
maxRequestCount(peer: Peer, messageName: string): number {
6463
const now = Date.now()
65-
const mrcBase = (peer.les as BoundProtocol).status.mrc[messageName].base
66-
const mrcReq = (peer.les as BoundProtocol).status.mrc[messageName].req
67-
const mrr = (peer.les as BoundProtocol).status.mrr
68-
const bl = (peer.les as BoundProtocol).status.bl
64+
const mrcBase = peer.les!.status.mrc[messageName].base
65+
const mrcReq = peer.les!.status.mrc[messageName].req
66+
const mrr = peer.les!.status.mrr
67+
const bl = peer.les!.status.bl
6968
const params = this.in.get(peer.id) ?? ({ ble: bl } as FlowParams)
7069
if (params.last) {
7170
// recharge BLE at rate of MRR when less than BL

packages/client/lib/net/protocol/lesprotocol.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,17 @@ export interface LesProtocolOptions extends ProtocolOptions {
1212
flow?: FlowControl
1313
}
1414

15+
/* Messages with responses that are added as methods in camelCase to BoundProtocol. */
16+
export interface LesProtocolMethods {
17+
getBlockHeaders: (opts: {
18+
reqId?: BN
19+
block: BN | Buffer
20+
max: number
21+
skip?: number
22+
reverse?: number
23+
}) => Promise<{ reqId: BN; bv: BN; headers: BlockHeader[] }>
24+
}
25+
1526
const id = new BN(0)
1627

1728
const messages: Message[] = [

packages/client/lib/service/fullethereumservice.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { FullSynchronizer } from '../sync/fullsync'
33
import { EthProtocol } from '../net/protocol/ethprotocol'
44
import { LesProtocol } from '../net/protocol/lesprotocol'
55
import { Peer } from '../net/peer/peer'
6-
import { Protocol, BoundProtocol } from '../net/protocol'
6+
import { Protocol } from '../net/protocol'
77

88
interface FullEthereumServiceOptions extends EthereumServiceOptions {
99
/* Serve LES requests (default: false) */
@@ -85,12 +85,12 @@ export class FullEthereumService extends EthereumService {
8585
if (message.name === 'GetBlockHeaders') {
8686
const { block, max, skip, reverse } = message.data
8787
const headers: any = await this.chain.getHeaders(block, max, skip, reverse)
88-
;(peer.eth as BoundProtocol).send('BlockHeaders', headers)
88+
peer.eth!.send('BlockHeaders', headers)
8989
} else if (message.name === 'GetBlockBodies') {
9090
const hashes = message.data
9191
const blocks = await Promise.all(hashes.map((hash: any) => this.chain.getBlock(hash)))
9292
const bodies: any = blocks.map((block: any) => block.raw().slice(1))
93-
;(peer.eth as BoundProtocol).send('BlockBodies', bodies)
93+
peer.eth!.send('BlockBodies', bodies)
9494
} else if (message.name === 'NewBlockHashes') {
9595
await this.synchronizer.announced(message.data, peer)
9696
}
@@ -110,7 +110,7 @@ export class FullEthereumService extends EthereumService {
110110
this.config.logger.debug(`Dropping peer for violating flow control ${peer}`)
111111
} else {
112112
const headers: any = await this.chain.getHeaders(block, max, skip, reverse)
113-
;(peer.les as BoundProtocol).send('BlockHeaders', { reqId, bv, headers })
113+
peer.les!.send('BlockHeaders', { reqId, bv, headers })
114114
}
115115
}
116116
}

packages/client/lib/sync/fullsync.ts

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { BN } from 'ethereumjs-util'
22
import VM from '@ethereumjs/vm'
33
import { Peer } from '../net/peer/peer'
4-
import { BoundProtocol } from '../net/protocol'
54
import { short } from '../util'
65
import { Synchronizer, SynchronizerOptions } from './sync'
76
import { BlockFetcher } from './fetcher'
@@ -43,8 +42,6 @@ export class FullSynchronizer extends Synchronizer {
4342
* Returns true if peer can be used for syncing
4443
* @return {boolean}
4544
*/
46-
// TODO: Correct logic for return type (b/c peer.eth is not boolean)
47-
// "Type 'BoundProtocol | undefined' is not assignable to type 'boolean'"
4845
syncable(peer: Peer): boolean {
4946
return peer.eth !== undefined
5047
}
@@ -58,7 +55,7 @@ export class FullSynchronizer extends Synchronizer {
5855
const peers = this.pool.peers.filter(this.syncable.bind(this))
5956
if (peers.length < this.config.minPeers && !this.forceSync) return
6057
for (const peer of peers) {
61-
if (peer.eth && peer.eth.status) {
58+
if (peer.eth?.status) {
6259
const td = peer.eth.status.td
6360
if (
6461
(!best && td.gte((this.chain.blocks as any).td)) ||
@@ -76,9 +73,8 @@ export class FullSynchronizer extends Synchronizer {
7673
* @return {Promise} Resolves with header
7774
*/
7875
async latest(peer: Peer) {
79-
// @ts-ignore
80-
const headers = await (peer.eth as BoundProtocol).getBlockHeaders({
81-
block: (peer.eth as BoundProtocol).status.bestHash,
76+
const headers = await peer.eth!.getBlockHeaders({
77+
block: peer.eth!.status.bestHash,
8278
max: 1,
8379
})
8480
return headers[0]

packages/client/lib/sync/lightsync.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { Peer } from '../net/peer/peer'
2-
import { BoundProtocol } from '../net/protocol'
32
import { Synchronizer, SynchronizerOptions } from './sync'
43
import { HeaderFetcher } from './fetcher/headerfetcher'
54
import { BN } from 'ethereumjs-util'
@@ -30,7 +29,7 @@ export class LightSynchronizer extends Synchronizer {
3029
* @return {boolean}
3130
*/
3231
syncable(peer: Peer): boolean {
33-
return peer.les && peer.les.status.serveHeaders
32+
return peer.les?.status.serveHeaders
3433
}
3534

3635
/**
@@ -62,7 +61,7 @@ export class LightSynchronizer extends Synchronizer {
6261
*/
6362
async syncWithPeer(peer?: Peer): Promise<boolean> {
6463
if (!peer) return false
65-
const height = new BN((peer.les as BoundProtocol).status.headNum)
64+
const height = new BN(peer.les!.status.headNum)
6665
const first = ((this.chain.headers as any).height as BN).addn(1)
6766
const count = height.sub(first).addn(1)
6867
if (count.lten(0)) return false

packages/client/test/integration/fullethereumservice.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,15 @@ tape('[Integration:FullEthereumService]', async (t) => {
2626
t.test('should handle ETH requests', async (t) => {
2727
const [server, service] = await setup()
2828
const peer = await server.accept('peer0')
29-
const headers = await (peer.eth as any).getBlockHeaders({ block: 1, max: 2 })
29+
const headers = await peer.eth!.getBlockHeaders({ block: new BN(1), max: 2 })
3030
const hash = Buffer.from(
3131
'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069',
3232
'hex'
3333
)
3434
t.ok(headers[1].hash().equals(hash), 'handled GetBlockHeaders')
35-
const bodies = await (peer.eth as any).getBlockBodies([hash])
35+
const bodies = await peer.eth!.getBlockBodies([hash])
3636
t.deepEquals(bodies, [[[], []]], 'handled GetBlockBodies')
37-
await (peer.eth as any).send('NewBlockHashes', [[hash, new BN(2)]])
37+
await peer.eth!.send('NewBlockHashes', [[hash, new BN(2)]])
3838
t.pass('handled NewBlockHashes')
3939
await destroy(server, service)
4040
t.end()
@@ -43,7 +43,7 @@ tape('[Integration:FullEthereumService]', async (t) => {
4343
t.test('should handle LES requests', async (t) => {
4444
const [server, service] = await setup()
4545
const peer = await server.accept('peer0')
46-
const { headers } = await (peer.les as any).getBlockHeaders({ block: 1, max: 2 })
46+
const { headers } = await peer.les!.getBlockHeaders({ block: new BN(1), max: 2 })
4747
t.equals(
4848
headers[1].hash().toString('hex'),
4949
'a321d27cd2743617c1c1b0d7ecb607dd14febcdfca8f01b79c3f0249505ea069',

packages/client/test/net/protocol/boundprotocol.spec.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ tape('[BoundProtocol]', (t) => {
3434
sender,
3535
})
3636
t.ok(/this.request/.test((bound as any).testMessage.toString()), 'added testMessage')
37-
t.ok(/this.request/.test((bound as any).TestMessage.toString()), 'added TestMessage')
3837
t.end()
3938
})
4039

0 commit comments

Comments
 (0)