Skip to content

Commit

Permalink
Merge branch 'main' into feat/add-auto-confirm-to-auto-tls
Browse files Browse the repository at this point in the history
  • Loading branch information
achingbrain authored Dec 6, 2024
2 parents 2cda67d + 92cc740 commit cb4b93a
Show file tree
Hide file tree
Showing 9 changed files with 313 additions and 33 deletions.
14 changes: 13 additions & 1 deletion packages/interface-internal/src/address-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@ export interface NodeAddress {
*/
verified: boolean

/**
* The timestamp at which the address was last verified
*/
lastVerified?: number

/**
* A millisecond timestamp after which this address should be reverified
*/
Expand All @@ -40,6 +45,13 @@ export interface NodeAddress {
type: AddressType
}

export interface ConfirmAddressOptions {
/**
* Override the TTL of the observed address verification
*/
ttl?: number
}

export interface AddressManager {
/**
* Get peer listen multiaddrs
Expand All @@ -61,7 +73,7 @@ export interface AddressManager {
* Signal that we have confidence an observed multiaddr is publicly dialable -
* this will make it appear in the output of getAddresses()
*/
confirmObservedAddr(addr: Multiaddr): void
confirmObservedAddr(addr: Multiaddr, options?: ConfirmAddressOptions): void

/**
* Signal that we do not have confidence an observed multiaddr is publicly dialable -
Expand Down
33 changes: 28 additions & 5 deletions packages/libp2p/src/address-manager/dns-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import type { Logger } from '@libp2p/interface'
import type { NodeAddress } from '@libp2p/interface-internal'
import type { Multiaddr, StringTuple } from '@multiformats/multiaddr'

const MAX_DATE = 8_640_000_000_000_000

export const defaultValues = {
maxObservedAddresses: 10
}
Expand All @@ -13,6 +15,7 @@ interface DNSMapping {
domain: string
verified: boolean
expires: number
lastVerified?: number
}

const CODEC_TLS = 0x01c0
Expand Down Expand Up @@ -46,13 +49,15 @@ export class DNSMappings {
add (domain: string, addresses: string[]): void {
addresses.forEach(ip => {
this.log('add DNS mapping %s to %s', ip, domain)
// we are only confident if this is an local domain mapping, otherwise
// we will require external validation
const verified = isPrivateIp(ip) === true

this.mappings.set(ip, {
domain,
// we are only confident if this is an local domain mapping, otherwise
// we will require external validation
verified: isPrivateIp(ip) === true,
expires: 0
verified,
expires: verified ? MAX_DATE - Date.now() : 0,
lastVerified: verified ? MAX_DATE - Date.now() : undefined
})
})
}
Expand Down Expand Up @@ -109,7 +114,8 @@ export class DNSMappings {
}`),
verified: mapping.verified,
type: 'dns-mapping',
expires: mapping.expires
expires: mapping.expires,
lastVerified: mapping.lastVerified
})
}
}
Expand Down Expand Up @@ -139,12 +145,29 @@ export class DNSMappings {
startingConfidence = mapping.verified
mapping.verified = true
mapping.expires = Date.now() + ttl
mapping.lastVerified = Date.now()
}
}

return startingConfidence
}

unconfirm (ma: Multiaddr, ttl: number): boolean {
const host = this.findHost(ma)
let wasConfident = false

for (const [ip, mapping] of this.mappings.entries()) {
if (mapping.domain === host) {
this.log('removing verification of %s to %s DNS mapping', ip, mapping.domain)
wasConfident = wasConfident || mapping.verified
mapping.verified = false
mapping.expires = Date.now() + ttl
}
}

return wasConfident
}

private findHost (ma: Multiaddr): string | undefined {
for (const tuple of ma.stringTuples()) {
if (tuple[0] === CODEC_SNI) {
Expand Down
89 changes: 67 additions & 22 deletions packages/libp2p/src/address-manager/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,18 @@ import { multiaddr } from '@multiformats/multiaddr'
import { DNSMappings } from './dns-mappings.js'
import { IPMappings } from './ip-mappings.js'
import { ObservedAddresses } from './observed-addresses.js'
import { TransportAddresses } from './transport-addresses.js'
import type { ComponentLogger, Libp2pEvents, Logger, TypedEventTarget, PeerId, PeerStore } from '@libp2p/interface'
import type { AddressManager as AddressManagerInterface, TransportManager, NodeAddress } from '@libp2p/interface-internal'
import type { AddressManager as AddressManagerInterface, TransportManager, NodeAddress, ConfirmAddressOptions } from '@libp2p/interface-internal'
import type { Filter } from '@libp2p/utils/filters'
import type { Multiaddr } from '@multiformats/multiaddr'

const ONE_MINUTE = 60_000

export const defaultValues = {
maxObservedAddresses: 10,
observedAddressTTL: 60_000 * 10
addressVerificationTTL: ONE_MINUTE * 10,
addressVerificationRetry: ONE_MINUTE * 5
}

export interface AddressManagerInit {
Expand Down Expand Up @@ -50,9 +54,25 @@ export interface AddressManagerInit {
maxObservedAddresses?: number

/**
* How long before each observed address should be reverified
* How long before each public address should be reverified in ms.
*
* Requires `@libp2p/autonat` or some other verification method to be
* configured.
*
* @default 600_000
*/
addressVerificationTTL?: number

/**
* After a transport or mapped address has failed to verify, how long to wait
* before retrying it in ms
*
* Requires `@libp2p/autonat` or some other verification method to be
* configured.
*
* @default 300_000
*/
observedAddressTTL?: number
addressVerificationRetry?: number
}

export interface AddressManagerComponents {
Expand Down Expand Up @@ -103,8 +123,10 @@ export class AddressManager implements AddressManagerInterface {
private readonly observed: ObservedAddresses
private readonly dnsMappings: DNSMappings
private readonly ipMappings: IPMappings
private readonly transportAddresses: TransportAddresses
private readonly observedAddressFilter: Filter
private readonly observedAddressTTL: number
private readonly addressVerificationTTL: number
private readonly addressVerificationRetry: number

/**
* Responsible for managing the peer addresses.
Expand All @@ -123,9 +145,11 @@ export class AddressManager implements AddressManagerInterface {
this.observed = new ObservedAddresses(components, init)
this.dnsMappings = new DNSMappings(components, init)
this.ipMappings = new IPMappings(components, init)
this.transportAddresses = new TransportAddresses(components, init)
this.announceFilter = init.announceFilter ?? defaultAddressFilter
this.observedAddressFilter = createScalableCuckooFilter(1024)
this.observedAddressTTL = init.observedAddressTTL ?? defaultValues.observedAddressTTL
this.addressVerificationTTL = init.addressVerificationTTL ?? defaultValues.addressVerificationTTL
this.addressVerificationRetry = init.addressVerificationRetry ?? defaultValues.addressVerificationRetry

// this method gets called repeatedly on startup when transports start listening so
// debounce it so we don't cause multiple self:peer:update events to be emitted
Expand Down Expand Up @@ -221,20 +245,24 @@ export class AddressManager implements AddressManagerInterface {
this.observed.add(addr)
}

confirmObservedAddr (addr: Multiaddr): void {
confirmObservedAddr (addr: Multiaddr, options?: ConfirmAddressOptions): void {
addr = stripPeerId(addr, this.components.peerId)
let startingConfidence = true

if (this.observed.has(addr)) {
startingConfidence = this.observed.confirm(addr, this.observedAddressTTL)
startingConfidence = this.observed.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
}

if (this.transportAddresses.has(addr)) {
startingConfidence = this.transportAddresses.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
}

if (this.dnsMappings.has(addr)) {
startingConfidence = this.dnsMappings.confirm(addr, this.observedAddressTTL)
startingConfidence = this.dnsMappings.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
}

if (this.ipMappings.has(addr)) {
startingConfidence = this.ipMappings.confirm(addr, this.observedAddressTTL)
startingConfidence = this.ipMappings.confirm(addr, options?.ttl ?? this.addressVerificationTTL)
}

// only trigger the 'self:peer:update' event if our confidence in an address has changed
Expand All @@ -243,12 +271,31 @@ export class AddressManager implements AddressManagerInterface {
}
}

removeObservedAddr (addr: Multiaddr): void {
removeObservedAddr (addr: Multiaddr, options?: ConfirmAddressOptions): void {
addr = stripPeerId(addr, this.components.peerId)

this.observed.remove(addr)
this.dnsMappings.remove(addr)
this.ipMappings.remove(addr)
let startingConfidence = false

if (this.observed.has(addr)) {
startingConfidence = this.observed.remove(addr)
}

if (this.transportAddresses.has(addr)) {
startingConfidence = this.transportAddresses.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
}

if (this.dnsMappings.has(addr)) {
startingConfidence = this.dnsMappings.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
}

if (this.ipMappings.has(addr)) {
startingConfidence = this.ipMappings.unconfirm(addr, options?.ttl ?? this.addressVerificationRetry)
}

// only trigger the 'self:peer:update' event if our confidence in an address has changed
if (startingConfidence) {
this._updatePeerStoreAddresses()
}
}

getAddresses (): Multiaddr[] {
Expand Down Expand Up @@ -299,20 +346,17 @@ export class AddressManager implements AddressManagerInterface {
multiaddr,
verified: true,
type: 'announce',
expires: Date.now() + this.observedAddressTTL
expires: Date.now() + this.addressVerificationTTL,
lastVerified: Date.now()
}))
}

let addresses: NodeAddress[] = []

// add transport addresses
addresses = addresses.concat(
this.components.transportManager.getAddrs().map(multiaddr => ({
multiaddr,
verified: true,
type: 'transport',
expires: Date.now() + this.observedAddressTTL
}))
this.components.transportManager.getAddrs()
.map(multiaddr => this.transportAddresses.get(multiaddr, this.addressVerificationTTL))
)

// add append announce addresses
Expand All @@ -321,7 +365,8 @@ export class AddressManager implements AddressManagerInterface {
multiaddr,
verified: true,
type: 'announce',
expires: Date.now() + this.observedAddressTTL
expires: Date.now() + this.addressVerificationTTL,
lastVerified: Date.now()
}))
)

Expand Down
29 changes: 28 additions & 1 deletion packages/libp2p/src/address-manager/ip-mappings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ interface PublicAddressMapping {
protocol: 'tcp' | 'udp'
verified: boolean
expires: number
lastVerified?: number
}

const CODEC_IP4 = 0x04
Expand Down Expand Up @@ -134,7 +135,8 @@ export class IPMappings {
}`),
verified: mapping.verified,
type: 'ip-mapping',
expires: mapping.expires
expires: mapping.expires,
lastVerified: mapping.lastVerified
})
}
}
Expand All @@ -155,10 +157,35 @@ export class IPMappings {
startingConfidence = mapping.verified
mapping.verified = true
mapping.expires = Date.now() + ttl
mapping.lastVerified = Date.now()
}
}
}

return startingConfidence
}

unconfirm (ma: Multiaddr, ttl: number): boolean {
const tuples = ma.stringTuples()
const host = tuples[0][1] ?? ''
const protocol = tuples[1][0] === CODEC_TCP ? 'tcp' : 'udp'
const port = parseInt(tuples[1][1] ?? '0')
let wasConfident = false

for (const mappings of this.mappings.values()) {
for (let i = 0; i < mappings.length; i++) {
const mapping = mappings[i]

if (mapping.externalIp === host && mapping.externalPort === port && mapping.protocol === protocol) {
this.log('removing verification of %s:%s to %s:%s %s IP mapping', mapping.externalIp, mapping.externalPort, host, port, protocol)

wasConfident = wasConfident || mapping.verified
mapping.verified = false
mapping.expires = Date.now() + ttl
}
}
}

return wasConfident
}
}
14 changes: 11 additions & 3 deletions packages/libp2p/src/address-manager/observed-addresses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ export const defaultValues = {
interface ObservedAddressMetadata {
verified: boolean
expires: number
lastVerified?: number
}

export class ObservedAddresses {
Expand Down Expand Up @@ -60,23 +61,30 @@ export class ObservedAddresses {
multiaddr: multiaddr(ma),
verified: metadata.verified,
type: 'observed',
expires: metadata.expires
expires: metadata.expires,
lastVerified: metadata.lastVerified
}))
}

remove (ma: Multiaddr): void {
remove (ma: Multiaddr): boolean {
const startingConfidence = this.addresses.get(ma.toString())?.verified ?? false

this.log('removing observed address %a', ma)
this.addresses.delete(ma.toString())

return startingConfidence
}

confirm (ma: Multiaddr, ttl: number): boolean {
const addrString = ma.toString()
const metadata = this.addresses.get(addrString) ?? {
verified: false,
expires: Date.now() + ttl
expires: Date.now() + ttl,
lastVerified: Date.now()
}
const startingConfidence = metadata.verified
metadata.verified = true
metadata.lastVerified = Date.now()

this.log('marking observed address %a as verified', addrString)
this.addresses.set(addrString, metadata)
Expand Down
Loading

0 comments on commit cb4b93a

Please sign in to comment.