Skip to content

Commit 69c93ac

Browse files
achingbrainmaschad
andauthored
docs: add migration guide for libp2p@0.46.x (#1883)
Adds migration guide detailing breaking changes in libp2p@0.46.x --------- Co-authored-by: Chad Nehemiah <chad.nehemiah94@gmail.com>
1 parent c999d6a commit 69c93ac

File tree

2 files changed

+235
-1
lines changed

2 files changed

+235
-1
lines changed

doc/migrations/v0.45-v0.46.md

+234
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
# Migrating to libp2p@46 <!-- omit in toc -->
2+
3+
A migration guide for refactoring your application code from libp2p `v0.45` to `v0.46`.
4+
5+
## Table of Contents <!-- omit in toc -->
6+
7+
- [New features](#new-features)
8+
- [Manual identify](#manual-identify)
9+
- [Transient connections](#transient-connections)
10+
- [Breaking changes](#breaking-changes)
11+
- [Graceful stream closing](#graceful-stream-closing)
12+
- [Stream/Connection stat properties](#streamconnection-stat-properties)
13+
- [Interface module consolidation](#interface-module-consolidation)
14+
15+
## New features
16+
17+
### Manual identify
18+
19+
The [identify protocol](https://github.com/libp2p/specs/blob/master/identify/README.md) is used by libp2p to discover which protocols a remote peer supports. By default it runs on every connection after it opens.
20+
21+
Applications interested in peers that support certain protocols can register [topology callbacks](https://libp2p.github.io/js-libp2p/interfaces/_libp2p_interface.index.unknown.Topology.html) to be notified when network peers that support those protocols connect or disconnect.
22+
23+
`libp2p@0.46.x` adds the ability for the user to fine-tune their identify usage and to run the identify protocol manually:
24+
25+
```ts
26+
import { createLibp2p } from 'libp2p'
27+
import { identifyService } from 'libp2p/identify'
28+
29+
const node = await createLibp2p({
30+
services: {
31+
identify: identifyService({
32+
// identify has stream limits so to prevent remote peers from closing
33+
// streams due to too many identify streams being opened in parallel,
34+
// so use this setting to disable running identify automatically.
35+
//
36+
// Note that this means you will need to run identify manually for
37+
// every connection that opens in order for topologies to work.
38+
//
39+
// Some modules such as KAD-DHT and Circuit Relay rely on this being
40+
// the case.
41+
runOnConnectionOpen: false
42+
})
43+
}
44+
})
45+
46+
const conn = await node.dial('/ip4/123.123...')
47+
const identifyResult = await node.services.identify.identify(conn)
48+
```
49+
50+
Note that this is an advanced option and is not necessary for the vast majority of users.
51+
52+
Most users will want to configure a topology instead to be notified when new peers are discovered that support a given protocol:
53+
54+
```ts
55+
import { createLibp2p } from 'libp2p'
56+
import { identifyService } from 'libp2p/identify'
57+
58+
const node = await createLibp2p({
59+
services: {
60+
identify: identifyService()
61+
}
62+
})
63+
64+
node.register('/my/protocol', {
65+
onConnect (peer, connection) {
66+
// this is called after identify has completed and the peer has confirmed
67+
// which protocols it supports
68+
},
69+
onDisconnect (peer, connection) {
70+
// handle disconnect
71+
}
72+
})
73+
```
74+
75+
### Transient connections
76+
77+
Some connections have limits applied to them by the remote. For example, as part of the [Circuit Relay v2 protocol](https://github.com/libp2p/specs/blob/master/relay/circuit-v2.md) relay servers are allowed to limit the amount of data transferred over a relayed connection and for how long.
78+
79+
These connections are not expected to be long-lived and must be treated as slightly fragile as the remote may close them at any time. To detect this, these types of connections have a boolean `.transient` property set to `true`.
80+
81+
```ts
82+
import { createLibp2p } from 'libp2p'
83+
import { identifyService } from 'libp2p/identify'
84+
85+
const node = await createLibp2p({ /* ... */ })
86+
87+
// make a direct connection to a peer
88+
const conn1 = await node.dial('/ip4/123.123.123.123/tcp/123')
89+
console.info(conn1.transient) // false
90+
91+
// make a connection to a peer via a relay server
92+
const conn2 = await node.dial('/ip4/.../p2p-circuit/...')
93+
console.info(conn2.transient) // true
94+
```
95+
96+
By default no protocols may run over a transient connection - to allow this protocols must explicitly opt-in to being run. This is in order to prevent high-bandwidth protocols from accidentally causing the remote to close the connection.
97+
98+
```ts
99+
import { createLibp2p } from 'libp2p'
100+
import { identifyService } from 'libp2p/identify'
101+
102+
const node = await createLibp2p({
103+
// config here
104+
})
105+
106+
// register an incoming stream handler for a protocol that is allowed to run over
107+
// transient connections
108+
await node.register('/my/protocol', () => {}, {
109+
runOnTransientConnection: true
110+
})
111+
112+
// open a stream and allow the protocol to run over a transient connection
113+
const stream = await node.dialProtocol('/ip4/.../p2p-circuit/...', '/my/protocol', {
114+
runOnTransientConnection: true
115+
})
116+
117+
// the same flag can be passed to the `newStream` method on the connection itself
118+
const conn = await node.dial()
119+
conn.newStream('/my/protocol', {
120+
runOnTransientConnection: true
121+
})
122+
```
123+
124+
## Breaking changes
125+
126+
### Graceful stream closing
127+
128+
Streams can either be closed gracefully, where we wait for any unsent data to be sent, or aborted in which case any unsent data is discarded and a reset message is sent, notifying the remote of the abnormal termination.
129+
130+
To close a stream gracefully we call the `.close` method (or `.closeRead`/`.closeWrite` for when we want half-closed streams). To abort a stream we call `.abort` and pass an error object.
131+
132+
In previous versions the `.close` method was synchronous which meant it could not wait for existing data to be sent which made nodes behave unpredictably.
133+
134+
From `0.46.x` the `.close`/`.closeRead`/`.closeWrite` methods on the Stream interface are now asynchronous. `.abort` is a synchronous method that accepts an Error object.
135+
136+
Similarly the Connection interface now has asynchronous `.close` and synchronous `.abort` methods.
137+
138+
The `.reset` method has been removed from the Stream interface as it is only to be invoked internally by stream multiplexers when a remote stream reset has occurred.
139+
140+
**Before**
141+
142+
```js
143+
const stream = await libp2p.dialProtocol(multiaddr, '/my-protocol/1.0.0')
144+
145+
// send some data
146+
await stream.sink([data])
147+
148+
// close the stream - previously this may not have waited for the data to be sent
149+
stream.close()
150+
151+
// alternatively cause the stream to error on the remote
152+
stream.abort(new Error('Oh no!'))
153+
```
154+
155+
**After**
156+
157+
```js
158+
const stream = await libp2p.dialProtocol(multiaddr, '/my-protocol/1.0.0')
159+
160+
// send some data
161+
await stream.sink([data])
162+
163+
// close the stream - this method is now async
164+
await stream.close()
165+
166+
// alternatively cause the stream to error on the remote
167+
stream.abort(new Error('Oh no!'))
168+
```
169+
170+
### Stream/Connection stat properties
171+
172+
The properties on the `stream.stat` and `connection.stat` objects are now stored on the stream/connection itself.
173+
174+
**Before**
175+
176+
```js
177+
// stream.stat properties
178+
console.info(stream.stat.direction)
179+
console.info(stream.stat.timeline)
180+
console.info(stream.stat.protocol)
181+
182+
// connection.stat properties
183+
console.info(connection.stat.direction)
184+
console.info(connection.stat.timeline)
185+
console.info(connection.stat.multiplexer)
186+
console.info(connection.stat.encryption)
187+
console.info(connection.stat.status)
188+
```
189+
190+
**After**
191+
192+
```js
193+
// stream.stat properties
194+
console.info(stream.direction)
195+
console.info(stream.timeline)
196+
console.info(stream.protocol)
197+
198+
// connection.stat properties
199+
console.info(connection.direction)
200+
console.info(connection.timeline)
201+
console.info(connection.multiplexer)
202+
console.info(connection.encryption)
203+
console.info(connection.status)
204+
```
205+
206+
### Interface module consolidation
207+
208+
In an effort to prevent breaking changes affecting unrelated modules, libp2p prior to 0.46.x had a large number of single-issue interface modules for internal and external types - `@libp2p/address-manager`, `@libp2p/connection-gater`, `@libp2p/connection-manager` and so on.
209+
210+
This meant that although we could release a new version of the address manager interface without impacting modules that only depended on the connection manager, releasing any change became a multiple-step process during which there was a time window sometimes lasting several days when the latest versions of modules would be incompatible with each other.
211+
212+
Adding new methods and types to interfaces also became a breaking change since the existing released implementations of those interfaces would not implement the new methods which complicated matters further.
213+
214+
Since [libp2p/js-libp2p#1792](https://github.com/libp2p/js-libp2p/pull/1792) converted libp2p into a monorepo project, a lot of these problems have gone away since we can now release multiple libp2p modules simultaneously.
215+
216+
The urgency that required multiple interface modules has also subsided somewhat so now all libp2p interfaces are collected into two modules - `@lib2p2p/interface` for public-facing APIs and `@libp2p/interface-internal` for APIs designed to be consumed by libp2p components.
217+
218+
**Before**
219+
220+
```js
221+
import type { Libp2p } from '@libp2p/interface-libp2p'
222+
import type { AddressManager } from '@libp2p/interface-address-manager'
223+
import type { ConnectionManager } from '@libp2p/interface-connection-manager'
224+
// etc
225+
```
226+
227+
**After**
228+
229+
```js
230+
import type { Libp2p } from '@libp2p/interface'
231+
import type { AddressManager } from '@libp2p/interface-internal/address-manager'
232+
import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
233+
// etc
234+
```

interop/BrowserDockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ ENV BROWSER=$BROWSER
1111
ENV CI true
1212

1313
# manually specify runner until https://github.com/hugomrdias/playwright-test/issues/572 is resolved
14-
ENTRYPOINT npm run test:interop:multidim -- --build false --types false -t browser -- --browser $BROWSER --runner mocha
14+
ENTRYPOINT npm run test:interop:multidim -- --build false --types false -t browser -- --browser $BROWSER

0 commit comments

Comments
 (0)