Skip to content
This repository was archived by the owner on Jun 26, 2023. It is now read-only.

Commit ec81622

Browse files
authored
fix: simplify transport interface, update interfaces for use with libp2p (#180)
* Ensure implementation of interfaces (e.g. `@libp2p/connection` implements `Connection` now) * Adds mocks for missing components (connection encrypter, etc) * Introduce `Components` class to hold system components that modules require access too - this means system components do not need to depend on `libp2p` any more * Add `Initializable` interface to allow injecting `Components` instance at runtime * Make naming of interfaces less cryptic - e.g. `Muxer` becomes `StreamMuxer`, etc * Introduces peer collections - lists, maps and sets keyed by `PeerId` instead of strings * Adds `.trace` field to `@libp2p/logger` for detailed logging * Pubsub implementations are now expected to serialize their own RPC representations * PeerIds now only stringify to `base58btc`, since a stringified PeerId is a multihash digest encoded in base58btc
1 parent eab83be commit ec81622

File tree

119 files changed

+2752
-2432
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+2752
-2432
lines changed

packages/libp2p-connection/package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -154,13 +154,13 @@
154154
"test:electron-main": "npm run test -- -t electron-main"
155155
},
156156
"dependencies": {
157-
"@libp2p/interfaces": "^1.3.12",
157+
"@libp2p/interfaces": "^1.3.0",
158158
"@multiformats/multiaddr": "^10.1.5",
159159
"err-code": "^3.0.1"
160160
},
161161
"devDependencies": {
162-
"@libp2p/interface-compliance-tests": "^1.1.14",
163-
"@libp2p/peer-id-factory": "^1.0.6",
162+
"@libp2p/interface-compliance-tests": "^1.1.0",
163+
"@libp2p/peer-id-factory": "^1.0.0",
164164
"aegir": "^36.1.3",
165165
"it-pair": "^2.0.2"
166166
}

packages/libp2p-connection/src/index.ts

Lines changed: 18 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,12 @@
11
import type { Multiaddr } from '@multiformats/multiaddr'
22
import errCode from 'err-code'
33
import { OPEN, CLOSING, CLOSED } from '@libp2p/interfaces/connection/status'
4-
import type { ConnectionStat, Metadata, ProtocolStream, Stream } from '@libp2p/interfaces/connection'
4+
import { symbol } from '@libp2p/interfaces/connection'
5+
import type { Connection, ConnectionStat, Metadata, ProtocolStream, Stream } from '@libp2p/interfaces/connection'
56
import type { PeerId } from '@libp2p/interfaces/peer-id'
67

7-
const connectionSymbol = Symbol.for('@libp2p/interface-connection/connection')
8-
98
interface ConnectionInit {
10-
localAddr: Multiaddr
119
remoteAddr: Multiaddr
12-
localPeer: PeerId
1310
remotePeer: PeerId
1411
newStream: (protocols: string[]) => Promise<ProtocolStream>
1512
close: () => Promise<void>
@@ -21,23 +18,15 @@ interface ConnectionInit {
2118
* An implementation of the js-libp2p connection.
2219
* Any libp2p transport should use an upgrader to return this connection.
2320
*/
24-
export class Connection {
21+
export class ConnectionImpl implements Connection {
2522
/**
2623
* Connection identifier.
2724
*/
2825
public readonly id: string
29-
/**
30-
* Observed multiaddr of the local peer
31-
*/
32-
public readonly localAddr: Multiaddr
3326
/**
3427
* Observed multiaddr of the remote peer
3528
*/
3629
public readonly remoteAddr: Multiaddr
37-
/**
38-
* Local peer id
39-
*/
40-
public readonly localPeer: PeerId
4130
/**
4231
* Remote peer id
4332
*/
@@ -75,12 +64,10 @@ export class Connection {
7564
* Any libp2p transport should use an upgrader to return this connection.
7665
*/
7766
constructor (init: ConnectionInit) {
78-
const { localAddr, remoteAddr, localPeer, remotePeer, newStream, close, getStreams, stat } = init
67+
const { remoteAddr, remotePeer, newStream, close, getStreams, stat } = init
7968

8069
this.id = `${(parseInt(String(Math.random() * 1e9))).toString(36)}${Date.now()}`
81-
this.localAddr = localAddr
8270
this.remoteAddr = remoteAddr
83-
this.localPeer = localPeer
8471
this.remotePeer = remotePeer
8572
this.stat = {
8673
...stat,
@@ -98,17 +85,10 @@ export class Connection {
9885
return 'Connection'
9986
}
10087

101-
get [connectionSymbol] () {
88+
get [symbol] () {
10289
return true
10390
}
10491

105-
/**
106-
* Checks if the given value is a `Connection` instance
107-
*/
108-
static isConnection (other: any) {
109-
return Boolean(connectionSymbol in other)
110-
}
111-
11292
/**
11393
* Get all the streams of the muxer
11494
*/
@@ -119,7 +99,7 @@ export class Connection {
11999
/**
120100
* Create a new stream from this connection
121101
*/
122-
async newStream (protocols: string[]) {
102+
async newStream (protocols: string | string[]) {
123103
if (this.stat.status === CLOSING) {
124104
throw errCode(new Error('the connection is being closed'), 'ERR_CONNECTION_BEING_CLOSED')
125105
}
@@ -128,7 +108,9 @@ export class Connection {
128108
throw errCode(new Error('the connection is closed'), 'ERR_CONNECTION_CLOSED')
129109
}
130110

131-
if (!Array.isArray(protocols)) protocols = [protocols]
111+
if (!Array.isArray(protocols)) {
112+
protocols = [protocols]
113+
}
132114

133115
const { stream, protocol } = await this._newStream(protocols)
134116

@@ -143,9 +125,12 @@ export class Connection {
143125
/**
144126
* Add a stream when it is opened to the registry
145127
*/
146-
addStream (stream: Stream, metadata: Metadata) {
128+
addStream (stream: Stream, metadata: Partial<Metadata> = {}) {
147129
// Add metadata for the stream
148-
this.registry.set(stream.id, metadata)
130+
this.registry.set(stream.id, {
131+
protocol: metadata.protocol ?? '',
132+
metadata: metadata.metadata ?? {}
133+
})
149134
}
150135

151136
/**
@@ -174,3 +159,7 @@ export class Connection {
174159
this.stat.status = CLOSED
175160
}
176161
}
162+
163+
export function createConnection (init: ConnectionInit): Connection {
164+
return new ConnectionImpl(init)
165+
}

packages/libp2p-connection/test/compliance.spec.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import tests from '@libp2p/interface-compliance-tests/connection'
2-
import { Connection } from '../src/index.js'
2+
import { createConnection } from '../src/index.js'
33
import peers from '@libp2p/interface-compliance-tests/utils/peers'
44
import * as PeerIdFactory from '@libp2p/peer-id-factory'
55
import { Multiaddr } from '@multiformats/multiaddr'
@@ -13,19 +13,13 @@ describe('compliance tests', () => {
1313
* certain values for testing.
1414
*/
1515
async setup (properties) {
16-
const localAddr = new Multiaddr('/ip4/127.0.0.1/tcp/8080')
1716
const remoteAddr = new Multiaddr('/ip4/127.0.0.1/tcp/8081')
18-
const [localPeer, remotePeer] = await Promise.all([
19-
PeerIdFactory.createFromJSON(peers[0]),
20-
PeerIdFactory.createFromJSON(peers[1])
21-
])
17+
const remotePeer = await PeerIdFactory.createFromJSON(peers[0])
2218
const openStreams: Stream[] = []
2319
let streamId = 0
2420

25-
const connection = new Connection({
26-
localPeer,
21+
const connection = createConnection({
2722
remotePeer,
28-
localAddr,
2923
remoteAddr,
3024
stat: {
3125
timeline: {

packages/libp2p-connection/test/index.spec.ts

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Connection } from '../src/index.js'
1+
import { createConnection } from '../src/index.js'
22
import * as PeerIdFactory from '@libp2p/peer-id-factory'
33
import { pair } from 'it-pair'
44
import { Multiaddr } from '@multiformats/multiaddr'
@@ -32,15 +32,12 @@ const peers = [{
3232

3333
describe('connection tests', () => {
3434
it('should not require local or remote addrs', async () => {
35-
const localPeer = await PeerIdFactory.createFromJSON(peers[0])
3635
const remotePeer = await PeerIdFactory.createFromJSON(peers[1])
3736

3837
const openStreams: any[] = []
3938
let streamId = 0
4039

41-
return new Connection({
42-
localPeer,
43-
localAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4001'),
40+
return createConnection({
4441
remotePeer,
4542
remoteAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4002'),
4643
stat: {

packages/libp2p-interface-compliance-tests/package.json

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,13 +198,13 @@
198198
"test:electron-main": "npm run test -- -t electron-main"
199199
},
200200
"dependencies": {
201-
"@libp2p/crypto": "^0.22.7",
202-
"@libp2p/interfaces": "^1.3.12",
201+
"@libp2p/crypto": "^0.22.8",
202+
"@libp2p/interfaces": "^1.3.0",
203203
"@libp2p/logger": "^1.1.0",
204-
"@libp2p/multistream-select": "^1.0.1",
205-
"@libp2p/peer-id": "^1.1.6",
206-
"@libp2p/peer-id-factory": "^1.0.6",
207-
"@libp2p/pubsub": "^1.2.8",
204+
"@libp2p/multistream-select": "^1.0.0",
205+
"@libp2p/peer-id": "^1.1.0",
206+
"@libp2p/peer-id-factory": "^1.0.0",
207+
"@libp2p/pubsub": "^1.2.0",
208208
"@multiformats/multiaddr": "^10.1.5",
209209
"abortable-iterator": "^4.0.2",
210210
"aegir": "^36.1.3",
@@ -216,7 +216,7 @@
216216
"it-goodbye": "^4.0.1",
217217
"it-map": "^1.0.6",
218218
"it-ndjson": "^0.1.1",
219-
"it-pair": "^2.0.0",
219+
"it-pair": "^2.0.2",
220220
"it-pipe": "^2.0.3",
221221
"it-pushable": "^2.0.1",
222222
"it-stream-types": "^1.0.4",

packages/libp2p-interface-compliance-tests/src/connection-encrypter/index.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import peers from '../utils/peers.js'
77
import { UnexpectedPeerError } from '@libp2p/interfaces/connection-encrypter/errors'
88
import { createMaConnPair } from './utils/index.js'
99
import type { TestSetup } from '../index.js'
10-
import type { Encrypter } from '@libp2p/interfaces/connection-encrypter'
10+
import type { ConnectionEncrypter } from '@libp2p/interfaces/connection-encrypter'
1111
import type { PeerId } from '@libp2p/interfaces/peer-id'
1212
import type { Source } from 'it-stream-types'
1313

14-
export default (common: TestSetup<Encrypter>) => {
14+
export default (common: TestSetup<ConnectionEncrypter>) => {
1515
describe('interface-connection-encrypter compliance tests', () => {
16-
let crypto: Encrypter
16+
let crypto: ConnectionEncrypter
1717
let localPeer: PeerId
1818
let remotePeer: PeerId
1919
let mitmPeer: PeerId
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
import { peerIdFromBytes } from '@libp2p/peer-id'
2+
import { handshake } from 'it-handshake'
3+
import { duplexPair } from 'it-pair/duplex'
4+
import { pipe } from 'it-pipe'
5+
import { UnexpectedPeerError } from '@libp2p/interfaces/connection-encrypter/errors'
6+
import { Multiaddr } from '@multiformats/multiaddr'
7+
import type { ConnectionEncrypter } from '@libp2p/interfaces/connection-encrypter'
8+
import type { Transform, Source } from 'it-stream-types'
9+
10+
// A basic transform that does nothing to the data
11+
const transform = (): Transform<Uint8Array, Uint8Array> => {
12+
return (source: Source<Uint8Array>) => (async function * () {
13+
for await (const chunk of source) {
14+
yield chunk
15+
}
16+
})()
17+
}
18+
19+
export function mockConnectionEncrypter () {
20+
const encrypter: ConnectionEncrypter = {
21+
protocol: 'insecure',
22+
secureInbound: async (localPeer, duplex, expectedPeer) => {
23+
// 1. Perform a basic handshake.
24+
const shake = handshake(duplex)
25+
shake.write(localPeer.toBytes())
26+
const remoteId = await shake.read()
27+
28+
if (remoteId == null) {
29+
throw new Error('Could not read remote ID')
30+
}
31+
32+
const remotePeer = peerIdFromBytes(remoteId.slice())
33+
shake.rest()
34+
35+
if (expectedPeer != null && !expectedPeer.equals(remotePeer)) {
36+
throw new UnexpectedPeerError()
37+
}
38+
39+
// 2. Create your encryption box/unbox wrapper
40+
const wrapper = duplexPair<Uint8Array>()
41+
const encrypt = transform() // Use transform iterables to modify data
42+
const decrypt = transform()
43+
44+
void pipe(
45+
wrapper[0], // We write to wrapper
46+
encrypt, // The data is encrypted
47+
shake.stream, // It goes to the remote peer
48+
decrypt, // Decrypt the incoming data
49+
wrapper[0] // Pipe to the wrapper
50+
)
51+
52+
return {
53+
conn: {
54+
...wrapper[1],
55+
close: async () => {},
56+
localAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4001'),
57+
remoteAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4002'),
58+
timeline: {
59+
open: Date.now()
60+
},
61+
conn: true
62+
},
63+
remotePeer,
64+
remoteEarlyData: new Uint8Array(0)
65+
}
66+
},
67+
secureOutbound: async (localPeer, duplex, remotePeer) => {
68+
// 1. Perform a basic handshake.
69+
const shake = handshake(duplex)
70+
shake.write(localPeer.toBytes())
71+
const remoteId = await shake.read()
72+
73+
if (remoteId == null) {
74+
throw new Error('Could not read remote ID')
75+
}
76+
77+
shake.rest()
78+
79+
// 2. Create your encryption box/unbox wrapper
80+
const wrapper = duplexPair<Uint8Array>()
81+
const encrypt = transform()
82+
const decrypt = transform()
83+
84+
void pipe(
85+
wrapper[0], // We write to wrapper
86+
encrypt, // The data is encrypted
87+
shake.stream, // It goes to the remote peer
88+
decrypt, // Decrypt the incoming data
89+
wrapper[0] // Pipe to the wrapper
90+
)
91+
92+
return {
93+
conn: {
94+
...wrapper[1],
95+
close: async () => {},
96+
localAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4001'),
97+
remoteAddr: new Multiaddr('/ip4/127.0.0.1/tcp/4002'),
98+
timeline: {
99+
open: Date.now()
100+
},
101+
conn: true
102+
},
103+
remotePeer: peerIdFromBytes(remoteId.slice()),
104+
remoteEarlyData: new Uint8Array(0)
105+
}
106+
}
107+
}
108+
109+
return encrypter
110+
}

packages/libp2p-interface-compliance-tests/src/mocks/connection-manager.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,21 @@
11
import { EventEmitter } from '@libp2p/interfaces'
2-
import type { Connection } from '@libp2p/interfaces/src/connection'
3-
import type { PeerId } from '@libp2p/interfaces/src/peer-id'
4-
import type { ConnectionManager, ConnectionManagerEvents } from '@libp2p/interfaces/src/registrar'
2+
import type { Connection } from '@libp2p/interfaces/connection'
3+
import type { PeerId } from '@libp2p/interfaces/peer-id'
4+
import type { ConnectionManager, ConnectionManagerEvents } from '@libp2p/interfaces/registrar'
55

66
class MockConnectionManager extends EventEmitter<ConnectionManagerEvents> implements ConnectionManager {
7+
getConnectionMap (): Map<string, Connection[]> {
8+
throw new Error('Method not implemented.')
9+
}
10+
11+
getConnectionList (): Connection[] {
12+
throw new Error('Method not implemented.')
13+
}
14+
15+
getConnections (): Connection[] {
16+
throw new Error('Method not implemented.')
17+
}
18+
719
getConnection (peerId: PeerId): Connection | undefined {
820
throw new Error('Method not implemented.')
921
}

0 commit comments

Comments
 (0)