Skip to content

Commit d6c8601

Browse files
authored
feat: add autonat support (#1298)
Implements the [autonat](https://github.com/libp2p/specs/blob/master/autonat/README.md) spec to give us confidence in our external addresses and to open the door to things like switching DHT server mode on automatically, hole punching, v2 circuit relay etc. Depends on: - [x] libp2p/js-libp2p-interfaces#269 Fixes #1005
1 parent 064035e commit d6c8601

15 files changed

+1720
-59
lines changed

package.json

+4-1
Original file line numberDiff line numberDiff line change
@@ -79,11 +79,12 @@
7979
"scripts": {
8080
"clean": "aegir clean",
8181
"lint": "aegir lint",
82-
"dep-check": "aegir dep-check",
82+
"dep-check": "aegir dep-check -i protons",
8383
"prepublishOnly": "node scripts/update-version.js",
8484
"build": "aegir build",
8585
"docs": "aegir docs",
8686
"generate": "run-s generate:proto:*",
87+
"generate:proto:autonat": "protons ./src/autonat/pb/index.proto",
8788
"generate:proto:circuit": "protons ./src/circuit/pb/index.proto",
8889
"generate:proto:fetch": "protons ./src/fetch/pb/proto.proto",
8990
"generate:proto:identify": "protons ./src/identify/pb/message.proto",
@@ -143,6 +144,7 @@
143144
"it-drain": "^2.0.0",
144145
"it-filter": "^2.0.0",
145146
"it-first": "^2.0.0",
147+
"it-parallel": "^3.0.0",
146148
"it-handshake": "^4.1.2",
147149
"it-length-prefixed": "^8.0.2",
148150
"it-map": "^2.0.0",
@@ -201,6 +203,7 @@
201203
"p-event": "^5.0.1",
202204
"p-times": "^4.0.0",
203205
"p-wait-for": "^5.0.0",
206+
"protons": "^7.0.2",
204207
"sinon": "^15.0.1",
205208
"sinon-ts": "^1.0.0"
206209
},

src/address-manager/index.ts

+62-35
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,35 @@ export interface AddressFilter {
4444

4545
const defaultAddressFilter = (addrs: Multiaddr[]): Multiaddr[] => addrs
4646

47+
interface ObservedAddressMetadata {
48+
confident: boolean
49+
}
50+
51+
/**
52+
* If the passed multiaddr contains the passed peer id, remove it
53+
*/
54+
function stripPeerId (ma: Multiaddr, peerId: PeerId): Multiaddr {
55+
const observedPeerIdStr = ma.getPeerId()
56+
57+
// strip our peer id if it has been passed
58+
if (observedPeerIdStr != null) {
59+
const observedPeerId = peerIdFromString(observedPeerIdStr)
60+
61+
// use same encoding for comparison
62+
if (observedPeerId.equals(peerId)) {
63+
ma = ma.decapsulate(multiaddr(`/p2p/${peerId.toString()}`))
64+
}
65+
}
66+
67+
return ma
68+
}
69+
4770
export class DefaultAddressManager extends EventEmitter<AddressManagerEvents> {
4871
private readonly components: DefaultAddressManagerComponents
4972
// this is an array to allow for duplicates, e.g. multiples of `/ip4/0.0.0.0/tcp/0`
5073
private readonly listen: string[]
5174
private readonly announce: Set<string>
52-
private readonly observed: Set<string>
75+
private readonly observed: Map<string, ObservedAddressMetadata>
5376
private readonly announceFilter: AddressFilter
5477

5578
/**
@@ -66,7 +89,7 @@ export class DefaultAddressManager extends EventEmitter<AddressManagerEvents> {
6689
this.components = components
6790
this.listen = listen.map(ma => ma.toString())
6891
this.announce = new Set(announce.map(ma => ma.toString()))
69-
this.observed = new Set()
92+
this.observed = new Map()
7093
this.announceFilter = init.announceFilter ?? defaultAddressFilter
7194
}
7295

@@ -88,52 +111,51 @@ export class DefaultAddressManager extends EventEmitter<AddressManagerEvents> {
88111
* Get observed multiaddrs
89112
*/
90113
getObservedAddrs (): Multiaddr[] {
91-
return Array.from(this.observed).map((a) => multiaddr(a))
114+
return Array.from(this.observed).map(([a]) => multiaddr(a))
92115
}
93116

94117
/**
95118
* Add peer observed addresses
96-
* Signal that we have confidence an observed multiaddr is publicly dialable -
97-
* this will make it appear in the output of getAddresses()
98119
*/
99-
confirmObservedAddr (addr: Multiaddr): void {
100-
101-
}
120+
addObservedAddr (addr: Multiaddr): void {
121+
addr = stripPeerId(addr, this.components.peerId)
122+
const addrString = addr.toString()
102123

103-
/**
104-
* Signal that we do not have confidence an observed multiaddr is publicly dialable -
105-
* this will remove it from the output of getObservedAddrs()
106-
*/
107-
removeObservedAddr (addr: Multiaddr): void {
124+
// do not trigger the change:addresses event if we already know about this address
125+
if (this.observed.has(addrString)) {
126+
return
127+
}
108128

129+
this.observed.set(addrString, {
130+
confident: false
131+
})
109132
}
110133

111-
/**
112-
* Add peer observed addresses
113-
*/
114-
addObservedAddr (addr: string | Multiaddr): void {
115-
let ma = multiaddr(addr)
116-
const remotePeer = ma.getPeerId()
117-
118-
// strip our peer id if it has been passed
119-
if (remotePeer != null) {
120-
const remotePeerId = peerIdFromString(remotePeer)
121-
122-
// use same encoding for comparison
123-
if (remotePeerId.equals(this.components.peerId)) {
124-
ma = ma.decapsulate(multiaddr(`/p2p/${this.components.peerId.toString()}`))
125-
}
134+
confirmObservedAddr (addr: Multiaddr): void {
135+
addr = stripPeerId(addr, this.components.peerId)
136+
const addrString = addr.toString()
137+
138+
const metadata = this.observed.get(addrString) ?? {
139+
confident: false
126140
}
127141

128-
const addrString = ma.toString()
142+
const startingConfidence = metadata.confident
129143

130-
// do not trigger the change:addresses event if we already know about this address
131-
if (this.observed.has(addrString)) {
132-
return
144+
this.observed.set(addrString, {
145+
confident: true
146+
})
147+
148+
// only trigger the change:addresses event if our confidence in an address has changed
149+
if (!startingConfidence) {
150+
this.dispatchEvent(new CustomEvent('change:addresses'))
133151
}
152+
}
153+
154+
removeObservedAddr (addr: Multiaddr): void {
155+
addr = stripPeerId(addr, this.components.peerId)
156+
const addrString = addr.toString()
134157

135-
this.observed.add(addrString)
136-
this.dispatchEvent(new CustomEvent('change:addresses'))
158+
this.observed.delete(addrString)
137159
}
138160

139161
getAddresses (): Multiaddr[] {
@@ -144,7 +166,12 @@ export class DefaultAddressManager extends EventEmitter<AddressManagerEvents> {
144166
addrs = this.components.transportManager.getAddrs().map(ma => ma.toString())
145167
}
146168

147-
addrs = addrs.concat(this.getObservedAddrs().map(ma => ma.toString()))
169+
// add observed addresses we are confident in
170+
addrs = addrs.concat(
171+
Array.from(this.observed)
172+
.filter(([ma, metadata]) => metadata.confident)
173+
.map(([ma]) => ma)
174+
)
148175

149176
// dedupe multiaddrs
150177
const addrSet = new Set(addrs)

src/autonat/constants.ts

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
2+
export const PROTOCOL = '/libp2p/autonat/1.0.0'
3+
export const PROTOCOL_VERSION = '1.0.0'
4+
export const PROTOCOL_NAME = 'autonat'

0 commit comments

Comments
 (0)