From c8c329e9d4b4776e051a2aafe7ee13e89194abfc Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 17 Nov 2022 17:13:30 -0700 Subject: [PATCH 01/18] Fill in code documentation --- .coverage/coverage-final.json | 2 + .coverage/coverage-pw.json | 1 + .../86a415dc-f2ec-4a3d-b888-db58545f6218.json | 1 + .../86a415dc-f2ec-4a3d-b888-db58545f6218.json | 1 + .nyc_output/processinfo/index.json | 1 + package.json | 1 + src/error.ts | 98 +++++++++---------- src/maconn.ts | 34 ++++++- src/muxer.ts | 58 ++++++++++- src/options.ts | 6 +- src/sdp.ts | 44 ++++++--- src/stream.ts | 31 ++++++ src/transport.ts | 60 ++++++++++-- 13 files changed, 258 insertions(+), 80 deletions(-) create mode 100644 .coverage/coverage-final.json create mode 100644 .coverage/coverage-pw.json create mode 100644 .nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json create mode 100644 .nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json create mode 100644 .nyc_output/processinfo/index.json diff --git a/.coverage/coverage-final.json b/.coverage/coverage-final.json new file mode 100644 index 0000000..fe401cc --- /dev/null +++ b/.coverage/coverage-final.json @@ -0,0 +1,2 @@ +{"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/test/connection.browser.spec.ts": {"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/test/connection.browser.spec.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":0}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":69}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":39}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":34}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":32}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":54}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":27}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":0}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":38}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":0}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":47}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":57}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":74}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":45}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":113}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":64}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":34}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":93}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":0}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":43}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":55}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":7}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":0}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":55}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":49}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":45}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":74}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":45}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":113}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":64}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":37}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":51}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":34}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":32}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":28}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":57}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":45}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":56}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":25}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":14}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":7}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":37}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":7}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":0}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":6}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":0}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":9}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} +} diff --git a/.coverage/coverage-pw.json b/.coverage/coverage-pw.json new file mode 100644 index 0000000..5821601 --- /dev/null +++ b/.coverage/coverage-pw.json @@ -0,0 +1 @@ +{"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/error.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/error.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/sdp.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/sdp.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/proto_ts/message.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/proto_ts/message.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/stream.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/stream.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/util.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/util.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/maconn.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/maconn.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/muxer.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/muxer.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/options.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/options.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/transport.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/transport.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/index.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/index.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}}} \ No newline at end of file diff --git a/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json b/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json b/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json new file mode 100644 index 0000000..ad3997a --- /dev/null +++ b/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json @@ -0,0 +1 @@ +{"parent":null,"pid":98624,"argv":["/Users/daviddimaria/.nvm/versions/node/v16.15.0/bin/node","/Users/daviddimaria/work/conduit/js-libp2p-webrtc/node_modules/.bin/mocha","--temp-directory",".coverage","--all"],"execArgv":[],"cwd":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc","time":1668528326079,"ppid":98623,"coverageFilename":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json","externalId":"","uuid":"86a415dc-f2ec-4a3d-b888-db58545f6218","files":[]} \ No newline at end of file diff --git a/.nyc_output/processinfo/index.json b/.nyc_output/processinfo/index.json new file mode 100644 index 0000000..bd3a80d --- /dev/null +++ b/.nyc_output/processinfo/index.json @@ -0,0 +1 @@ +{"processes":{"86a415dc-f2ec-4a3d-b888-db58545f6218":{"parent":null,"children":[]}},"files":{},"externalIds":{}} \ No newline at end of file diff --git a/package.json b/package.json index 33c5963..18b12a3 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "build": "aegir build", "test": "aegir test --target browser", "test:interop": "run-p --race start-ext-server wait-then-test", + "test:coverage": "aegir test --target browser --cov -f \"./dist/test/**/*.spec.js\" && npx c8 report", "start-ext-server": "rm -vf dist/test/server-multiaddr.js ; cd ../go-libp2p/ && go run examples/webrtc/main.go ../js-libp2p-webrtc/dist/test/ ", "wait-for-server": "wait-on --delay 1000 --timeout 10000 dist/test/server-multiaddr.js", "wait-then-test": "run-s wait-for-server test", diff --git a/src/error.ts b/src/error.ts index 98ff1e8..c360895 100644 --- a/src/error.ts +++ b/src/error.ts @@ -1,13 +1,6 @@ import errCode from 'err-code' import { Direction } from '@libp2p/interface-connection' -export class WebRTCTransportError extends Error { - constructor (msg: string) { - super('WebRTC transport error: ' + msg) - this.name = 'WebRTCTransportError' - } -} - export enum codes { ERR_ALREADY_ABORTED = 'ERR_ALREADY_ABORTED', ERR_DATA_CHANNEL = 'ERR_DATA_CHANNEL', @@ -21,6 +14,13 @@ export enum codes { ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS = 'ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS', } +export class WebRTCTransportError extends Error { + constructor (msg: string) { + super('WebRTC transport error: ' + msg) + this.name = 'WebRTCTransportError' + } +} + export class ConnectionClosedError extends WebRTCTransportError { constructor (state: RTCPeerConnectionState, msg: string) { super(`peerconnection moved to state: ${state}:` + msg) @@ -32,38 +32,37 @@ export function connectionClosedError (state: RTCPeerConnectionState, msg: strin return errCode(new ConnectionClosedError(state, msg), codes.ERR_CONNECTION_CLOSED) } -export class InvalidArgumentError extends WebRTCTransportError { - constructor (msg: string) { - super('There was a problem with a provided argument: ' + msg) - this.name = 'WebRTC/InvalidArgumentError' +export class DataChannelError extends WebRTCTransportError { + constructor (streamLabel: string, errorMessage: string) { + super(`[stream: ${streamLabel}] data channel error: ${errorMessage}`) + this.name = 'WebRTC/DataChannelError' } } -export function unsupportedHashAlgorithm (algorithm: string) { - return errCode(new UnsupportedHashAlgorithmError(algorithm), codes.ERR_HASH_NOT_SUPPORTED) +export function dataChannelError (streamLabel: string, msg: string) { + return errCode(new DataChannelError(streamLabel, msg), codes.ERR_DATA_CHANNEL) } -export class UnsupportedHashAlgorithmError extends WebRTCTransportError { - constructor (algo: string) { - const msg = `unsupported hash algorithm: ${algo}` - super(msg) - this.name = 'WebRTC/UnsupportedHashAlgorithmError' +export class InappropriateMultiaddrError extends WebRTCTransportError { + constructor (msg: string) { + super('There was a problem with the Multiaddr which was passed in: ' + msg) + this.name = 'WebRTC/InappropriateMultiaddrError' } } -export function invalidArgument (msg: string) { - return errCode(new InvalidArgumentError(msg), codes.ERR_INVALID_PARAMETERS) +export function inappropriateMultiaddr (msg: string) { + return errCode(new InappropriateMultiaddrError(msg), codes.ERR_INVALID_MULTIADDR) } -export class UnimplementedError extends WebRTCTransportError { - constructor (methodName: string) { - super('A method (' + methodName + ') was called though it has been intentionally left unimplemented.') - this.name = 'WebRTC/UnimplementedError' +export class InvalidArgumentError extends WebRTCTransportError { + constructor (msg: string) { + super('There was a problem with a provided argument: ' + msg) + this.name = 'WebRTC/InvalidArgumentError' } } -export function unimplemented (methodName: string) { - return errCode(new UnimplementedError(methodName), codes.ERR_NOT_IMPLEMENTED) +export function invalidArgument (msg: string) { + return errCode(new InvalidArgumentError(msg), codes.ERR_INVALID_PARAMETERS) } export class InvalidFingerprintError extends WebRTCTransportError { @@ -77,17 +76,6 @@ export function invalidFingerprint (fingerprint: string, source: string) { return errCode(new InvalidFingerprintError(fingerprint, source), codes.ERR_INVALID_FINGERPRINT) } -export class InappropriateMultiaddrError extends WebRTCTransportError { - constructor (msg: string) { - super('There was a problem with the Multiaddr which was passed in: ' + msg) - this.name = 'WebRTC/InappropriateMultiaddrError' - } -} - -export function inappropriateMultiaddr (msg: string) { - return errCode(new InappropriateMultiaddrError(msg), codes.ERR_INVALID_MULTIADDR) -} - export class OperationAbortedError extends WebRTCTransportError { constructor (context: string, abortReason: string) { super(`Signalled to abort because (${abortReason}})${context}`) @@ -99,25 +87,37 @@ export function operationAborted (context: string, reason: string) { return errCode(new OperationAbortedError(context, reason), codes.ERR_ALREADY_ABORTED) } -export class DataChannelError extends WebRTCTransportError { - constructor (streamLabel: string, errorMessage: string) { - super(`[stream: ${streamLabel}] data channel error: ${errorMessage}`) - this.name = 'WebRTC/DataChannelError' +export class OverStreamLimitError extends WebRTCTransportError { + constructor (msg: string) { + super(msg) + this.name = 'WebRTC/OverStreamLimitError' } } -export function dataChannelError (streamLabel: string, msg: string) { - return errCode(new DataChannelError(streamLabel, msg), codes.ERR_DATA_CHANNEL) +export function overStreamLimit (dir: Direction, proto: string) { + const code = dir === 'inbound' ? codes.ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS : codes.ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS + return errCode(new OverStreamLimitError(`${dir} stream limit reached for protocol - ${proto}`), code) } -export class StreamingLimitationError extends WebRTCTransportError { - constructor (msg: string) { +export class UnimplementedError extends WebRTCTransportError { + constructor (methodName: string) { + super('A method (' + methodName + ') was called though it has been intentionally left unimplemented.') + this.name = 'WebRTC/UnimplementedError' + } +} + +export function unimplemented (methodName: string) { + return errCode(new UnimplementedError(methodName), codes.ERR_NOT_IMPLEMENTED) +} + +export class UnsupportedHashAlgorithmError extends WebRTCTransportError { + constructor (algo: string) { + const msg = `unsupported hash algorithm: ${algo}` super(msg) - this.name = 'WebRTC/StreamingLimitationError' + this.name = 'WebRTC/UnsupportedHashAlgorithmError' } } -export function overStreamLimit (dir: Direction, proto: string) { - const code = dir === 'inbound' ? codes.ERR_TOO_MANY_INBOUND_PROTOCOL_STREAMS : codes.ERR_TOO_MANY_OUTBOUND_PROTOCOL_STREAMS - return errCode(new StreamingLimitationError(`${dir} stream limit reached for protocol - ${proto}`), code) +export function unsupportedHashAlgorithm (algorithm: string) { + return errCode(new UnsupportedHashAlgorithmError(algorithm), codes.ERR_HASH_NOT_SUPPORTED) } diff --git a/src/maconn.ts b/src/maconn.ts index 7182372..65725ef 100644 --- a/src/maconn.ts +++ b/src/maconn.ts @@ -8,17 +8,46 @@ import { nopSink, nopSource } from './util.js' const log = logger('libp2p:webrtc:connection') interface WebRTCMultiaddrConnectionInit { + /** + * WebRTC Peer Connection + */ peerConnection: RTCPeerConnection + + /** + * The multiaddr address used to communicate with the remote peer + */ remoteAddr: Multiaddr + + /** + * Holds the relevant events timestamps of the connection + */ timeline: MultiaddrConnectionTimeline } export class WebRTCMultiaddrConnection implements MultiaddrConnection { + /** + * WebRTC Peer Connection + */ private readonly peerConnection: RTCPeerConnection; + + /** + * The multiaddr address used to communicate with the remote peer + */ remoteAddr: Multiaddr; + + /** + * Holds the lifecycle times of the connection + */ timeline: MultiaddrConnectionTimeline; + /** + * The stream source, a no-op as the transport natively supports multiplexing + */ source: Source = nopSource + + /** + * The stream destination, a no-op as the transport natively supports multiplexing + */ sink: Sink> = nopSink; constructor (init: WebRTCMultiaddrConnectionInit) { @@ -28,7 +57,10 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection { } async close (err?: Error | undefined): Promise { - log.error('error closing connection', err) + if (err !== undefined) { + log.error('error closing connection', err) + } + this.peerConnection.close() } } diff --git a/src/muxer.ts b/src/muxer.ts index 668148b..108a5b7 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -7,7 +7,14 @@ import { WebRTCStream } from './stream.js' import { nopSink, nopSource } from './util.js' export class DataChannelMuxerFactory implements StreamMuxerFactory { + /** + * WebRTC Peer Connection + */ private readonly peerConnection: RTCPeerConnection + + /** + * The string representation of the protocol, requried by StreamMuxerFactory + */ protocol: string = '/webrtc' constructor (peerConnection: RTCPeerConnection) { @@ -19,21 +26,61 @@ export class DataChannelMuxerFactory implements StreamMuxerFactory { } } +/** + * A libp2p data channel stream muxer + */ export class DataChannelMuxer implements StreamMuxer { + /** + * WebRTC Peer Connection + */ private readonly peerConnection: RTCPeerConnection + /** + * WebRTC Peer Connection + */ readonly protocol: string = '/webrtc' + + /** + * WebRTC Peer Connection + */ streams: Stream[] = [] + + /** + * Initialized stream muxer + */ init?: StreamMuxerInit + + /** + * Close or abort all tracked streams and stop the muxer + */ close: (err?: Error | undefined) => void = () => {} - // nop source and sink, since the transport natively supports - // multiplexing + /** + * The stream source, a no-op as the transport natively supports multiplexing + */ source: Source = nopSource; + + /** + * The stream destination, a no-op as the transport natively supports multiplexing + */ sink: Sink> = nopSink; constructor (peerConnection: RTCPeerConnection, init?: StreamMuxerInit) { + /** + * Initialized stream muxer + */ this.init = init + + /** + * WebRTC Peer Connection + */ this.peerConnection = peerConnection + + /** + * Fired when a data channel has been added to the connection has been + * added by the remote peer. + * + * {@link https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-ondatachannel} + */ this.peerConnection.ondatachannel = ({ channel }) => { const stream = new WebRTCStream({ channel, @@ -51,6 +98,10 @@ export class DataChannelMuxer implements StreamMuxer { } } + /** + * Initiate a new stream with the given name. If no name is + * provided, the id of the stream will be used. + */ newStream (name: string = ''): Stream { const channel = this.peerConnection.createDataChannel(name) const stream = new WebRTCStream({ @@ -63,8 +114,7 @@ export class DataChannelMuxer implements StreamMuxer { }, closeCb: this.init?.onStreamEnd }) + return stream } } - -// export {} diff --git a/src/options.ts b/src/options.ts index b5a7e55..debd186 100644 --- a/src/options.ts +++ b/src/options.ts @@ -1,8 +1,4 @@ import { CreateListenerOptions, DialOptions } from '@libp2p/interface-transport' -export interface WebRTCListenerOptions extends CreateListenerOptions { - //, WebRTCInitiatorInit { - // channelOptions?: WebRTCReceiverInit -} - +export interface WebRTCListenerOptions extends CreateListenerOptions {} export interface WebRTCDialOptions extends DialOptions {} diff --git a/src/sdp.ts b/src/sdp.ts index 4c62c95..f7302d8 100644 --- a/src/sdp.ts +++ b/src/sdp.ts @@ -4,11 +4,13 @@ import { bases } from 'multiformats/basics' import * as multihashes from 'multihashes' import { inappropriateMultiaddr, invalidArgument, invalidFingerprint, unsupportedHashAlgorithm } from './error.js' +import { CERTHASH_CODE } from './transport.js' const log = logger('libp2p:webrtc:sdp') -const CERTHASH_CODE: number = 466 -// Get base2 | identity decoders +/** + * Get base2 | identity decoders + */ export const mbdecoder: any = (function () { const decoders = Object.values(bases).map((b) => b.decoder) let acc = decoders[0].or(decoders[1]) @@ -16,7 +18,9 @@ export const mbdecoder: any = (function () { return acc })() -// Extract the ipv from a multiaddr +/** + * Get base2 | identity decoders + */ function ipv (ma: Multiaddr): string { for (const proto of ma.protoNames()) { if (proto.startsWith('ip')) { @@ -24,7 +28,7 @@ function ipv (ma: Multiaddr): string { } } - log('Warning: multiaddr does not appear to contain IP4 or IP6.', ma) + log('Warning: multiaddr does not appear to contain IP4 or IP6, defaulting to IP6', ma) return 'IP6' } @@ -41,28 +45,34 @@ export function certhash (ma: Multiaddr): string { return certhash } -// Convert a certhash into a multihash +/** + * Convert a certhash into a multihash + */ export function decodeCerthash (certhash: string) { const mbdecoded = mbdecoder.decode(certhash) return multihashes.decode(mbdecoded) } -// Extract the fingerprint from a multiaddr +/** + * Extract the fingerprint from a multiaddr + */ export function ma2Fingerprint (ma: Multiaddr): string[] { // certhash_value is a multibase encoded multihash encoded string const mhdecoded = decodeCerthash(certhash(ma)) const prefix = toSupportedHashFunction(mhdecoded.name) - const fp = mhdecoded.digest.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '') - const sdp = fp.match(/.{1,2}/g) + const fingerprint = mhdecoded.digest.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '') + const sdp = fingerprint.match(/.{1,2}/g) if (sdp == null) { - throw invalidFingerprint(fp, ma.toString()) + throw invalidFingerprint(fingerprint, ma.toString()) } - return [`${prefix.toUpperCase()} ${sdp.join(':').toUpperCase()}`, fp] + return [`${prefix.toUpperCase()} ${sdp.join(':').toUpperCase()}`, fingerprint] } -// Normalize the hash name from a given multihash has name +/** + * Normalize the hash name from a given multihash has name + */ export function toSupportedHashFunction (name: multihashes.HashName): string { switch (name) { case 'sha1': @@ -76,7 +86,9 @@ export function toSupportedHashFunction (name: multihashes.HashName): string { } } -// Convert a multiaddr into a SDP +/** + * Convert a multiaddr into a SDP + */ function ma2sdp (ma: Multiaddr, ufrag: string): string { const { host, port } = ma.toOptions() const ipVersion = ipv(ma) @@ -99,7 +111,9 @@ a=max-message-size:100000 a=candidate:1467250027 1 UDP 1467250027 ${host} ${port} typ host\r\n` } -// Create an answer SDP from a multiaddr +/** + * Create an answer SDP from a multiaddr + */ export function fromMultiAddr (ma: Multiaddr, ufrag: string): RTCSessionDescriptionInit { return { type: 'answer', @@ -107,7 +121,9 @@ export function fromMultiAddr (ma: Multiaddr, ufrag: string): RTCSessionDescript } } -// Replace the ufrag and password values in a SDP +/** + * Replace (munge) the ufrag and password values in a SDP + */ export function munge (desc: RTCSessionDescriptionInit, ufrag: string): RTCSessionDescriptionInit { if (desc.sdp === undefined) { throw invalidArgument("Can't munge a missing SDP") diff --git a/src/stream.ts b/src/stream.ts index 42d4e95..9c42f83 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -12,6 +12,9 @@ import * as pb from '../proto_ts/message.js' const log = logger('libp2p:webrtc:stream') +/** + * Constructs a default StreamStat + */ export function defaultStat (dir: Direction): StreamStat { return { direction: dir, @@ -23,9 +26,26 @@ export function defaultStat (dir: Direction): StreamStat { } interface StreamInitOpts { + /** + * The network channel used for bidirectional peer-to-peer transfers of arbitrary data + * + * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel} + */ channel: RTCDataChannel + + /** + * User defined stream metadata + */ metadata?: Record + + /** + * Stats about this stream + */ stat: StreamStat + + /** + * Callback to invoke when the stream is closed. + */ closeCb?: (stream: WebRTCStream) => void } @@ -33,7 +53,18 @@ interface StreamInitOpts { * State transitions for a stream */ interface StreamStateInput { + /** + * Outbound conections are opened by the local node, inbound streams are opened by the remote + */ direction: 'inbound' | 'outbound' + + /** + * Message flag from the protobuffs + * + * 0 = FIN + * 1 = STOP_SENDING + * 2 = RESET + */ flag: pb.Message_Flag } diff --git a/src/transport.ts b/src/transport.ts index be2a6f6..24d07db 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -19,53 +19,93 @@ import * as sdp from './sdp.js' import { WebRTCStream } from './stream.js' const log = logger('libp2p:webrtc:transport') + +/** + * The time to wait, in milliseconds, for the data channel handshake to complete + */ const HANDSHAKE_TIMEOUT_MS = 10000 -const WEBRTC_CODE: number = 280 -const CERTHASH_CODE: number = 466 +/** + * Created by converting the hexadecimal protocol code to an integer. + * + * {@link https://github.com/multiformats/multiaddr/blob/master/protocols.csv} + */ +export const WEBRTC_CODE: number = 280 + +/** + * Created by converting the hexadecimal protocol code to an integer. + * + * {@link https://github.com/multiformats/multiaddr/blob/master/protocols.csv} + */ +export const CERTHASH_CODE: number = 466 + +/** + * The peer for this transport + */ +// @TODO(ddimaria): seems like an unnessary abstraction, consider removing export interface WebRTCTransportComponents { peerId: PeerId } export class WebRTCTransport implements Transport { + /** + * The peer for this transport + */ private readonly components: WebRTCTransportComponents constructor (components: WebRTCTransportComponents) { this.components = components } + /** + * Dial a given multiaddr + */ async dial (ma: Multiaddr, options: WebRTCDialOptions): Promise { const rawConn = await this._connect(ma, options) log(`dialing address - ${ma.toString()}`) return rawConn } + /** + * Create transport listeners + */ createListener (options: CreateListenerOptions): Listener { throw unimplemented('WebRTCTransport.createListener') } - // Filter out invalid multiaddrs + /** + * Takes a list of `Multiaddr`s and returns only valid addresses for the transport + */ filter (multiaddrs: Multiaddr[]): Multiaddr[] { return multiaddrs.filter(validMa) } - // Implement toString() for WebRTCTransport + /** + * Implement toString() for WebRTCTransport + */ get [Symbol.toStringTag] (): string { return '@libp2p/webrtc' } + /** + * Symbol.for('@libp2p/transport') + */ get [symbol] (): true { return true } - // Connect to a peer + /** + * Connect to a peer using a multiaddr + */ async _connect (ma: Multiaddr, options: WebRTCDialOptions): Promise { const rps = ma.getPeerId() + if (rps === null) { throw inappropriateMultiaddr("we need to have the remote's PeerId") } const remoteCerthash = sdp.decodeCerthash(sdp.certhash(ma)) + // ECDSA is preferred over RSA here. From our testing we find that P-256 elliptic // curve is supported by Pion, webrtc-rs, as well as Chromium (P-228 and P-384 // was not supported in Chromium). We use the same hash function as found in the @@ -162,8 +202,10 @@ export class WebRTCTransport implements Transport { return await options.upgrader.upgradeOutbound(maConn, { skipProtection: true, skipEncryption: true, muxerFactory }) } - // Generate a noise prologue from the peer connection's certificate. - // noise prologue = bytes('libp2p-webrtc-noise:') + noise-responder fingerprint + noise-initiator fingerprint + /** + * Generate a noise prologue from the peer connection's certificate. + * noise prologue = bytes('libp2p-webrtc-noise:') + noise-responder fingerprint + noise-initiator fingerprint + */ private generateNoisePrologue (pc: RTCPeerConnection, hashName: multihashes.HashName, ma: Multiaddr): Uint8Array { if (pc.getConfiguration().certificates?.length === 0) { throw invalidArgument('no local certificate') @@ -191,6 +233,10 @@ export class WebRTCTransport implements Transport { } } +/** + * Determine if a given multiaddr contains a WebRTC Code (280), + * a Certhash Code (466) and a PeerId + */ function validMa (ma: Multiaddr): boolean { const codes = ma.protoCodes() return codes.includes(WEBRTC_CODE) && codes.includes(CERTHASH_CODE) && ma.getPeerId() != null From 6d7c52872cb2148264a6af3123f0532b57700481 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 17 Nov 2022 17:15:15 -0700 Subject: [PATCH 02/18] Ignore coverage reports --- .coverage/coverage-final.json | 2 -- .coverage/coverage-pw.json | 1 - .gitignore | 2 ++ .nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json | 1 - .../processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json | 1 - .nyc_output/processinfo/index.json | 1 - 6 files changed, 2 insertions(+), 6 deletions(-) delete mode 100644 .coverage/coverage-final.json delete mode 100644 .coverage/coverage-pw.json delete mode 100644 .nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json delete mode 100644 .nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json delete mode 100644 .nyc_output/processinfo/index.json diff --git a/.coverage/coverage-final.json b/.coverage/coverage-final.json deleted file mode 100644 index fe401cc..0000000 --- a/.coverage/coverage-final.json +++ /dev/null @@ -1,2 +0,0 @@ -{"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/test/connection.browser.spec.ts": {"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/test/connection.browser.spec.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":22}},"1":{"start":{"line":2,"column":0},"end":{"line":2,"column":0}},"2":{"start":{"line":3,"column":0},"end":{"line":3,"column":69}},"3":{"start":{"line":4,"column":0},"end":{"line":4,"column":39}},"4":{"start":{"line":5,"column":0},"end":{"line":5,"column":34}},"5":{"start":{"line":6,"column":0},"end":{"line":6,"column":32}},"6":{"start":{"line":7,"column":0},"end":{"line":7,"column":54}},"7":{"start":{"line":8,"column":0},"end":{"line":8,"column":27}},"8":{"start":{"line":9,"column":0},"end":{"line":9,"column":0}},"9":{"start":{"line":10,"column":0},"end":{"line":10,"column":38}},"10":{"start":{"line":11,"column":0},"end":{"line":11,"column":0}},"11":{"start":{"line":12,"column":0},"end":{"line":12,"column":47}},"12":{"start":{"line":13,"column":0},"end":{"line":13,"column":57}},"13":{"start":{"line":14,"column":0},"end":{"line":14,"column":74}},"14":{"start":{"line":15,"column":0},"end":{"line":15,"column":45}},"15":{"start":{"line":16,"column":0},"end":{"line":16,"column":113}},"16":{"start":{"line":17,"column":0},"end":{"line":17,"column":64}},"17":{"start":{"line":18,"column":0},"end":{"line":18,"column":34}},"18":{"start":{"line":19,"column":0},"end":{"line":19,"column":93}},"19":{"start":{"line":20,"column":0},"end":{"line":20,"column":0}},"20":{"start":{"line":21,"column":0},"end":{"line":21,"column":43}},"21":{"start":{"line":22,"column":0},"end":{"line":22,"column":55}},"22":{"start":{"line":23,"column":0},"end":{"line":23,"column":7}},"23":{"start":{"line":24,"column":0},"end":{"line":24,"column":0}},"24":{"start":{"line":25,"column":0},"end":{"line":25,"column":55}},"25":{"start":{"line":26,"column":0},"end":{"line":26,"column":49}},"26":{"start":{"line":27,"column":0},"end":{"line":27,"column":45}},"27":{"start":{"line":28,"column":0},"end":{"line":28,"column":74}},"28":{"start":{"line":29,"column":0},"end":{"line":29,"column":45}},"29":{"start":{"line":30,"column":0},"end":{"line":30,"column":113}},"30":{"start":{"line":31,"column":0},"end":{"line":31,"column":64}},"31":{"start":{"line":32,"column":0},"end":{"line":32,"column":37}},"32":{"start":{"line":33,"column":0},"end":{"line":33,"column":51}},"33":{"start":{"line":34,"column":0},"end":{"line":34,"column":34}},"34":{"start":{"line":35,"column":0},"end":{"line":35,"column":32}},"35":{"start":{"line":36,"column":0},"end":{"line":36,"column":28}},"36":{"start":{"line":37,"column":0},"end":{"line":37,"column":57}},"37":{"start":{"line":38,"column":0},"end":{"line":38,"column":45}},"38":{"start":{"line":39,"column":0},"end":{"line":39,"column":56}},"39":{"start":{"line":40,"column":0},"end":{"line":40,"column":25}},"40":{"start":{"line":41,"column":0},"end":{"line":41,"column":14}},"41":{"start":{"line":42,"column":0},"end":{"line":42,"column":7}},"42":{"start":{"line":43,"column":0},"end":{"line":43,"column":37}},"43":{"start":{"line":44,"column":0},"end":{"line":44,"column":7}},"44":{"start":{"line":45,"column":0},"end":{"line":45,"column":0}},"45":{"start":{"line":46,"column":0},"end":{"line":46,"column":6}},"46":{"start":{"line":47,"column":0},"end":{"line":47,"column":0}},"47":{"start":{"line":48,"column":0},"end":{"line":48,"column":9}}},"s":{"0":1,"1":1,"2":1,"3":1,"4":1,"5":1,"6":1,"7":1,"8":1,"9":1,"10":1,"11":1,"12":1,"13":1,"14":1,"15":1,"16":1,"17":1,"18":1,"19":1,"20":1,"21":1,"22":1,"23":1,"24":1,"25":1,"26":1,"27":1,"28":1,"29":1,"30":1,"31":1,"32":1,"33":1,"34":1,"35":1,"36":1,"37":1,"38":1,"39":1,"40":1,"41":1,"42":1,"43":1,"44":1,"45":1,"46":1,"47":1},"branchMap":{},"b":{},"fnMap":{},"f":{}} -} diff --git a/.coverage/coverage-pw.json b/.coverage/coverage-pw.json deleted file mode 100644 index 5821601..0000000 --- a/.coverage/coverage-pw.json +++ /dev/null @@ -1 +0,0 @@ -{"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/error.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/error.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/sdp.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/sdp.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/proto_ts/message.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/proto_ts/message.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/stream.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/stream.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/util.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/util.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/maconn.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/maconn.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/muxer.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/muxer.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/options.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/options.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/transport.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/transport.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}},"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/index.ts":{"path":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/src/index.ts","all":false,"statementMap":{"0":{"start":{"line":1,"column":0},"end":{"line":1,"column":0}}},"s":{"0":1},"branchMap":{},"b":{},"fnMap":{},"f":{}}} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 4dd334d..ad84570 100644 --- a/.gitignore +++ b/.gitignore @@ -18,6 +18,8 @@ public/css/main.css # Coverage reports coverage +.coverage +.nyc_output # API keys and secrets .env diff --git a/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json b/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json deleted file mode 100644 index 9e26dfe..0000000 --- a/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json +++ /dev/null @@ -1 +0,0 @@ -{} \ No newline at end of file diff --git a/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json b/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json deleted file mode 100644 index ad3997a..0000000 --- a/.nyc_output/processinfo/86a415dc-f2ec-4a3d-b888-db58545f6218.json +++ /dev/null @@ -1 +0,0 @@ -{"parent":null,"pid":98624,"argv":["/Users/daviddimaria/.nvm/versions/node/v16.15.0/bin/node","/Users/daviddimaria/work/conduit/js-libp2p-webrtc/node_modules/.bin/mocha","--temp-directory",".coverage","--all"],"execArgv":[],"cwd":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc","time":1668528326079,"ppid":98623,"coverageFilename":"/Users/daviddimaria/work/conduit/js-libp2p-webrtc/.nyc_output/86a415dc-f2ec-4a3d-b888-db58545f6218.json","externalId":"","uuid":"86a415dc-f2ec-4a3d-b888-db58545f6218","files":[]} \ No newline at end of file diff --git a/.nyc_output/processinfo/index.json b/.nyc_output/processinfo/index.json deleted file mode 100644 index bd3a80d..0000000 --- a/.nyc_output/processinfo/index.json +++ /dev/null @@ -1 +0,0 @@ -{"processes":{"86a415dc-f2ec-4a3d-b888-db58545f6218":{"parent":null,"children":[]}},"files":{},"externalIds":{}} \ No newline at end of file From 37794cd4e4e2f01e9cd2b6cef8d51d3233dc9f1c Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Wed, 30 Nov 2022 09:43:42 -0700 Subject: [PATCH 03/18] Test maconn, update existing test and code to accomodate tests --- src/maconn.ts | 3 +- test/maconn.browser.spec.ts | 33 ++++++++++++ test/util.ts | 104 ++++++++++++++++++------------------ 3 files changed, 87 insertions(+), 53 deletions(-) create mode 100644 test/maconn.browser.spec.ts diff --git a/src/maconn.ts b/src/maconn.ts index 65725ef..cd9d30d 100644 --- a/src/maconn.ts +++ b/src/maconn.ts @@ -28,7 +28,7 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection { /** * WebRTC Peer Connection */ - private readonly peerConnection: RTCPeerConnection; + readonly peerConnection: RTCPeerConnection; /** * The multiaddr address used to communicate with the remote peer @@ -61,6 +61,7 @@ export class WebRTCMultiaddrConnection implements MultiaddrConnection { log.error('error closing connection', err) } + this.timeline.close = new Date().getTime() this.peerConnection.close() } } diff --git a/test/maconn.browser.spec.ts b/test/maconn.browser.spec.ts new file mode 100644 index 0000000..88a524b --- /dev/null +++ b/test/maconn.browser.spec.ts @@ -0,0 +1,33 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ + +import { WebRTCMultiaddrConnection } from './../src/maconn' +import { createConnectedRTCPeerConnectionPair } from './util' + +import { multiaddr } from '@multiformats/multiaddr' +import { expect } from 'chai' + +describe('Multiaddr Connection', () => { + it('can open and close', async () => { + const peers = await createConnectedRTCPeerConnectionPair(); + console.log(peers) + // const peerConnection = new RTCPeerConnection() + // peerConnection.createDataChannel('whatever', { negotiated: true, id: 91 }) + + const remoteAddr = multiaddr('/ip4/1.2.3.4/udp/1234/webrtc/certhash/uEiAUqV7kzvM1wI5DYDc1RbcekYVmXli_Qprlw3IkiEg6tQ') + const maConn = new WebRTCMultiaddrConnection({ + peerConnection: peers[0], + remoteAddr, + timeline: { + open: (new Date()).getTime() + } + }) + + console.log(maConn.peerConnection) + expect(maConn.timeline.close).to.be.undefined + + await maConn.close() + console.log(maConn.peerConnection) + + expect(maConn.timeline.close).to.not.be.undefined + }) +}) diff --git a/test/util.ts b/test/util.ts index c272783..d35f886 100644 --- a/test/util.ts +++ b/test/util.ts @@ -12,69 +12,69 @@ export const expectError = (error: unknown, message: string) => { // import {createEd25519PeerId} from '@libp2p/peer-id-factory'; // import {mockRegistrar, mockUpgrader} from '@libp2p/interface-mocks'; // import {Components} from '@libp2p/components'; -// import defer, {DeferredPromise} from 'p-defer'; +import defer, {DeferredPromise} from 'p-defer'; // import {MockConnection} from '../src/connection'; // import {Multiaddr} from '@multiformats/multiaddr'; // import {v4} from 'uuid'; -// import {Registrar, StreamHandler} from '@libp2p/interface-registrar'; -// import { pipe } from 'it-pipe'; -// import { logger } from '@libp2p/logger'; +import {/*Registrar, */StreamHandler} from '@libp2p/interface-registrar'; +import { pipe } from 'it-pipe'; +import { logger } from '@libp2p/logger'; -// const log = logger('libp2p:webrtc:test:util'); +const log = logger('libp2p:webrtc:test:util'); -// export const echoHandler: StreamHandler = ({ stream }) => pipe(stream.source, stream.sink); +export const echoHandler: StreamHandler = ({ stream }) => pipe(stream.source, stream.sink); -// export async function createConnectedRTCPeerConnectionPair(): Promise { -// let [client, server] = [new RTCPeerConnection(), new RTCPeerConnection()]; -// // log('created peer connections'); -// // we don't need auth for a local test but we need a component for candidate gathering -// client.createDataChannel('data'); -// client.onicecandidate = ({candidate}) => { -// if (candidate !== null) { -// server.addIceCandidate(candidate); -// } -// }; -// server.onicecandidate = ({candidate}) => { -// if (candidate !== null) { -// client.addIceCandidate(candidate); -// } -// }; -// let resolveOnConnect = (pc: RTCPeerConnection): DeferredPromise => { -// let promise: DeferredPromise = defer(); -// pc.onconnectionstatechange = (_evt) => { -// switch (pc.connectionState) { -// case 'connected': -// log.trace('pc connected'); -// promise.resolve(); -// return; -// case 'failed': -// case 'disconnected': -// promise.reject(`Peerconnection state: ${pc.connectionState}`); -// return; -// } -// }; -// return promise; -// } +export async function createConnectedRTCPeerConnectionPair(): Promise { + let [client, server] = [new RTCPeerConnection(), new RTCPeerConnection()]; + // log('created peer connections'); + // we don't need auth for a local test but we need a component for candidate gathering + client.createDataChannel('data'); + client.onicecandidate = ({candidate}) => { + if (candidate !== null) { + server.addIceCandidate(candidate); + } + }; + server.onicecandidate = ({candidate}) => { + if (candidate !== null) { + client.addIceCandidate(candidate); + } + }; + let resolveOnConnect = (pc: RTCPeerConnection): DeferredPromise => { + let promise: DeferredPromise = defer(); + pc.onconnectionstatechange = (_evt) => { + switch (pc.connectionState) { + case 'connected': + log.trace('pc connected'); + promise.resolve(); + return; + case 'failed': + case 'disconnected': + promise.reject(`Peerconnection state: ${pc.connectionState}`); + return; + } + }; + return promise; + } -// let clientConnected = resolveOnConnect(client); -// let serverConnected = resolveOnConnect(server); -// log('set callbacks on peerconnections'); + let clientConnected = resolveOnConnect(client); + let serverConnected = resolveOnConnect(server); + log('set callbacks on peerconnections'); -// let clientOffer = await client.createOffer(); -// await client.setLocalDescription(clientOffer); -// await server.setRemoteDescription(clientOffer); -// let serverAnswer = await server.createAnswer(); -// await server.setLocalDescription(serverAnswer); -// await client.setRemoteDescription(serverAnswer); -// log('completed sdp exchange'); + let clientOffer = await client.createOffer(); + await client.setLocalDescription(clientOffer); + await server.setRemoteDescription(clientOffer); + let serverAnswer = await server.createAnswer(); + await server.setLocalDescription(serverAnswer); + await client.setRemoteDescription(serverAnswer); + log('completed sdp exchange'); -// await Promise.all([clientConnected.promise, serverConnected.promise]) + await Promise.all([clientConnected.promise, serverConnected.promise]) -// log.trace(`clientstate: ${client.connectionState}, serverstate: ${server.connectionState}`) + log.trace(`clientstate: ${client.connectionState}, serverstate: ${server.connectionState}`) -// log('created peer connections'); -// return [client, server]; -// } + log('created peer connections'); + return [client, server]; +} // export async function createConnectionPair(): Promise<{ connection: ic.Connection, registrar: Registrar }[]> { // let [clientPeerId, serverPeerId] = await Promise.all([createEd25519PeerId(), createEd25519PeerId()]); From 232d171337ba0cae9a0e4b6dfd23cdac8f1451f0 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Wed, 30 Nov 2022 18:05:24 -0700 Subject: [PATCH 04/18] Create a browser-to-server example --- examples/browser-to-server/README.md | 34 + examples/browser-to-server/index.html | 41 + examples/browser-to-server/index.js | 39 + examples/browser-to-server/package.json | 18 + examples/browser-to-server/vite.config.js | 11 + examples/go-libp2p-server/.gitignore | 1 + examples/go-libp2p-server/go.mod | 111 +++ examples/go-libp2p-server/go.sum | 904 ++++++++++++++++++++++ examples/go-libp2p-server/main.go | 96 +++ package.json | 3 +- src/muxer.ts | 4 +- test/maconn.browser.spec.ts | 15 +- test/server-multiaddr.ts | 2 +- test/transport.browser.spec.ts | 8 +- test/util.ts | 122 ++- 15 files changed, 1328 insertions(+), 81 deletions(-) create mode 100644 examples/browser-to-server/README.md create mode 100644 examples/browser-to-server/index.html create mode 100644 examples/browser-to-server/index.js create mode 100644 examples/browser-to-server/package.json create mode 100644 examples/browser-to-server/vite.config.js create mode 100644 examples/go-libp2p-server/.gitignore create mode 100644 examples/go-libp2p-server/go.mod create mode 100644 examples/go-libp2p-server/go.sum create mode 100644 examples/go-libp2p-server/main.go diff --git a/examples/browser-to-server/README.md b/examples/browser-to-server/README.md new file mode 100644 index 0000000..19d79ce --- /dev/null +++ b/examples/browser-to-server/README.md @@ -0,0 +1,34 @@ +# js-libp2p-webrtc Browser to Server + +This example leverages the [vite bundler](https://vitejs.dev/) to compile and serve the libp2p code in the browser. You can use other bundlers such as Webpack, but we will not be covering them here. + +## Running the Go Server + +To run the Go LibP2P WebRTC server: + +```shell +npm run go-libp2p-server +``` + +Copy the multiaddress in the output. + +## Running the Example + +In a separate console tab, install dependencies and start the Vite server: + +```shell +npm i && npm run start +``` + +The browser window will automatically open. +Using the copied multiaddress from the Go server, pasted that value into the `Server MultiAddress` input and click the `Connect` button. +Once the peer is connected, click the message section will appear. Enter a message and click the `Send` button. + +The output should look like: + +```text +Dialing /ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb +Peer connected '/ip4/10.0.1.5/udp/54375/webrtc/certhash/uEiADy8JubdWrAzseyzfXFyCpdRN02eWZg86tjCrTCA5dbQ/p2p/12D3KooWEG7N4bnZfFBNZE7WG6xm2P4Sr6sonMwyD4HCAqApEthb' +Sending message 'hello' +Received message 'hello' +``` \ No newline at end of file diff --git a/examples/browser-to-server/index.html b/examples/browser-to-server/index.html new file mode 100644 index 0000000..24ff11f --- /dev/null +++ b/examples/browser-to-server/index.html @@ -0,0 +1,41 @@ + + + + + + js-libp2p WebRTC + + + +
+
+ + + +
+
+ + + +
+
+
+ + + diff --git a/examples/browser-to-server/index.js b/examples/browser-to-server/index.js new file mode 100644 index 0000000..fdd5487 --- /dev/null +++ b/examples/browser-to-server/index.js @@ -0,0 +1,39 @@ +import { createLibp2p } from 'libp2p' +import { Noise } from '@chainsafe/libp2p-noise' +import { multiaddr } from '@multiformats/multiaddr' +import first from "it-first"; +import { pipe } from "it-pipe"; +import { fromString, toString } from "uint8arrays"; +import { webRTC } from 'js-libp2p-webrtc' + + let stream; + const output = document.getElementById('output') + const sendSection = document.getElementById('send-section') + const appendOutput = (line) => output.innerText += `${line}\n` + const clean = (line) => line.replaceAll('\n', '') + + const node = await createLibp2p({ + transports: [webRTC()], + connectionEncryption: [() => new Noise()], + }); + + await node.start() + + node.connectionManager.addEventListener('peer:connect', (connection) => { + appendOutput(`Peer connected '${node.getConnections().map(c => c.remoteAddr.toString())}'`) + sendSection.style.display = 'block' + }) + + window.connect.onclick = async () => { + const ma = multiaddr(window.peer.value) + appendOutput(`Dialing ${ma}`) + stream = await node.dialProtocol(ma, ['/echo/1.0.0']) + } + + window.send.onclick = async () => { + const message = `${window.message.value}\n` + appendOutput(`Sending message '${clean(message)}'`) + const response = await pipe([fromString(message)], stream, async (source) => await first(source)) + const responseDecoded = toString(response.slice(0, response.length)); + appendOutput(`Received message '${clean(responseDecoded)}'`) + } \ No newline at end of file diff --git a/examples/browser-to-server/package.json b/examples/browser-to-server/package.json new file mode 100644 index 0000000..8d75a28 --- /dev/null +++ b/examples/browser-to-server/package.json @@ -0,0 +1,18 @@ +{ + "name": "js-libp2p-webrtc-browser-to-server", + "version": "1.0.0", + "description": "Connect a browser to a server", + "type": "module", + "scripts": { + "start": "vite", + "go-libp2p-server": "cd ../go-libp2p-server && go build && ./go-libp2p-server" + }, + "dependencies": { + "@chainsafe/libp2p-noise": "^9.0.0", + "@multiformats/multiaddr": "^11.0.5", + "js-libp2p-webrtc": "github:little-bear-labs/js-libp2p-webrtc#libp2p-demo-2022", + "it-pushable": "^3.1.0", + "libp2p": "^0.40.0", + "vite": "^3.1.0" + } +} diff --git a/examples/browser-to-server/vite.config.js b/examples/browser-to-server/vite.config.js new file mode 100644 index 0000000..0298488 --- /dev/null +++ b/examples/browser-to-server/vite.config.js @@ -0,0 +1,11 @@ +export default { + build: { + target: 'es2020' + }, + optimizeDeps: { + esbuildOptions: { target: 'es2020', supported: { bigint: true } } + }, + server: { + open: true + } +} \ No newline at end of file diff --git a/examples/go-libp2p-server/.gitignore b/examples/go-libp2p-server/.gitignore new file mode 100644 index 0000000..baadac2 --- /dev/null +++ b/examples/go-libp2p-server/.gitignore @@ -0,0 +1 @@ +go-libp2p-server \ No newline at end of file diff --git a/examples/go-libp2p-server/go.mod b/examples/go-libp2p-server/go.mod new file mode 100644 index 0000000..b1fe819 --- /dev/null +++ b/examples/go-libp2p-server/go.mod @@ -0,0 +1,111 @@ +module github.com/little-bear-labs/js-libp2p/go-libp2p-server + +go 1.18 + +replace github.com/libp2p/go-libp2p => github.com/ckousik/go-libp2p v0.23.3-0.20221029141116-67b7c6290b11 + +require github.com/libp2p/go-libp2p v0.23.2 + +require ( + github.com/benbjohnson/clock v1.3.0 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cespare/xxhash/v2 v2.1.2 // indirect + github.com/containerd/cgroups v1.0.4 // indirect + github.com/coreos/go-systemd/v22 v22.4.0 // indirect + github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/elastic/gosigar v0.14.2 // indirect + github.com/flynn/noise v1.0.0 // indirect + github.com/francoispqt/gojay v1.2.13 // indirect + github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 // indirect + github.com/godbus/dbus/v5 v5.1.0 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/mock v1.6.0 // indirect + github.com/golang/protobuf v1.5.2 // indirect + github.com/google/gopacket v1.1.19 // indirect + github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect + github.com/google/uuid v1.3.0 // indirect + github.com/gorilla/websocket v1.5.0 // indirect + github.com/huin/goupnp v1.0.3 // indirect + github.com/ipfs/go-cid v0.3.2 // indirect + github.com/ipfs/go-log/v2 v2.5.1 // indirect + github.com/jackpal/go-nat-pmp v1.0.2 // indirect + github.com/jbenet/go-temp-err-catcher v0.1.0 // indirect + github.com/klauspost/compress v1.15.10 // indirect + github.com/klauspost/cpuid/v2 v2.1.1 // indirect + github.com/koron/go-ssdp v0.0.3 // indirect + github.com/libp2p/go-buffer-pool v0.1.0 // indirect + github.com/libp2p/go-cidranger v1.1.0 // indirect + github.com/libp2p/go-flow-metrics v0.1.0 // indirect + github.com/libp2p/go-libp2p-asn-util v0.2.0 // indirect + github.com/libp2p/go-msgio v0.2.0 // indirect + github.com/libp2p/go-nat v0.1.0 // indirect + github.com/libp2p/go-netroute v0.2.0 // indirect + github.com/libp2p/go-openssl v0.1.0 // indirect + github.com/libp2p/go-reuseport v0.2.0 // indirect + github.com/libp2p/go-yamux/v4 v4.0.0 // indirect + github.com/lucas-clemente/quic-go v0.30.0 // indirect + github.com/marten-seemann/qtls-go1-18 v0.1.3 // indirect + github.com/marten-seemann/qtls-go1-19 v0.1.1 // indirect + github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd // indirect + github.com/mattn/go-isatty v0.0.16 // indirect + github.com/mattn/go-pointer v0.0.1 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect + github.com/miekg/dns v1.1.50 // indirect + github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b // indirect + github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc // indirect + github.com/minio/sha256-simd v1.0.0 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/multiformats/go-base32 v0.1.0 // indirect + github.com/multiformats/go-base36 v0.1.0 // indirect + github.com/multiformats/go-multiaddr v0.7.0 // indirect + github.com/multiformats/go-multiaddr-dns v0.3.1 // indirect + github.com/multiformats/go-multiaddr-fmt v0.1.0 // indirect + github.com/multiformats/go-multibase v0.1.1 // indirect + github.com/multiformats/go-multicodec v0.6.0 // indirect + github.com/multiformats/go-multihash v0.2.1 // indirect + github.com/multiformats/go-multistream v0.3.3 // indirect + github.com/multiformats/go-varint v0.0.6 // indirect + github.com/onsi/ginkgo/v2 v2.2.0 // indirect + github.com/opencontainers/runtime-spec v1.0.2 // indirect + github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 // indirect + github.com/pion/datachannel v1.5.2 // indirect + github.com/pion/dtls/v2 v2.1.5 // indirect + github.com/pion/ice/v2 v2.2.6 // indirect + github.com/pion/interceptor v0.1.12 // indirect + github.com/pion/logging v0.2.2 // indirect + github.com/pion/mdns v0.0.5 // indirect + github.com/pion/randutil v0.1.0 // indirect + github.com/pion/rtcp v1.2.10 // indirect + github.com/pion/rtp v1.7.13 // indirect + github.com/pion/sctp v1.8.2 // indirect + github.com/pion/sdp/v3 v3.0.6 // indirect + github.com/pion/srtp/v2 v2.0.10 // indirect + github.com/pion/stun v0.3.5 // indirect + github.com/pion/transport v0.13.1 // indirect + github.com/pion/turn/v2 v2.0.8 // indirect + github.com/pion/udp v0.1.1 // indirect + github.com/pion/webrtc/v3 v3.1.43 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.13.0 // indirect + github.com/prometheus/client_model v0.2.0 // indirect + github.com/prometheus/common v0.37.0 // indirect + github.com/prometheus/procfs v0.8.0 // indirect + github.com/raulk/go-watchdog v1.3.0 // indirect + github.com/rogpeppe/go-internal v1.9.0 // indirect + github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 // indirect + github.com/spaolacci/murmur3 v1.1.0 // indirect + go.uber.org/atomic v1.10.0 // indirect + go.uber.org/multierr v1.8.0 // indirect + go.uber.org/zap v1.23.0 // indirect + golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect + golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b // indirect + golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 // indirect + golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 // indirect + golang.org/x/sync v0.0.0-20220907140024-f12130a52804 // indirect + golang.org/x/sys v0.0.0-20220913175220-63ea55921009 // indirect + golang.org/x/tools v0.1.12 // indirect + google.golang.org/protobuf v1.28.1 // indirect + lukechampine.com/blake3 v1.1.7 // indirect +) diff --git a/examples/go-libp2p-server/go.sum b/examples/go-libp2p-server/go.sum new file mode 100644 index 0000000..b75fec9 --- /dev/null +++ b/examples/go-libp2p-server/go.sum @@ -0,0 +1,904 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.31.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.37.0/go.mod h1:TS1dMSSfndXH133OKGwekG838Om/cQT0BUHV3HcBgoo= +cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= +cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= +cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= +cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= +cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= +cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= +cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= +cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= +cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= +cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= +cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= +cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= +cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= +cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= +cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= +cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= +cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= +cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= +cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= +cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= +cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= +cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= +cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= +cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= +cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= +cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= +cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= +cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= +dmitri.shuralyov.com/app/changes v0.0.0-20180602232624-0a106ad413e3/go.mod h1:Yl+fi1br7+Rr3LqpNJf1/uxUdtRUV+Tnj0o93V2B9MU= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +dmitri.shuralyov.com/html/belt v0.0.0-20180602232347-f7d459c86be0/go.mod h1:JLBrvjyP0v+ecvNYvCpyZgu5/xkfAUhi6wJj28eUfSU= +dmitri.shuralyov.com/service/change v0.0.0-20181023043359-a85b471d5412/go.mod h1:a1inKt/atXimZ4Mv927x+r7UpyzRUf4emIoiiSC2TN4= +dmitri.shuralyov.com/state v0.0.0-20180228185332-28bcc343414c/go.mod h1:0PRwlb0D6DFvNNtx+9ybjezNCa8XF0xaYcETyp6rHWU= +git.apache.org/thrift.git v0.0.0-20180902110319-2566ecd5d999/go.mod h1:fPE2ZNJGynbRyZ4dJvy6G277gSllfV2HJqblrnkyeyg= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= +github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bradfitz/go-smtpd v0.0.0-20170404230938-deb6d6237625/go.mod h1:HYsPBTaaSFSlLx/70C2HPIMNZpVV8+vt/A+FMnYP11g= +github.com/buger/jsonparser v0.0.0-20181115193947-bf1c66bbce23/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= +github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/ckousik/go-libp2p v0.23.3-0.20221029141116-67b7c6290b11 h1:jDbXA1WFY34vGicMQFOKzrNOLqsoEPjPCyc1BBif3cs= +github.com/ckousik/go-libp2p v0.23.3-0.20221029141116-67b7c6290b11/go.mod h1:vfPGrhHarx5Qc9/ZhsjsBoqq57Or2Yy7mJJu5ia1j8A= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/containerd/cgroups v0.0.0-20201119153540-4cbc285b3327/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.4 h1:jN/mbWBEaz+T1pi5OFtnkQ+8qnmEbAr1Oo1FRm5B0dA= +github.com/containerd/cgroups v1.0.4/go.mod h1:nLNQtsF7Sl2HxNebu77i1R0oDlhiTG+kO4JTrUzo6IA= +github.com/coreos/go-systemd v0.0.0-20181012123002-c6f51f82210d/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.4.0 h1:y9YHcjnjynCd/DVbg5j9L/33jQM3MxJlbj/zWskzfGU= +github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c h1:pFUpOrbxDR6AkioZ1ySsx5yxlDQZ8stG2b88gTPxgJU= +github.com/davidlazar/go-crypto v0.0.0-20200604182044-b73af7476f6c/go.mod h1:6UhI8N9EjYm1c2odKpFpAYeR8dsBeM7PtzQhRgxRr9U= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc= +github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elastic/gosigar v0.12.0/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/elastic/gosigar v0.14.2 h1:Dg80n8cr90OZ7x+bAax/QjoW/XqTI11RmA79ZwIm9/4= +github.com/elastic/gosigar v0.14.2/go.mod h1:iXRIGg2tLnu7LBdpqzyQfGDEidKCfWcCMS0WKyPWoMs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/flynn/noise v1.0.0 h1:DlTHqmzmvcEiKj+4RYo/imoswx/4r6iBlCMfVtrMXpQ= +github.com/flynn/noise v1.0.0/go.mod h1:xbMo+0i6+IGbYdJhF31t2eR1BIU0CYc12+BNAKwUTag= +github.com/francoispqt/gojay v1.2.13 h1:d2m3sFjloqoIUQU3TsHBgj6qg/BVGlTBeHDUmyJnXKk= +github.com/francoispqt/gojay v1.2.13/go.mod h1:ehT5mTG4ua4581f1++1WLG0vPdaA9HaiDsoyrBGkyDY= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/gliderlabs/ssh v0.1.1/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= +github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= +github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= +github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gopacket v1.1.17/go.mod h1:UdDNZ1OO62aGYVnPhxT1U6aI7ukYtA/kB8vaU0diBUM= +github.com/google/gopacket v1.1.19 h1:ves8RnFZPGiFnTS0uPQStjwru6uO6h+nlr9j6fL7kF8= +github.com/google/gopacket v1.1.19/go.mod h1:iJ8V8n6KS+z2U1A8pUwu8bW5SyEMkXJB8Yo/Vo+TKTo= +github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= +github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= +github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE= +github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/gax-go v2.0.0+incompatible/go.mod h1:SFVmujtThgffbyetf+mdk2eWhX2bMyUtNHzFKcPA9HY= +github.com/googleapis/gax-go/v2 v2.0.3/go.mod h1:LLvjysVCY1JZeum8Z6l8qUty8fiNwE08qbEPm1M08qg= +github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= +github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= +github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/grpc-gateway v1.5.0/go.mod h1:RSKVYQBd5MCa4OVpNdGskqpgL2+G+NZTnrVHpWWfpdw= +github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/huin/goupnp v1.0.0/go.mod h1:n9v9KO1tAxYH82qOn+UTIFQDmx5n1Zxd/ClZDMX7Bnc= +github.com/huin/goupnp v1.0.3 h1:N8No57ls+MnjlB+JPiCVSOyy/ot7MJTqlo7rn+NYSqQ= +github.com/huin/goupnp v1.0.3/go.mod h1:ZxNlw5WqJj6wSsRK5+YfflQGXYfccj5VgQsMNixHM7Y= +github.com/huin/goutil v0.0.0-20170803182201-1ca381bf3150/go.mod h1:PpLOETDnJ0o3iZrZfqZzyLl6l7F3c6L1oWn7OICBi6o= +github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/ipfs/go-cid v0.3.2 h1:OGgOd+JCFM+y1DjWPmVH+2/4POtpDzwcr7VgnB7mZXc= +github.com/ipfs/go-cid v0.3.2/go.mod h1:gQ8pKqT/sUxGY+tIwy1RPpAojYu7jAyCp5Tz1svoupw= +github.com/ipfs/go-detect-race v0.0.1 h1:qX/xay2W3E4Q1U7d9lNs1sU9nvguX0a7319XbyQ6cOk= +github.com/ipfs/go-detect-race v0.0.1/go.mod h1:8BNT7shDZPo99Q74BpGMK+4D8Mn4j46UU0LZ723meps= +github.com/ipfs/go-log/v2 v2.5.1 h1:1XdUzF7048prq4aBjDQQ4SL5RxftpRGdXhNRwKSAlcY= +github.com/ipfs/go-log/v2 v2.5.1/go.mod h1:prSpmC1Gpllc9UYWxDiZDreBYw7zp4Iqp1kOLU9U5UI= +github.com/jackpal/go-nat-pmp v1.0.2 h1:KzKSgb7qkJvOUTqYl9/Hg/me3pWgBmERKrTGD7BdWus= +github.com/jackpal/go-nat-pmp v1.0.2/go.mod h1:QPH045xvCAeXUZOxsnwmrtiCoxIr9eob+4orBN1SBKc= +github.com/jbenet/go-temp-err-catcher v0.1.0 h1:zpb3ZH6wIE8Shj2sKS+khgRvf7T7RABoLk/+KKHggpk= +github.com/jbenet/go-temp-err-catcher v0.1.0/go.mod h1:0kJRvmDZXNMIiJirNPEYfhpPwbGVtZVWC34vc5WLsDk= +github.com/jellevandenhooff/dkim v0.0.0-20150330215556-f50fe3d243e1/go.mod h1:E0B/fFc00Y+Rasa88328GlI/XbtyysCtTHZS8h7IrBU= +github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= +github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= +github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.15.10 h1:Ai8UzuomSCDw90e1qNMtb15msBXsNpH6gzkkENQNcJo= +github.com/klauspost/compress v1.15.10/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM= +github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.1.1 h1:t0wUqjowdm8ezddV5k0tLWVklVuvLJpoHeb4WBdydm0= +github.com/klauspost/cpuid/v2 v2.1.1/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/koron/go-ssdp v0.0.0-20191105050749-2e1c40ed0b5d/go.mod h1:5Ky9EC2xfoUKUor0Hjgi2BJhCSXJfMOFlmyYrVKGQMk= +github.com/koron/go-ssdp v0.0.3 h1:JivLMY45N76b4p/vsWGOKewBQu6uf39y8l+AQ7sDKx8= +github.com/koron/go-ssdp v0.0.3/go.mod h1:b2MxI6yh02pKrsyNoQUsk4+YNikaGhe4894J+Q5lDvA= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.3/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/libp2p/go-buffer-pool v0.1.0 h1:oK4mSFcQz7cTQIfqbe4MIj9gLW+mnanjyFtc6cdF0Y8= +github.com/libp2p/go-buffer-pool v0.1.0/go.mod h1:N+vh8gMqimBzdKkSMVuydVDq+UV5QTWy5HSiZacSbPg= +github.com/libp2p/go-cidranger v1.1.0 h1:ewPN8EZ0dd1LSnrtuwd4709PXVcITVeuwbag38yPW7c= +github.com/libp2p/go-cidranger v1.1.0/go.mod h1:KWZTfSr+r9qEo9OkI9/SIEeAtw+NNoU0dXIXt15Okic= +github.com/libp2p/go-flow-metrics v0.1.0 h1:0iPhMI8PskQwzh57jB9WxIuIOQ0r+15PChFGkx3Q3WM= +github.com/libp2p/go-flow-metrics v0.1.0/go.mod h1:4Xi8MX8wj5aWNDAZttg6UPmc0ZrnFNsMtpsYUClFtro= +github.com/libp2p/go-libp2p-asn-util v0.2.0 h1:rg3+Os8jbnO5DxkC7K/Utdi+DkY3q/d1/1q+8WeNAsw= +github.com/libp2p/go-libp2p-asn-util v0.2.0/go.mod h1:WoaWxbHKBymSN41hWSq/lGKJEca7TNm58+gGJi2WsLI= +github.com/libp2p/go-libp2p-testing v0.12.0 h1:EPvBb4kKMWO29qP4mZGyhVzUyR25dvfUIK5WDu6iPUA= +github.com/libp2p/go-msgio v0.2.0 h1:W6shmB+FeynDrUVl2dgFQvzfBZcXiyqY4VmpQLu9FqU= +github.com/libp2p/go-msgio v0.2.0/go.mod h1:dBVM1gW3Jk9XqHkU4eKdGvVHdLa51hoGfll6jMJMSlY= +github.com/libp2p/go-nat v0.1.0 h1:MfVsH6DLcpa04Xr+p8hmVRG4juse0s3J8HyNWYHffXg= +github.com/libp2p/go-nat v0.1.0/go.mod h1:X7teVkwRHNInVNWQiO/tAiAVRwSr5zoRz4YSTC3uRBM= +github.com/libp2p/go-netroute v0.1.2/go.mod h1:jZLDV+1PE8y5XxBySEBgbuVAXbhtuHSdmLPL2n9MKbk= +github.com/libp2p/go-netroute v0.2.0 h1:0FpsbsvuSnAhXFnCY0VLFbJOzaK0VnP0r1QT/o4nWRE= +github.com/libp2p/go-netroute v0.2.0/go.mod h1:Vio7LTzZ+6hoT4CMZi5/6CpY3Snzh2vgZhWgxMNwlQI= +github.com/libp2p/go-openssl v0.1.0 h1:LBkKEcUv6vtZIQLVTegAil8jbNpJErQ9AnT+bWV+Ooo= +github.com/libp2p/go-openssl v0.1.0/go.mod h1:OiOxwPpL3n4xlenjx2h7AwSGaFSC/KZvf6gNdOBQMtc= +github.com/libp2p/go-reuseport v0.2.0 h1:18PRvIMlpY6ZK85nIAicSBuXXvrYoSw3dsBAR7zc560= +github.com/libp2p/go-reuseport v0.2.0/go.mod h1:bvVho6eLMm6Bz5hmU0LYN3ixd3nPPvtIlaURZZgOY4k= +github.com/libp2p/go-sockaddr v0.0.2/go.mod h1:syPvOmNs24S3dFVGJA1/mrqdeijPxLV2Le3BRLKd68k= +github.com/libp2p/go-yamux/v4 v4.0.0 h1:+Y80dV2Yx/kv7Y7JKu0LECyVdMXm1VUoko+VQ9rBfZQ= +github.com/libp2p/go-yamux/v4 v4.0.0/go.mod h1:NWjl8ZTLOGlozrXSOZ/HlfG++39iKNnM5wwmtQP1YB4= +github.com/lucas-clemente/quic-go v0.30.0 h1:nwLW0h8ahVQ5EPTIM7uhl/stHqQDea15oRlYKZmw2O0= +github.com/lucas-clemente/quic-go v0.30.0/go.mod h1:ssOrRsOmdxa768Wr78vnh2B8JozgLsMzG/g+0qEC7uk= +github.com/lunixbochs/vtclean v1.0.0/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/marten-seemann/qpack v0.3.0 h1:UiWstOgT8+znlkDPOg2+3rIuYXJ2CnGDkGUXN6ki6hE= +github.com/marten-seemann/qtls-go1-18 v0.1.3 h1:R4H2Ks8P6pAtUagjFty2p7BVHn3XiwDAl7TTQf5h7TI= +github.com/marten-seemann/qtls-go1-18 v0.1.3/go.mod h1:mJttiymBAByA49mhlNZZGrH5u1uXYZJ+RW28Py7f4m4= +github.com/marten-seemann/qtls-go1-19 v0.1.1 h1:mnbxeq3oEyQxQXwI4ReCgW9DPoPR94sNlqWoDZnjRIE= +github.com/marten-seemann/qtls-go1-19 v0.1.1/go.mod h1:5HTDWtVudo/WFsHKRNuOhWlbdjrfs5JHrYb0wIJqGpI= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd h1:br0buuQ854V8u83wA0rVZ8ttrq5CpaPZdvrK0LP2lOk= +github.com/marten-seemann/tcp v0.0.0-20210406111302-dfbc87cc63fd/go.mod h1:QuCEs1Nt24+FYQEqAAncTDPJIuGs+LxK1MCiFL25pMU= +github.com/marten-seemann/webtransport-go v0.2.0 h1:987jPVqcyE3vF+CHNIxDhT0P21O+bI4fVF+0NoRujSo= +github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= +github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-pointer v0.0.1 h1:n+XhsuGeVO6MEAp7xyEukFINEa+Quek5psIR/ylA6o0= +github.com/mattn/go-pointer v0.0.1/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc= +github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/microcosm-cc/bluemonday v1.0.1/go.mod h1:hsXNsILzKxV+sX77C5b8FSuKF00vh2OMYv+xgHpAMF4= +github.com/miekg/dns v1.1.41/go.mod h1:p6aan82bvRIyn+zDIv9xYNUpwa73JcSh9BKwknJysuI= +github.com/miekg/dns v1.1.50 h1:DQUfb9uc6smULcREF09Uc+/Gd46YWqJd5DbpPE9xkcA= +github.com/miekg/dns v1.1.50/go.mod h1:e3IlAVfNqAllflbibAZEWOXOQ+Ynzk/dDozDxY7XnME= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c h1:bzE/A84HN25pxAuk9Eej1Kz9OUelF97nAc82bDquQI8= +github.com/mikioh/tcp v0.0.0-20190314235350-803a9b46060c/go.mod h1:0SQS9kMwD2VsyFEB++InYyBJroV/FRmBgcydeSUcJms= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b h1:z78hV3sbSMAUoyUMM0I83AUIT6Hu17AWfgjzIbtrYFc= +github.com/mikioh/tcpinfo v0.0.0-20190314235526-30a79bb1804b/go.mod h1:lxPUiZwKoFL8DUUmalo2yJJUCxbPKtm8OKfqr2/FTNU= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc h1:PTfri+PuQmWDqERdnNMiD9ZejrlswWrCpBEZgWOiTrc= +github.com/mikioh/tcpopt v0.0.0-20190314235656-172688c1accc/go.mod h1:cGKTAVKx4SxOuR/czcZ/E2RSJ3sfHs8FpHhQ5CWMf9s= +github.com/minio/blake2b-simd v0.0.0-20160723061019-3f5f724cb5b1/go.mod h1:pD8RvIylQ358TN4wwqatJ8rNavkEINozVn9DtGI3dfQ= +github.com/minio/sha256-simd v0.1.1-0.20190913151208-6de447530771/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/mr-tron/base58 v1.1.2/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/multiformats/go-base32 v0.1.0 h1:pVx9xoSPqEIQG8o+UbAe7DNi51oej1NtK+aGkbLYxPE= +github.com/multiformats/go-base32 v0.1.0/go.mod h1:Kj3tFY6zNr+ABYMqeUNeGvkIC/UYgtWibDcT0rExnbI= +github.com/multiformats/go-base36 v0.1.0 h1:JR6TyF7JjGd3m6FbLU2cOxhC0Li8z8dLNGQ89tUg4F4= +github.com/multiformats/go-base36 v0.1.0/go.mod h1:kFGE83c6s80PklsHO9sRn2NCoffoRdUUOENyW/Vv6sM= +github.com/multiformats/go-multiaddr v0.1.1/go.mod h1:aMKBKNEYmzmDmxfX88/vz+J5IU55txyt0p4aiWVohjo= +github.com/multiformats/go-multiaddr v0.2.0/go.mod h1:0nO36NvPpyV4QzvTLi/lafl2y95ncPj0vFwVF6k6wJ4= +github.com/multiformats/go-multiaddr v0.7.0 h1:gskHcdaCyPtp9XskVwtvEeQOG465sCohbQIirSyqxrc= +github.com/multiformats/go-multiaddr v0.7.0/go.mod h1:Fs50eBDWvZu+l3/9S6xAE7ZYj6yhxlvaVZjakWN7xRs= +github.com/multiformats/go-multiaddr-dns v0.3.1 h1:QgQgR+LQVt3NPTjbrLLpsaT2ufAA2y0Mkk+QRVJbW3A= +github.com/multiformats/go-multiaddr-dns v0.3.1/go.mod h1:G/245BRQ6FJGmryJCrOuTdB37AMA5AMOVuO6NY3JwTk= +github.com/multiformats/go-multiaddr-fmt v0.1.0 h1:WLEFClPycPkp4fnIzoFoV9FVd49/eQsuaL3/CWe167E= +github.com/multiformats/go-multiaddr-fmt v0.1.0/go.mod h1:hGtDIW4PU4BqJ50gW2quDuPVjyWNZxToGUh/HwTZYJo= +github.com/multiformats/go-multibase v0.1.1 h1:3ASCDsuLX8+j4kx58qnJ4YFq/JWTJpCyDW27ztsVTOI= +github.com/multiformats/go-multibase v0.1.1/go.mod h1:ZEjHE+IsUrgp5mhlEAYjMtZwK1k4haNkcaPg9aoe1a8= +github.com/multiformats/go-multicodec v0.6.0 h1:KhH2kSuCARyuJraYMFxrNO3DqIaYhOdS039kbhgVwpE= +github.com/multiformats/go-multicodec v0.6.0/go.mod h1:GUC8upxSBE4oG+q3kWZRw/+6yC1BqO550bjhWsJbZlw= +github.com/multiformats/go-multihash v0.0.8/go.mod h1:YSLudS+Pi8NHE7o6tb3D8vrpKa63epEDmG8nTduyAew= +github.com/multiformats/go-multihash v0.2.1 h1:aem8ZT0VA2nCHHk7bPJ1BjUbHNciqZC/d16Vve9l108= +github.com/multiformats/go-multihash v0.2.1/go.mod h1:WxoMcYG85AZVQUyRyo9s4wULvW5qrI9vb2Lt6evduFc= +github.com/multiformats/go-multistream v0.3.3 h1:d5PZpjwRgVlbwfdTDjife7XszfZd8KYWfROYFlGcR8o= +github.com/multiformats/go-multistream v0.3.3/go.mod h1:ODRoqamLUsETKS9BNcII4gcRsJBU5VAwRIv7O39cEXg= +github.com/multiformats/go-varint v0.0.1/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/multiformats/go-varint v0.0.6 h1:gk85QWKxh3TazbLxED/NlDVv8+q+ReFJk7Y2W/KhfNY= +github.com/multiformats/go-varint v0.0.6/go.mod h1:3Ls8CIEsrijN6+B7PbrXRPxHRPuXSrVKRY101jdMZYE= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/neelance/astrewrite v0.0.0-20160511093645-99348263ae86/go.mod h1:kHJEU3ofeGjhHklVoIGuVj85JJwZ6kWPaJwCIxgnFmo= +github.com/neelance/sourcemap v0.0.0-20151028013722-8c68805598ab/go.mod h1:Qr6/a/Q4r9LP1IltGz7tA7iOK1WonHEYhu1HRBA7ZiM= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.2.0 h1:3ZNA3L1c5FYDFTTxbFeVGGD8jYvjYauHD30YgLxVsNI= +github.com/onsi/ginkgo/v2 v2.2.0/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= +github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= +github.com/onsi/gomega v1.20.1 h1:PA/3qinGoukvymdIDV8pii6tiZgC8kbmJO6Z5+b002Q= +github.com/opencontainers/runtime-spec v1.0.2 h1:UfAcuLBJB9Coz72x1hgl8O5RVzTdNiaglX6v2DM6FI0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/openzipkin/zipkin-go v0.1.1/go.mod h1:NtoC/o8u3JlF1lSlyPNswIbeQH9bJTmOf0Erfk+hxe8= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58 h1:onHthvaw9LFnH4t2DcNVpwGmV9E1BkGknEliJkfwQj0= +github.com/pbnjay/memory v0.0.0-20210728143218-7b4eea64cf58/go.mod h1:DXv8WO4yhMYhSNPKjeNKa5WY9YCIEBRbNzFFPJbWO6Y= +github.com/pion/datachannel v1.5.2 h1:piB93s8LGmbECrpO84DnkIVWasRMk3IimbcXkTQLE6E= +github.com/pion/datachannel v1.5.2/go.mod h1:FTGQWaHrdCwIJ1rw6xBIfZVkslikjShim5yr05XFuCQ= +github.com/pion/dtls/v2 v2.1.3/go.mod h1:o6+WvyLDAlXF7YiPB/RlskRoeK+/JtuaZa5emwQcWus= +github.com/pion/dtls/v2 v2.1.5 h1:jlh2vtIyUBShchoTDqpCCqiYCyRFJ/lvf/gQ8TALs+c= +github.com/pion/dtls/v2 v2.1.5/go.mod h1:BqCE7xPZbPSubGasRoDFJeTsyJtdD1FanJYL0JGheqY= +github.com/pion/ice/v2 v2.2.6 h1:R/vaLlI1J2gCx141L5PEwtuGAGcyS6e7E0hDeJFq5Ig= +github.com/pion/ice/v2 v2.2.6/go.mod h1:SWuHiOGP17lGromHTFadUe1EuPgFh/oCU6FCMZHooVE= +github.com/pion/interceptor v0.1.11/go.mod h1:tbtKjZY14awXd7Bq0mmWvgtHB5MDaRN7HV3OZ/uy7s8= +github.com/pion/interceptor v0.1.12 h1:CslaNriCFUItiXS5o+hh5lpL0t0ytQkFnUcbbCs2Zq8= +github.com/pion/interceptor v0.1.12/go.mod h1:bDtgAD9dRkBZpWHGKaoKb42FhDHTG2rX8Ii9LRALLVA= +github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= +github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= +github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= +github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= +github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= +github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= +github.com/pion/rtcp v1.2.9/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= +github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= +github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= +github.com/pion/rtp v1.7.13 h1:qcHwlmtiI50t1XivvoawdCGTP4Uiypzfrsap+bijcoA= +github.com/pion/rtp v1.7.13/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/sctp v1.8.0/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sctp v1.8.2 h1:yBBCIrUMJ4yFICL3RIvR4eh/H2BTTvlligmSTy+3kiA= +github.com/pion/sctp v1.8.2/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= +github.com/pion/sdp/v3 v3.0.5/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= +github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= +github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= +github.com/pion/srtp/v2 v2.0.10 h1:b8ZvEuI+mrL8hbr/f1YiJFB34UMrOac3R3N1yq2UN0w= +github.com/pion/srtp/v2 v2.0.10/go.mod h1:XEeSWaK9PfuMs7zxXyiN252AHPbH12NX5q/CFDWtUuA= +github.com/pion/stun v0.3.5 h1:uLUCBCkQby4S1cf6CGuR9QrVOKcvUwFeemaC865QHDg= +github.com/pion/stun v0.3.5/go.mod h1:gDMim+47EeEtfWogA37n6qXZS88L5V6LqFcf+DZA2UA= +github.com/pion/transport v0.12.2/go.mod h1:N3+vZQD9HlDP5GWkZ85LohxNsDcNgofQmyL6ojX5d8Q= +github.com/pion/transport v0.12.3/go.mod h1:OViWW9SP2peE/HbwBvARicmAVnesphkNkCVZIWJ6q9A= +github.com/pion/transport v0.13.0/go.mod h1:yxm9uXpK9bpBBWkITk13cLo1y5/ur5VQpG22ny6EP7g= +github.com/pion/transport v0.13.1 h1:/UH5yLeQtwm2VZIPjxwnNFxjS4DFhyLfS4GlfuKUzfA= +github.com/pion/transport v0.13.1/go.mod h1:EBxbqzyv+ZrmDb82XswEE0BjfQFtuw1Nu6sjnjWCsGg= +github.com/pion/turn/v2 v2.0.8 h1:KEstL92OUN3k5k8qxsXHpr7WWfrdp7iJZHx99ud8muw= +github.com/pion/turn/v2 v2.0.8/go.mod h1:+y7xl719J8bAEVpSXBXvTxStjJv3hbz9YFflvkpcGPw= +github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= +github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= +github.com/pion/webrtc/v3 v3.1.43 h1:YT3ZTO94UT4kSBvZnRAH82+0jJPUruiKr9CEstdlQzk= +github.com/pion/webrtc/v3 v3.1.43/go.mod h1:G/J8k0+grVsjC/rjCZ24AKoCCxcFFODgh7zThNZGs0M= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= +github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= +github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= +github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180801064454-c7de2306084e/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= +github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= +github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= +github.com/prometheus/procfs v0.0.0-20180725123919-05ee40e3a273/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= +github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/raulk/go-watchdog v1.3.0 h1:oUmdlHxdkXRJlwfG0O9omj8ukerm8MEQavSiDTEtBsk= +github.com/raulk/go-watchdog v1.3.0/go.mod h1:fIvOnLbF0b0ZwkB9YU4mOW9Did//4vPZtDqv66NfsMU= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= +github.com/shurcooL/component v0.0.0-20170202220835-f88ec8f54cc4/go.mod h1:XhFIlyj5a1fBNx5aJTbKoIq0mNaPvOagO+HjB3EtxrY= +github.com/shurcooL/events v0.0.0-20181021180414-410e4ca65f48/go.mod h1:5u70Mqkb5O5cxEA8nxTsgrgLehJeAw6Oc4Ab1c/P1HM= +github.com/shurcooL/github_flavored_markdown v0.0.0-20181002035957-2122de532470/go.mod h1:2dOwnU2uBioM+SGy2aZoq1f/Sd1l9OkAeAUvjSyvgU0= +github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk= +github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ= +github.com/shurcooL/gofontwoff v0.0.0-20180329035133-29b52fc0a18d/go.mod h1:05UtEgK5zq39gLST6uB0cf3NEHjETfB4Fgr3Gx5R9Vw= +github.com/shurcooL/gopherjslib v0.0.0-20160914041154-feb6d3990c2c/go.mod h1:8d3azKNyqcHP1GaQE/c6dDgjkgSx2BZ4IoEi4F1reUI= +github.com/shurcooL/highlight_diff v0.0.0-20170515013008-09bb4053de1b/go.mod h1:ZpfEhSmds4ytuByIcDnOLkTHGUI6KNqRNPDLHDk+mUU= +github.com/shurcooL/highlight_go v0.0.0-20181028180052-98c3abbbae20/go.mod h1:UDKB5a1T23gOMUJrI+uSuH0VRDStOiUVSjBTRDVBVag= +github.com/shurcooL/home v0.0.0-20181020052607-80b7ffcb30f9/go.mod h1:+rgNQw2P9ARFAs37qieuu7ohDNQ3gds9msbT2yn85sg= +github.com/shurcooL/htmlg v0.0.0-20170918183704-d01228ac9e50/go.mod h1:zPn1wHpTIePGnXSHpsVPWEktKXHr6+SS6x/IKRb7cpw= +github.com/shurcooL/httperror v0.0.0-20170206035902-86b7830d14cc/go.mod h1:aYMfkZ6DWSJPJ6c4Wwz3QtW22G7mf/PEgaB9k/ik5+Y= +github.com/shurcooL/httpfs v0.0.0-20171119174359-809beceb2371/go.mod h1:ZY1cvUeJuFPAdZ/B6v7RHavJWZn2YPVFQ1OSXhCGOkg= +github.com/shurcooL/httpgzip v0.0.0-20180522190206-b1c53ac65af9/go.mod h1:919LwcH0M7/W4fcZ0/jy0qGght1GIhqyS/EgWGH2j5Q= +github.com/shurcooL/issues v0.0.0-20181008053335-6292fdc1e191/go.mod h1:e2qWDig5bLteJ4fwvDAc2NHzqFEthkqn7aOZAOpj+PQ= +github.com/shurcooL/issuesapp v0.0.0-20180602232740-048589ce2241/go.mod h1:NPpHK2TI7iSaM0buivtFUc9offApnI0Alt/K8hcHy0I= +github.com/shurcooL/notifications v0.0.0-20181007000457-627ab5aea122/go.mod h1:b5uSkrEVM1jQUspwbixRBhaIjIzL2xazXp6kntxYle0= +github.com/shurcooL/octicon v0.0.0-20181028054416-fa4f57f9efb2/go.mod h1:eWdoE5JD4R5UVWDucdOPg1g2fqQRq78IQa9zlOV1vpQ= +github.com/shurcooL/reactions v0.0.0-20181006231557-f2e0b4ca5b82/go.mod h1:TCR1lToEk4d2s07G3XGfz2QrgHXg4RJBvjrOozvoWfk= +github.com/shurcooL/sanitized_anchor_name v0.0.0-20170918181015-86672fcb3f95/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/shurcooL/users v0.0.0-20180125191416-49c67e49c537/go.mod h1:QJTqeLYEDaXHZDBsXlPCDqdhQuJkuw4NOtaxYe3xii4= +github.com/shurcooL/webdavfs v0.0.0-20170829043945-18c3829fa133/go.mod h1:hKmq5kWdCj2z2KEozexVbfEZIWiTjhE0+UjmZgPqehw= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sourcegraph/annotate v0.0.0-20160123013949-f4cad6c6324d/go.mod h1:UdhH50NIW0fCiwBSr0co2m7BnFLdv4fQTgdqdJTHFeE= +github.com/sourcegraph/syntaxhighlight v0.0.0-20170531221838-bd320f5d308e/go.mod h1:HuIsMU8RRBOtsCgI77wP899iHVBQpCmg4ErYMZB+2IA= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572 h1:RC6RW7j+1+HkWaX/Yh71Ee5ZHaHYt7ZP4sQgUrm6cDU= +github.com/spacemonkeygo/spacelog v0.0.0-20180420211403-2296661a0572/go.mod h1:w0SWMsp6j9O/dk4/ZpIhL+3CkG8ofA2vuv7k+ltqUMc= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= +github.com/tarm/serial v0.0.0-20180830185346-98f6abe2eb07/go.mod h1:kDXzergiv9cbyO7IOYJZWg1U88JhDg3PB6klq9Hg2pA= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/viant/assertly v0.4.8/go.mod h1:aGifi++jvCrUaklKEKT0BU95igDNaqkvz+49uaYMPRU= +github.com/viant/toolbox v0.24.0/go.mod h1:OxMCG57V0PXuIP2HNQrtJf2CjqdmbrOx5EkMILuUhzM= +github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +go.opencensus.io v0.18.0/go.mod h1:vKdFvxhtzZ9onBp9VKHK8z/sRpBMnKAsufL7wlDrCOA= +go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= +go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= +go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= +go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= +go.uber.org/goleak v1.1.11-0.20210813005559-691160354723/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= +go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= +go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +go.uber.org/zap v1.19.1/go.mod h1:j3DNczoxDZroyBnOT1L/Q79cfUMGZxlv/9dzN7SM1rI= +go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= +go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= +go4.org v0.0.0-20180809161055-417644f6feb5/go.mod h1:MkTOUMDaeVYJUOUsaDXIhWPZYa1yOyC1qaOBpL57BhE= +golang.org/x/build v0.0.0-20190111050920-041ab4dc3f9d/go.mod h1:OWs+y06UdEOHN4y+MfF/py+xQ/tYqIWW03b70/CG9Rw= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181030102418-4d3f4d9ffa16/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190313024323-a1f597ede03a/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200602180216-279210d13fed/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220516162934-403b01795ae8/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= +golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= +golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= +golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= +golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= +golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= +golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b h1:SCE/18RnFsLrjydh/R/s5EVvHoZprqEQUuoxK8q2Pc4= +golang.org/x/exp v0.0.0-20220916125017-b168a2c6b86b/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE= +golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= +golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= +golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181106065722-10aee1819953/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= +golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201201195509-5d6afe98e0b7/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211201190559-0a0e4e1bb54c/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220401154927-543a649e0bdd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220531201128-c960675eff93/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220630215102-69896b714898/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5 h1:KafLifaRFIuSJ5C+7CyFJOF9haxKNC1CEIDk8GX6X0k= +golang.org/x/net v0.0.0-20220920183852-bf014ff85ad5/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220907140024-f12130a52804 h1:0SH2R3f1b1VmIMG7BXbEZCBUu2dKmHschSmjqGUrW8A= +golang.org/x/sync v0.0.0-20220907140024-f12130a52804/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180810173357-98c5dad5d1a0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181029174526-d69651ed3497/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190228124157-a34e9553db1e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190316082340-a2f829d7f35f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200602225109-6fdc65e7d980/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426080607-c94f62235c83/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220608164250-635b8c9b7f68/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220913175220-63ea55921009 h1:PuvuRMeLWqsf/ZdT1UUZz0syhioyv1mzuFZsXs4fvhw= +golang.org/x/sys v0.0.0-20220913175220-63ea55921009/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030000716-a0a13e073c7b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= +golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= +golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= +golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20180910000450-7ca32eb868bf/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.0.0-20181030000543-1d582fd0359e/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= +google.golang.org/api v0.1.0/go.mod h1:UGEZY7KEX120AnNLIHFMKIo4obdJhkp2tPbaPlQx13Y= +google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= +google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= +google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= +google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= +google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= +google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= +google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.2.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.3.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= +google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181029155118-b69ba1387ce2/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20181202183823-bd91e49a0898/go.mod h1:7Ep/1NZk928CDR8SjdVbjWNpdIf6nzjE3BTgJDr2Atg= +google.golang.org/genproto v0.0.0-20190306203927-b5d61aea6440/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= +google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= +google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= +google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/grpc v1.14.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= +google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= +google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= +google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= +google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= +google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +grpc.go4.org v0.0.0-20170609214715-11d0a25b4919/go.mod h1:77eQGdRu53HpSqPFJFmuJdjuHRquDANNeA4x7B8WQ9o= +honnef.co/go/tools v0.0.0-20180728063816-88497007e858/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0= +lukechampine.com/blake3 v1.1.7/go.mod h1:tkKEOtDkNtklkXtLNEOGNq5tcV90tJiA1vAA12R78LA= +rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= +rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= +rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sourcegraph.com/sourcegraph/go-diff v0.5.0/go.mod h1:kuch7UrkMzY0X+p9CRK03kfuPQ2zzQcaEFbx8wA8rck= +sourcegraph.com/sqs/pbtypes v0.0.0-20180604144634-d3ebe8f20ae4/go.mod h1:ketZ/q3QxT9HOBeFhu6RdvsftgpsbFHBF5Cas6cDKZ0= diff --git a/examples/go-libp2p-server/main.go b/examples/go-libp2p-server/main.go new file mode 100644 index 0000000..33120f3 --- /dev/null +++ b/examples/go-libp2p-server/main.go @@ -0,0 +1,96 @@ +package main + +import ( + "bufio" + "fmt" + "log" + "net" + "os" + "os/signal" + "syscall" + + "github.com/libp2p/go-libp2p" + "github.com/libp2p/go-libp2p/core/host" + "github.com/libp2p/go-libp2p/core/network" + "github.com/libp2p/go-libp2p/core/peer" + webrtc "github.com/libp2p/go-libp2p/p2p/transport/webrtc" +) + +var listenerIp = net.IPv4(127, 0, 0, 1) + +func init() { + ifaces, err := net.Interfaces() + if err != nil { + return + } + for _, iface := range ifaces { + if iface.Flags&net.FlagUp == 0 { + continue + } + addrs, err := iface.Addrs() + if err != nil { + return + } + for _, addr := range addrs { + // bind to private non-loopback ip + if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && ipnet.IP.IsPrivate() { + if ipnet.IP.To4() != nil { + listenerIp = ipnet.IP.To4() + return + } + } + } + } +} + +func echoHandler(stream network.Stream) { + for { + reader := bufio.NewReader(stream) + str, err := reader.ReadString('\n') + log.Printf("err: %s", err) + if err != nil { + return + } + log.Printf("echo: %s", str) + _, err = stream.Write([]byte(str)) + log.Printf("err: %s", err) + if err != nil { + return + } + + } +} + +func main() { + host := createHost() + host.SetStreamHandler("/echo/1.0.0", echoHandler) + defer host.Close() + remoteInfo := peer.AddrInfo{ + ID: host.ID(), + Addrs: host.Network().ListenAddresses(), + } + + remoteAddrs, _ := peer.AddrInfoToP2pAddrs(&remoteInfo) + fmt.Println("p2p addr: ", remoteAddrs[0]) + + fmt.Println("press Ctrl+C to quit") + ch := make(chan os.Signal, 1) + signal.Notify(ch, syscall.SIGTERM, syscall.SIGINT) + <-ch +} + +func createHost() host.Host { + h, err := libp2p.New( + libp2p.Transport(webrtc.New), + libp2p.ListenAddrStrings( + fmt.Sprintf("/ip4/%s/udp/0/webrtc", listenerIp), + ), + libp2p.DisableRelay(), + libp2p.Ping(true), + ) + if err != nil { + panic(err) + } + + return h +} diff --git a/package.json b/package.json index 18b12a3..95afbd4 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "release": "aegir release" }, "devDependencies": { + "@libp2p/interface-compliance-tests": "^3.0.3", "@libp2p/interface-mocks": "^7.0.2", "@libp2p/peer-id-factory": "^1.0.19", "@protobuf-ts/plugin": "^2.8.0", @@ -80,11 +81,11 @@ "dependencies": { "@chainsafe/libp2p-noise": "^10.0.0", "@libp2p/components": "^3.0.2", - "@libp2p/interfaces": "^3.0.2", "@libp2p/interface-connection": "^3.0.2", "@libp2p/interface-peer-id": "^1.0.5", "@libp2p/interface-stream-muxer": "^3.0.0", "@libp2p/interface-transport": "^2.0.0", + "@libp2p/interfaces": "^3.0.2", "@libp2p/logger": "^2.0.0", "@libp2p/multistream-select": "^3.0.2", "@libp2p/peer-id": "^1.1.15", diff --git a/src/muxer.ts b/src/muxer.ts index 108a5b7..019b628 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -1,4 +1,3 @@ -// import {Components} from "@libp2p/components" import { Stream } from '@libp2p/interface-connection' import { StreamMuxer, StreamMuxerFactory, StreamMuxerInit } from '@libp2p/interface-stream-muxer' import { Source, Sink } from 'it-stream-types' @@ -79,7 +78,7 @@ export class DataChannelMuxer implements StreamMuxer { * Fired when a data channel has been added to the connection has been * added by the remote peer. * - * {@link https://w3c.github.io/webrtc-pc/#dom-rtcpeerconnection-ondatachannel} + * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/datachannel_event} */ this.peerConnection.ondatachannel = ({ channel }) => { const stream = new WebRTCStream({ @@ -92,6 +91,7 @@ export class DataChannelMuxer implements StreamMuxer { }, closeCb: init?.onStreamEnd }) + if ((init?.onIncomingStream) != null) { init.onIncomingStream(stream) } diff --git a/test/maconn.browser.spec.ts b/test/maconn.browser.spec.ts index 88a524b..ff4c2dc 100644 --- a/test/maconn.browser.spec.ts +++ b/test/maconn.browser.spec.ts @@ -1,32 +1,25 @@ /* eslint-disable @typescript-eslint/no-unused-expressions */ -import { WebRTCMultiaddrConnection } from './../src/maconn' -import { createConnectedRTCPeerConnectionPair } from './util' - import { multiaddr } from '@multiformats/multiaddr' import { expect } from 'chai' +import { WebRTCMultiaddrConnection } from './../src/maconn' describe('Multiaddr Connection', () => { it('can open and close', async () => { - const peers = await createConnectedRTCPeerConnectionPair(); - console.log(peers) - // const peerConnection = new RTCPeerConnection() - // peerConnection.createDataChannel('whatever', { negotiated: true, id: 91 }) - + const peerConnection = new RTCPeerConnection() + peerConnection.createDataChannel('whatever', { negotiated: true, id: 91 }) const remoteAddr = multiaddr('/ip4/1.2.3.4/udp/1234/webrtc/certhash/uEiAUqV7kzvM1wI5DYDc1RbcekYVmXli_Qprlw3IkiEg6tQ') const maConn = new WebRTCMultiaddrConnection({ - peerConnection: peers[0], + peerConnection: peerConnection, remoteAddr, timeline: { open: (new Date()).getTime() } }) - console.log(maConn.peerConnection) expect(maConn.timeline.close).to.be.undefined await maConn.close() - console.log(maConn.peerConnection) expect(maConn.timeline.close).to.not.be.undefined }) diff --git a/test/server-multiaddr.ts b/test/server-multiaddr.ts index b963fd9..4071108 100644 --- a/test/server-multiaddr.ts +++ b/test/server-multiaddr.ts @@ -1 +1 @@ -export const SERVER_MULTIADDR = '' +export const SERVER_MULTIADDR = '/ip4/10.0.1.5/udp/51286/webrtc/certhash/uEiABp0C-w4k0yrwqWeLcftPP9bmgu64ssrY0wfH3PPZ3ow/p2p/12D3KooWE9Mg3FMFSQ6jvedGxVFe2UBfvzMCLC4qhaVnjV3xjn5R' diff --git a/test/transport.browser.spec.ts b/test/transport.browser.spec.ts index c0cf311..415c634 100644 --- a/test/transport.browser.spec.ts +++ b/test/transport.browser.spec.ts @@ -90,13 +90,13 @@ describe('WebRTC Transport', () => { }) }) -// @TODO(ddimaria): remove this test and remove related scripts in packageon +// @TODO(ddimaria): remove this test and remove related scripts in package.json describe('WebRTC Transport Interoperability', () => { it('can connect to a server', async () => { // we do not test connecting to an external server, as we do not appear to have one - if (SERVER_MULTIADDR === '') { - return - } + // if (SERVER_MULTIADDR === '') { + // return + // } const node = await createLibp2p({ transports: [webRTC()], diff --git a/test/util.ts b/test/util.ts index d35f886..b91eaec 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,4 +1,15 @@ import { expect } from 'chai' +// import * as ic from '@libp2p/interface-connection' +// import {Components} from '@libp2p/components'; +// import defer, { DeferredPromise } from 'p-defer' +// import {MockConnection} from '../src/connection'; +// import { multiaddr } from '@multiformats/multiaddr' +// import {v4} from 'uuid'; +// import { /* Registrar, */ StreamHandler } from '@libp2p/interface-registrar' +// import { pipe } from 'it-pipe' +// import { logger } from '@libp2p/logger' +// import { createEd25519PeerId } from '@libp2p/peer-id-factory'; +// import { mockRegistrar, mockUpgrader } from '@libp2p/interface-mocks'; export const expectError = (error: unknown, message: string) => { if (error instanceof Error) { @@ -8,73 +19,60 @@ export const expectError = (error: unknown, message: string) => { } } -// import * as ic from '@libp2p/interface-connection' -// import {createEd25519PeerId} from '@libp2p/peer-id-factory'; -// import {mockRegistrar, mockUpgrader} from '@libp2p/interface-mocks'; -// import {Components} from '@libp2p/components'; -import defer, {DeferredPromise} from 'p-defer'; -// import {MockConnection} from '../src/connection'; -// import {Multiaddr} from '@multiformats/multiaddr'; -// import {v4} from 'uuid'; -import {/*Registrar, */StreamHandler} from '@libp2p/interface-registrar'; -import { pipe } from 'it-pipe'; -import { logger } from '@libp2p/logger'; - -const log = logger('libp2p:webrtc:test:util'); +// const log = logger('libp2p:webrtc:test:util') -export const echoHandler: StreamHandler = ({ stream }) => pipe(stream.source, stream.sink); +// export const echoHandler: StreamHandler = async ({ stream }) => await pipe(stream.source, stream.sink) -export async function createConnectedRTCPeerConnectionPair(): Promise { - let [client, server] = [new RTCPeerConnection(), new RTCPeerConnection()]; - // log('created peer connections'); - // we don't need auth for a local test but we need a component for candidate gathering - client.createDataChannel('data'); - client.onicecandidate = ({candidate}) => { - if (candidate !== null) { - server.addIceCandidate(candidate); - } - }; - server.onicecandidate = ({candidate}) => { - if (candidate !== null) { - client.addIceCandidate(candidate); - } - }; - let resolveOnConnect = (pc: RTCPeerConnection): DeferredPromise => { - let promise: DeferredPromise = defer(); - pc.onconnectionstatechange = (_evt) => { - switch (pc.connectionState) { - case 'connected': - log.trace('pc connected'); - promise.resolve(); - return; - case 'failed': - case 'disconnected': - promise.reject(`Peerconnection state: ${pc.connectionState}`); - return; - } - }; - return promise; - } +// export async function createConnectedRTCPeerConnectionPair (): Promise { +// const [client, server] = [new RTCPeerConnection(), new RTCPeerConnection()] +// // log('created peer connections'); +// // we don't need auth for a local test but we need a component for candidate gathering +// client.createDataChannel('data') +// client.onicecandidate = ({ candidate }) => { +// if (candidate !== null) { +// server.addIceCandidate(candidate) +// } +// } +// server.onicecandidate = ({ candidate }) => { +// if (candidate !== null) { +// client.addIceCandidate(candidate) +// } +// } +// const resolveOnConnect = (pc: RTCPeerConnection): DeferredPromise => { +// const promise: DeferredPromise = defer() +// pc.onconnectionstatechange = (_evt) => { +// switch (pc.connectionState) { +// case 'connected': +// log('pc connected') +// promise.resolve() +// return +// case 'failed': +// case 'disconnected': +// promise.reject(`Peerconnection state: ${pc.connectionState}`) +// } +// } +// return promise +// } - let clientConnected = resolveOnConnect(client); - let serverConnected = resolveOnConnect(server); - log('set callbacks on peerconnections'); +// const clientConnected = resolveOnConnect(client) +// const serverConnected = resolveOnConnect(server) +// log('set callbacks on peerconnections') - let clientOffer = await client.createOffer(); - await client.setLocalDescription(clientOffer); - await server.setRemoteDescription(clientOffer); - let serverAnswer = await server.createAnswer(); - await server.setLocalDescription(serverAnswer); - await client.setRemoteDescription(serverAnswer); - log('completed sdp exchange'); +// const clientOffer = await client.createOffer() +// await client.setLocalDescription(clientOffer) +// await server.setRemoteDescription(clientOffer) +// const serverAnswer = await server.createAnswer() +// await server.setLocalDescription(serverAnswer) +// await client.setRemoteDescription(serverAnswer) +// log('completed sdp exchange') - await Promise.all([clientConnected.promise, serverConnected.promise]) +// await Promise.all([clientConnected.promise, serverConnected.promise]) - log.trace(`clientstate: ${client.connectionState}, serverstate: ${server.connectionState}`) +// log(`clientstate: ${client.connectionState}, serverstate: ${server.connectionState}`) - log('created peer connections'); - return [client, server]; -} +// log('created peer connections') +// return [client, server] +// } // export async function createConnectionPair(): Promise<{ connection: ic.Connection, registrar: Registrar }[]> { // let [clientPeerId, serverPeerId] = await Promise.all([createEd25519PeerId(), createEd25519PeerId()]); @@ -86,7 +84,7 @@ export async function createConnectedRTCPeerConnectionPair(): Promise Date: Thu, 1 Dec 2022 07:59:36 -0700 Subject: [PATCH 05/18] Fix copy/paste documentation errors --- src/muxer.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/muxer.ts b/src/muxer.ts index 019b628..6e899ed 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -34,12 +34,12 @@ export class DataChannelMuxer implements StreamMuxer { */ private readonly peerConnection: RTCPeerConnection /** - * WebRTC Peer Connection + * The protocol as represented in the multiaddress */ readonly protocol: string = '/webrtc' /** - * WebRTC Peer Connection + * Array of streams in the data channel */ streams: Stream[] = [] From 4599d334e38b5e9fb79728b053044d78b80be4d0 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 10:30:02 -0700 Subject: [PATCH 06/18] Remove unused --- test/server-multiaddr.ts | 1 - test/transport.browser.spec.ts | 33 ----------- test/util.ts | 103 --------------------------------- 3 files changed, 137 deletions(-) delete mode 100644 test/server-multiaddr.ts diff --git a/test/server-multiaddr.ts b/test/server-multiaddr.ts deleted file mode 100644 index 4071108..0000000 --- a/test/server-multiaddr.ts +++ /dev/null @@ -1 +0,0 @@ -export const SERVER_MULTIADDR = '/ip4/10.0.1.5/udp/51286/webrtc/certhash/uEiABp0C-w4k0yrwqWeLcftPP9bmgu64ssrY0wfH3PPZ3ow/p2p/12D3KooWE9Mg3FMFSQ6jvedGxVFe2UBfvzMCLC4qhaVnjV3xjn5R' diff --git a/test/transport.browser.spec.ts b/test/transport.browser.spec.ts index 415c634..6228679 100644 --- a/test/transport.browser.spec.ts +++ b/test/transport.browser.spec.ts @@ -1,16 +1,9 @@ import * as underTest from './../src/transport' import { expectError } from './util' import { UnimplementedError } from './../src/error' -import { webRTC } from '../src/index' import { mockUpgrader } from '@libp2p/interface-mocks' import { CreateListenerOptions, symbol } from '@libp2p/interface-transport' import { multiaddr, Multiaddr } from '@multiformats/multiaddr' -import { SERVER_MULTIADDR } from './server-multiaddr' -import { noise } from '@chainsafe/libp2p-noise' -import { createLibp2p } from 'libp2p' -import { fromString as uint8arrayFromString } from 'uint8arrays/from-string' -import { pipe } from 'it-pipe' -import first from 'it-first' import { createEd25519PeerId } from '@libp2p/peer-id-factory' import { expect, assert } from 'chai' @@ -89,29 +82,3 @@ describe('WebRTC Transport', () => { } }) }) - -// @TODO(ddimaria): remove this test and remove related scripts in package.json -describe('WebRTC Transport Interoperability', () => { - it('can connect to a server', async () => { - // we do not test connecting to an external server, as we do not appear to have one - // if (SERVER_MULTIADDR === '') { - // return - // } - - const node = await createLibp2p({ - transports: [webRTC()], - connectionEncryption: [noise({})] - }) - - await node.start() - - const ma = multiaddr(SERVER_MULTIADDR) - const stream = await node.dialProtocol(ma, ['/echo/1.0.0']) - const data = 'dataToBeEchoedBackToMe\n' - const response = await pipe([uint8arrayFromString(data)], stream, async (source) => await first(source)) - - expect(response?.subarray()).to.equal(uint8arrayFromString(data)) - - await node.stop() - }) -}) diff --git a/test/util.ts b/test/util.ts index b91eaec..415e643 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,15 +1,4 @@ import { expect } from 'chai' -// import * as ic from '@libp2p/interface-connection' -// import {Components} from '@libp2p/components'; -// import defer, { DeferredPromise } from 'p-defer' -// import {MockConnection} from '../src/connection'; -// import { multiaddr } from '@multiformats/multiaddr' -// import {v4} from 'uuid'; -// import { /* Registrar, */ StreamHandler } from '@libp2p/interface-registrar' -// import { pipe } from 'it-pipe' -// import { logger } from '@libp2p/logger' -// import { createEd25519PeerId } from '@libp2p/peer-id-factory'; -// import { mockRegistrar, mockUpgrader } from '@libp2p/interface-mocks'; export const expectError = (error: unknown, message: string) => { if (error instanceof Error) { @@ -18,95 +7,3 @@ export const expectError = (error: unknown, message: string) => { expect('Did not throw error:').to.equal(message) } } - -// const log = logger('libp2p:webrtc:test:util') - -// export const echoHandler: StreamHandler = async ({ stream }) => await pipe(stream.source, stream.sink) - -// export async function createConnectedRTCPeerConnectionPair (): Promise { -// const [client, server] = [new RTCPeerConnection(), new RTCPeerConnection()] -// // log('created peer connections'); -// // we don't need auth for a local test but we need a component for candidate gathering -// client.createDataChannel('data') -// client.onicecandidate = ({ candidate }) => { -// if (candidate !== null) { -// server.addIceCandidate(candidate) -// } -// } -// server.onicecandidate = ({ candidate }) => { -// if (candidate !== null) { -// client.addIceCandidate(candidate) -// } -// } -// const resolveOnConnect = (pc: RTCPeerConnection): DeferredPromise => { -// const promise: DeferredPromise = defer() -// pc.onconnectionstatechange = (_evt) => { -// switch (pc.connectionState) { -// case 'connected': -// log('pc connected') -// promise.resolve() -// return -// case 'failed': -// case 'disconnected': -// promise.reject(`Peerconnection state: ${pc.connectionState}`) -// } -// } -// return promise -// } - -// const clientConnected = resolveOnConnect(client) -// const serverConnected = resolveOnConnect(server) -// log('set callbacks on peerconnections') - -// const clientOffer = await client.createOffer() -// await client.setLocalDescription(clientOffer) -// await server.setRemoteDescription(clientOffer) -// const serverAnswer = await server.createAnswer() -// await server.setLocalDescription(serverAnswer) -// await client.setRemoteDescription(serverAnswer) -// log('completed sdp exchange') - -// await Promise.all([clientConnected.promise, serverConnected.promise]) - -// log(`clientstate: ${client.connectionState}, serverstate: ${server.connectionState}`) - -// log('created peer connections') -// return [client, server] -// } - -// export async function createConnectionPair(): Promise<{ connection: ic.Connection, registrar: Registrar }[]> { -// let [clientPeerId, serverPeerId] = await Promise.all([createEd25519PeerId(), createEd25519PeerId()]); -// let [clientRegistrar, serverRegistrar] = [mockRegistrar(), mockRegistrar()]; -// let upgrader = mockUpgrader(); -// let [client, server] = await createConnectedRTCPeerConnectionPair(); -// let clientConnection = new MockConnection({ -// id: v4(), -// pc: client, -// localPeer: clientPeerId, -// remotePeer: serverPeerId, -// remoteAddr: multiaddr(), -// components: new Components({ -// peerId: clientPeerId, -// registrar: clientRegistrar, -// upgrader: upgrader, -// }), -// direction: 'outbound', -// }); -// let serverConnection = new MockConnection({ -// id: v4(), -// pc: server, -// localPeer: serverPeerId, -// remotePeer: clientPeerId, -// remoteAddr: multiaddr(), -// components: new Components({ -// peerId: serverPeerId, -// registrar: serverRegistrar, -// upgrader: upgrader, -// }), -// direction: 'inbound', -// }); -// return [ -// { connection: clientConnection, registrar: clientRegistrar }, -// { connection: serverConnection, registrar: serverRegistrar }, -// ]; -// } From 041a057cb9144cdc214e409c2268bfd166f374d0 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 14:01:50 -0700 Subject: [PATCH 07/18] Test chrome and firefox --- package.json | 11 ++++------- test/transport.browser.spec.ts | 2 -- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 95afbd4..046a973 100644 --- a/package.json +++ b/package.json @@ -48,12 +48,10 @@ "autogen": "npx protoc --ts_out proto_ts --proto_path src src/*.proto", "othergen": "./node_modules/.bin/proto-loader-gen-types --longs=String --enums=String --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=proto_ts/ src/*.proto", "build": "aegir build", - "test": "aegir test --target browser", + "test": "aegir test -t browser", "test:interop": "run-p --race start-ext-server wait-then-test", - "test:coverage": "aegir test --target browser --cov -f \"./dist/test/**/*.spec.js\" && npx c8 report", - "start-ext-server": "rm -vf dist/test/server-multiaddr.js ; cd ../go-libp2p/ && go run examples/webrtc/main.go ../js-libp2p-webrtc/dist/test/ ", - "wait-for-server": "wait-on --delay 1000 --timeout 10000 dist/test/server-multiaddr.js", - "wait-then-test": "run-s wait-for-server test", + "test:chrome": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" --cov", + "test:firefox": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" -- --browser firefox", "lint": "aegir lint", "lint:fix": "aegir lint --fix", "clean": "aegir clean", @@ -75,8 +73,7 @@ "libp2p": "^0.40.0", "npm-run-all": "^4.1.5", "prettier": "^2.7.1", - "typescript": "^4.7.4", - "wait-on": "^6.0.1" + "typescript": "^4.7.4" }, "dependencies": { "@chainsafe/libp2p-noise": "^10.0.0", diff --git a/test/transport.browser.spec.ts b/test/transport.browser.spec.ts index 6228679..b251817 100644 --- a/test/transport.browser.spec.ts +++ b/test/transport.browser.spec.ts @@ -37,14 +37,12 @@ describe('WebRTC Transport', () => { } }) - // @TODO(ddimaria): determine if this test has value it('toString property getter', () => { const t = new underTest.WebRTCTransport(components) const s = t[Symbol.toStringTag] expect(s).to.equal('@libp2p/webrtc') }) - // @TODO(ddimaria): determine if this test has value it('symbol property getter', () => { const t = new underTest.WebRTCTransport(components) const s = t[symbol] From 86dd1d25ec6e4b2128d3e2cee5119b2334d8f086 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 14:18:09 -0700 Subject: [PATCH 08/18] Add webworker tests --- package.json | 2 ++ 1 file changed, 2 insertions(+) diff --git a/package.json b/package.json index 4ba1505..3fc7a5b 100644 --- a/package.json +++ b/package.json @@ -54,7 +54,9 @@ "test": "aegir test -t browser", "test:interop": "run-p --race start-ext-server wait-then-test", "test:chrome": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" --cov", + "test:chrome-webworker": "aegir test -t webworker -f \"./dist/test/**/*.spec.js\" --cov", "test:firefox": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" -- --browser firefox", + "test:firefox-webworker": "aegir test -t webworker -f \"./dist/test/**/*.spec.js\" -- --browser firefox", "lint": "aegir lint", "lint:fix": "aegir lint --fix", "clean": "aegir clean", From 3b78322249e64abdf1f6c76d54c4a927fce341d1 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 15:50:50 -0700 Subject: [PATCH 09/18] Removing webworker scripts as webrtc doesn't work in web/service workers --- package.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/package.json b/package.json index 3fc7a5b..4ba1505 100644 --- a/package.json +++ b/package.json @@ -54,9 +54,7 @@ "test": "aegir test -t browser", "test:interop": "run-p --race start-ext-server wait-then-test", "test:chrome": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" --cov", - "test:chrome-webworker": "aegir test -t webworker -f \"./dist/test/**/*.spec.js\" --cov", "test:firefox": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" -- --browser firefox", - "test:firefox-webworker": "aegir test -t webworker -f \"./dist/test/**/*.spec.js\" -- --browser firefox", "lint": "aegir lint", "lint:fix": "aegir lint --fix", "clean": "aegir clean", From 7d83c812288dcae5c8571927cfa6ca25116eca1b Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 17:59:41 -0700 Subject: [PATCH 10/18] Polish all the READMEs --- README.md | 123 +++++++++++++++++++++++--------- examples/README.md | 4 ++ package.json | 4 +- src/transport.ts | 2 +- test/connection.browser.spec.ts | 48 ------------- 5 files changed, 95 insertions(+), 86 deletions(-) create mode 100644 examples/README.md delete mode 100644 test/connection.browser.spec.ts diff --git a/README.md b/README.md index 92a083e..7c09902 100644 --- a/README.md +++ b/README.md @@ -3,8 +3,8 @@ [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/) [![IRC](https://img.shields.io/badge/freenode-%23libp2p-yellow.svg?style=flat-square)](http://webchat.freenode.net/?channels=%23libp2p) [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io) -[![codecov](https://img.shields.io/codecov/c/github/little-bear-labs//js-libp2p-webrtc.svg?style=flat-square)](https://codecov.io/gh/little-bear-labs//js-libp2p-webrtc) -[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/little-bear-labs//js-libp2p-webrtc/actions/workflows/js-test-and-release.yml) +[![codecov](https://img.shields.io/codecov/c/github/little-bear-labs/js-libp2p-webrtc.svg?style=flat-square)](https://codecov.io/gh/little-bear-labs/js-libp2p-webrtc) +[![CI](https://img.shields.io/github/workflow/status/libp2p/js-libp2p-interfaces/test%20&%20maybe%20release/master?style=flat-square)](https://github.com/little-bear-labs/js-libp2p-webrtc/actions/workflows/js-test-and-release.yml) > The browser implementation of the WebRTC module for libp2p. @@ -12,13 +12,14 @@ - [Install](#install) - [Usage](#usage) -- [API](#api) +- [Examples](#examples) +- [Interfaces](#interfaces) - [Transport](#transport) - [Connection](#connection) -- [Hacking](#hacking) - [Contribute](#contribute) -- [Development](#development) - [Build](#build) + - [Protocol Buffers](#protocol-buffers) + - [Test](#test) - [Lint](#lint) - [Clean](#clean) - [Check Dependencies](#check-dependencies) @@ -35,49 +36,81 @@ npm i @libp2p/webrtc ## Usage ```js -import { createLibp2pNode } from 'libp2p' -import { webRTC } from '@libp2p/webrtc' -import { noise } from '@chainsafe/libp2p-noise' +import { createLibp2p } from 'libp2p' +import { Noise } from '@chainsafe/libp2p-noise' import { multiaddr } from '@multiformats/multiaddr' -import { pipe } from 'it-pipe' -import all from 'it-all' - -const node = await createLibp2pNode({ - transports: [ - webRTC() - ], - connectionEncryption: [ - noise() - ] -}) - -const addr = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ') -const { stream } = await node.dialProtocol(addr, '/my-protocol/1.0.0') -const values = await pipe(stream, all) +import first from "it-first"; +import { pipe } from "it-pipe"; +import { fromString, toString } from "uint8arrays"; +import { webRTC } from 'js-libp2p-webrtc' + +const node = await createLibp2p({ + transports: [webRTC()], + connectionEncryption: [() => new Noise()], +}); + +await node.start() + +const ma = multiaddr('/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ') +const stream = await node.dialProtocol(ma, ['/my-protocol/1.0.0']) +const message = `Hello js-libp2p-webrtc\n` +const response = await pipe([fromString(message)], stream, async (source) => await first(source)) +const responseDecoded = toString(response.slice(0, response.length)) ``` -## API + +## Examples +Examples can be found in the [examples folder](examples/README.md). + +## Interfaces ### Transport -[![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/transport/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/transport) +![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-transport](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-transport/img/badge.png) -`libp2p-webrtc` accepts WebRTC encapsulated addresses: `/ip4/0.0.0.0/udp/56093/webrtc/certhash/uEiByaEfNSLBexWBNFZy_QB1vAKEj7JAXDizRs4_SnTflsQ` +Browsers can only `dial`, so `listen` is not supported. -### Connection +```js +interface Transport { + [Symbol.toStringTag]: string + [symbol]: true + dial: (ma: Multiaddr, options: DialOptions) => Promise + createListener: (options: CreateListenerOptions) => Listener + filter: MultiaddrFilter +} + +class WebRTCTransport implements Transport { + + async dial (ma: Multiaddr, options: WebRTCDialOptions): Promise { + const rawConn = await this._connect(ma, options) + log(`dialing address - ${ma.toString()}`) + return rawConn + } + + createListener (options: CreateListenerOptions): Listener { + throw unimplemented('WebRTCTransport.createListener') + } +} +``` -[![](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/libp2p-interfaces/src/connection/img/badge.png)](https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/libp2p-interfaces/src/connection) +### Connection -## Hacking +![https://github.com/libp2p/js-libp2p-interfaces/tree/master/packages/interface-connection](https://raw.githubusercontent.com/libp2p/js-libp2p-interfaces/master/packages/interface-connection/img/badge.png) -Besides the usual `npm install` to get dependencies, `npm run build` to invoke tsc, and `npm run test` to execute unit tests... +```js +interface MultiaddrConnection extends Duplex { + close: (err?: Error) => Promise + remoteAddr: Multiaddr + timeline: MultiaddrConnectionTimeline +} -There is also `npm run autogen` which uses ProtoBuf's protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files. +class WebRTCMultiaddrConnection implements MultiaddrConnection { } +``` ## Contribute Contributions are welcome! The libp2p implementation in JavaScript is a work in progress. As such, there's a few things you can do right now to help out: -- [Check out the existing issues](//github.com/little-bear-labs//js-libp2p-webrtc/issues). +- [Check out the existing issues](//github.com/little-bear-labs/js-libp2p-webrtc/issues). - **Perform code reviews**. - **Add tests**. There can never be enough tests. - Go through the modules and **check out existing issues**. This is especially useful for modules in active development. Some knowledge of IPFS/libp2p may be required, as well as the infrastructure behind it - for instance, you may need to read up on p2p and more complex operations like muxing to be able to help technically. @@ -86,8 +119,6 @@ Please be aware that all interactions related to libp2p are subject to the IPFS Small note: If editing the README, please conform to the [standard-readme](https://github.com/RichardLitt/standard-readme) specification. -## Development - This module leans heavily on (Aegir)[https://github.com/ipfs/aegir] for most of the `package.json` scripts. ### Build @@ -99,6 +130,30 @@ npm run build The build will be located in the `/dist` folder. +### Protocol Buffers + +There is also `npm run generate:proto` script that uses protoc to populate the generated code directory `proto_ts` based on `*.proto` files in src. Don't forget to run this step before `build` any time you make a change to any of the `*.proto` files. + +### Test + +To run all tests: + +```shell +npm test +``` + +To run tests for Chome only: + +```shell +npm run test:chrome +``` + +To run tests for Firefox only: + +```shell +npm run test:firefox +``` + ### Lint Aegir is also used to lint the code, which follows the [Standard](https://github.com/standard/standard) JS linter. The VS Code plugin for this standard is located at https://marketplace.visualstudio.com/items?itemName=standard.vscode-standard. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..8b78f83 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,4 @@ +# Examples + +* [Browser to Server Echo](browser-to-server/README.md): connect to a go-libp2p-webrtc server with a browser + diff --git a/package.json b/package.json index 4ba1505..c497567 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,9 @@ "!**/*.tsbuildinfo" ], "scripts": { - "autogen": "npx protoc --ts_out proto_ts --proto_path src src/*.proto", - "othergen": "./node_modules/.bin/proto-loader-gen-types --longs=String --enums=String --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=proto_ts/ src/*.proto", + "generate:proto": "npx protoc --ts_out proto_ts --proto_path src src/*.proto", "build": "aegir build", "test": "aegir test -t browser", - "test:interop": "run-p --race start-ext-server wait-then-test", "test:chrome": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" --cov", "test:firefox": "aegir test -t browser -f \"./dist/test/**/*.spec.js\" -- --browser firefox", "lint": "aegir lint", diff --git a/src/transport.ts b/src/transport.ts index 24d07db..44bfd0d 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -67,7 +67,7 @@ export class WebRTCTransport implements Transport { } /** - * Create transport listeners + * Create transport listeners no supported by browsers */ createListener (options: CreateListenerOptions): Listener { throw unimplemented('WebRTCTransport.createListener') diff --git a/test/connection.browser.spec.ts b/test/connection.browser.spec.ts deleted file mode 100644 index fa594b1..0000000 --- a/test/connection.browser.spec.ts +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-env mocha */ - -// import {createConnectionPair, echoHandler} from "../test/util.js"; -// import { expect } from 'aegir/chai'; -// import { pipe } from 'it-pipe'; -// import first from 'it-first'; -// import {fromString} from 'uint8arrays/from-string'; -// import {v4} from 'uuid'; - -// const echoProtocol = '/echo/1.0.0'; - -// describe('connection browser tests', () => { -// it('can run the echo protocol (first)', async () => { -// let [{ connection: client }, server] = await createConnectionPair(); -// let serverRegistrar = server.registrar; -// await serverRegistrar.handle(echoProtocol, echoHandler, { maxInboundStreams: 10, maxOutboundStreams: 10 }); -// let clientStream = await client.newStream([echoProtocol]); -// let data = fromString(v4()); -// let response = await pipe([data], clientStream, async (source) => await first(source)); - -// expect(response).to.not.be.undefined; -// expect(response!.subarray()).to.equalBytes(data); -// }); - -// it('can run the echo protocol (all)', async () => { -// //enableLogger('libp2p:webrtc:connection'); -// //enableLogger('libp2p:webrtc:stream'); -// let [{ connection: client }, server] = await createConnectionPair(); -// let serverRegistrar = server.registrar; -// await serverRegistrar.handle(echoProtocol, echoHandler, { maxInboundStreams: 10, maxOutboundStreams: 10 }); -// let clientStream = await client.newStream([echoProtocol]); -// // close stream after 2 seconds -// setTimeout(() => clientStream.close(), 2000); -// let data = fromString(v4()); -// clientStream.sink([data]); -// let responsed = false; -// for await (const response of clientStream.source) { -// expect(response).to.not.be.undefined; -// expect(response.subarray()).to.equalBytes(data); -// responsed = true; -// break; -// } -// expect(responsed).to.be.true(); -// }); - -// }); - -export {} From 57a06351f848404f48a0fb15242fb21451419fb0 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Thu, 1 Dec 2022 18:10:27 -0700 Subject: [PATCH 11/18] Prefer template literals --- src/error.ts | 16 ++++++++-------- test/transport.browser.spec.ts | 1 - 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/error.ts b/src/error.ts index c360895..f22136a 100644 --- a/src/error.ts +++ b/src/error.ts @@ -16,14 +16,14 @@ export enum codes { export class WebRTCTransportError extends Error { constructor (msg: string) { - super('WebRTC transport error: ' + msg) + super(`WebRTC transport error: ${msg}`) this.name = 'WebRTCTransportError' } } export class ConnectionClosedError extends WebRTCTransportError { constructor (state: RTCPeerConnectionState, msg: string) { - super(`peerconnection moved to state: ${state}:` + msg) + super(`peerconnection moved to state: ${state}: ${msg}`) this.name = 'WebRTC/ConnectionClosed' } } @@ -33,8 +33,8 @@ export function connectionClosedError (state: RTCPeerConnectionState, msg: strin } export class DataChannelError extends WebRTCTransportError { - constructor (streamLabel: string, errorMessage: string) { - super(`[stream: ${streamLabel}] data channel error: ${errorMessage}`) + constructor (streamLabel: string, msg: string) { + super(`[stream: ${streamLabel}] data channel error: ${msg}`) this.name = 'WebRTC/DataChannelError' } } @@ -45,7 +45,7 @@ export function dataChannelError (streamLabel: string, msg: string) { export class InappropriateMultiaddrError extends WebRTCTransportError { constructor (msg: string) { - super('There was a problem with the Multiaddr which was passed in: ' + msg) + super(`There was a problem with the Multiaddr which was passed in: ${msg}`) this.name = 'WebRTC/InappropriateMultiaddrError' } } @@ -56,7 +56,7 @@ export function inappropriateMultiaddr (msg: string) { export class InvalidArgumentError extends WebRTCTransportError { constructor (msg: string) { - super('There was a problem with a provided argument: ' + msg) + super(`There was a problem with a provided argument: ${msg}`) this.name = 'WebRTC/InvalidArgumentError' } } @@ -78,7 +78,7 @@ export function invalidFingerprint (fingerprint: string, source: string) { export class OperationAbortedError extends WebRTCTransportError { constructor (context: string, abortReason: string) { - super(`Signalled to abort because (${abortReason}})${context}`) + super(`Signalled to abort because (${abortReason}}) ${context}`) this.name = 'WebRTC/OperationAbortedError' } } @@ -101,7 +101,7 @@ export function overStreamLimit (dir: Direction, proto: string) { export class UnimplementedError extends WebRTCTransportError { constructor (methodName: string) { - super('A method (' + methodName + ') was called though it has been intentionally left unimplemented.') + super(`A method (${methodName}) was called though it has been intentionally left unimplemented.`) this.name = 'WebRTC/UnimplementedError' } } diff --git a/test/transport.browser.spec.ts b/test/transport.browser.spec.ts index b251817..ac190ed 100644 --- a/test/transport.browser.spec.ts +++ b/test/transport.browser.spec.ts @@ -26,7 +26,6 @@ describe('WebRTC Transport', () => { expect(t.constructor.name).to.equal('WebRTCTransport') }) - // @TODO(ddimaria): determine if this test has value it('createListner does throw', () => { const t = new underTest.WebRTCTransport(components) try { From 581ca72c37d75d917d75458d6ca739141a02f3cd Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Fri, 2 Dec 2022 07:36:00 -0700 Subject: [PATCH 12/18] Remove webworker and electron jobs in CI --- .github/workflows/js-test-and-release.yml | 44 +---------------------- 1 file changed, 1 insertion(+), 43 deletions(-) diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml index 97902b1..f28cfb5 100644 --- a/.github/workflows/js-test-and-release.yml +++ b/.github/workflows/js-test-and-release.yml @@ -35,20 +35,6 @@ jobs: with: flags: chrome - test-chrome-webworker: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v2 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:chrome-webworker - - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 - with: - flags: chrome-webworker - test-firefox: needs: check runs-on: ubuntu-latest @@ -63,36 +49,8 @@ jobs: with: flags: firefox - test-firefox-webworker: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v2 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npm run --if-present test:firefox-webworker - - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 - with: - flags: firefox-webworker - - test-electron-main: - needs: check - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v2 - with: - node-version: lts/* - - uses: ipfs/aegir/actions/cache-node-modules@master - - run: npx xvfb-maybe npm run --if-present test:electron-main - - uses: codecov/codecov-action@81cd2dc8148241f03f5839d295e000b8f761e378 # v3.1.0 - with: - flags: electron-main - release: - needs: [test-chrome, test-chrome-webworker, test-firefox, test-firefox-webworker, test-electron-main] + needs: [test-chrome, test-firefox] runs-on: ubuntu-latest if: github.event_name == 'push' && github.ref == 'refs/heads/master' # with #262 - 'refs/heads/${{{ github.default_branch }}}' steps: From 9d3afb096e7497cf32b771aad5a12ad638e9af9b Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Fri, 2 Dec 2022 07:50:07 -0700 Subject: [PATCH 13/18] Cosmetic refactors --- src/stream.ts | 37 ++++++++++++++++++++++++------------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/src/stream.ts b/src/stream.ts index 9c42f83..54b2870 100644 --- a/src/stream.ts +++ b/src/stream.ts @@ -27,7 +27,8 @@ export function defaultStat (dir: Direction): StreamStat { interface StreamInitOpts { /** - * The network channel used for bidirectional peer-to-peer transfers of arbitrary data + * The network channel used for bidirectional peer-to-peer transfers of + * arbitrary data * * {@link https://developer.mozilla.org/en-US/docs/Web/API/RTCDataChannel} */ @@ -54,7 +55,8 @@ interface StreamInitOpts { */ interface StreamStateInput { /** - * Outbound conections are opened by the local node, inbound streams are opened by the remote + * Outbound conections are opened by the local node, inbound streams are + * opened by the remote */ direction: 'inbound' | 'outbound' @@ -251,10 +253,8 @@ export class WebRTCStream implements Stream { this._innersrc.push(new Uint8Array(data as ArrayBufferLike)) } - // pipe framed protobuf messages through - // a length prefixed decoder, and surface - // data from the `Message.message` field - // through a source. + // pipe framed protobuf messages through a length prefixed decoder, and + // surface data from the `Message.message` field through a source. this._src = pipe( this._innersrc, lengthPrefixed.decode(), @@ -269,18 +269,22 @@ export class WebRTCStream implements Stream { ) } - // If user attempts to set a new source - // this should be a nop - set source (_src: Source) { - } + // If user attempts to set a new source this should be a noop + set source (_src: Source) { } get source (): Source { return this._src } + /** + * Closable sink + */ private async _sinkFn (src: Source): Promise { await this.opened.promise - if (this.streamState.state === StreamStates.CLOSED || this.streamState.state === StreamStates.WRITE_CLOSED) { + + const isClosed = (state: StreamStates) => state === StreamStates.CLOSED || state === StreamStates.WRITE_CLOSED + + if (isClosed(this.streamState.state)) { return } @@ -293,21 +297,26 @@ export class WebRTCStream implements Stream { } for await (const buf of merge(closeWriteIterable, src)) { - const state = self.streamState.state - if (state === StreamStates.CLOSED || state === StreamStates.WRITE_CLOSED) { + if (isClosed(self.streamState.state)) { return } + const msgbuf = pb.Message.toBinary({ message: buf.subarray() }) const sendbuf = lengthPrefixed.encode.single(msgbuf) + this.channel.send(sendbuf.subarray()) } } + /** + * Handle incoming + */ processIncomingProtobuf (buffer: Uint8Array): Uint8Array | undefined { const message = pb.Message.fromBinary(buffer) if (message.flag !== undefined) { const [currentState, nextState] = this.streamState.transition({ direction: 'inbound', flag: message.flag }) + if (currentState !== nextState) { // @TODO(ddimaria): determine if we need to check for StreamStates.OPEN switch (nextState) { @@ -325,6 +334,7 @@ export class WebRTCStream implements Stream { } } } + return message.message } @@ -391,6 +401,7 @@ export class WebRTCStream implements Stream { reset (): void { this.stat = defaultStat(this.stat.direction) const [currentState, nextState] = this.streamState.transition({ direction: 'outbound', flag: pb.Message_Flag.RESET }) + if (currentState !== nextState) { this._sendFlag(pb.Message_Flag.RESET) this.close() From 0a6ce42e7afc2bffc0b5e7486b3fbdeb02de802a Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Fri, 2 Dec 2022 08:02:59 -0700 Subject: [PATCH 14/18] Update master reference to main in CI --- .github/workflows/js-test-and-release.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/js-test-and-release.yml b/.github/workflows/js-test-and-release.yml index f28cfb5..2426eb1 100644 --- a/.github/workflows/js-test-and-release.yml +++ b/.github/workflows/js-test-and-release.yml @@ -2,10 +2,10 @@ name: test & maybe release on: push: branches: - - master # with #262 - ${{{ github.default_branch }}} + - main # with #262 - ${{{ github.default_branch }}} pull_request: branches: - - master # with #262 - ${{{ github.default_branch }}} + - main # with #262 - ${{{ github.default_branch }}} - develop jobs: From 584c6fadf9d7a0805d33edc86a8a2aa5a87901a3 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Fri, 2 Dec 2022 08:16:02 -0700 Subject: [PATCH 15/18] Refactor stream tests --- test/stream.browser.spec.ts | 90 +++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/test/stream.browser.spec.ts b/test/stream.browser.spec.ts index 2a095c1..0d962c8 100644 --- a/test/stream.browser.spec.ts +++ b/test/stream.browser.spec.ts @@ -1,74 +1,68 @@ import * as underTest from '../src/stream' import { expect, assert } from 'chai' +function setup (): { peerConnection: RTCPeerConnection, datachannel: RTCDataChannel, webrtcStream: underTest.WebRTCStream } { + const peerConnection = new RTCPeerConnection() + const datachannel = peerConnection.createDataChannel('whatever', { negotiated: true, id: 91 }) + const webrtcStream = new underTest.WebRTCStream({ channel: datachannel, stat: underTest.defaultStat('outbound') }) + + return { peerConnection, datachannel, webrtcStream } +} + describe('Stream Stats', () => { it('can construct', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - // expect(s.stat.timeline.close).to.not.exist(); - assert.notExists(s.stat.timeline.close) + const { webrtcStream } = setup() + assert.notExists(webrtcStream.stat.timeline.close) }) it('close marks it closed', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) + const { webrtcStream } = setup() - expect(s.streamState.state).to.equal(underTest.StreamStates.OPEN) - s.close() - expect(s.streamState.state).to.equal(underTest.StreamStates.CLOSED) + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.OPEN) + webrtcStream.close() + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.CLOSED) }) it('closeRead marks it read-closed only', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - expect(s.streamState.state).to.equal(underTest.StreamStates.OPEN) - s.closeRead() - expect(s.streamState.state).to.equal(underTest.StreamStates.READ_CLOSED) + const { webrtcStream } = setup() + + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.OPEN) + webrtcStream.closeRead() + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.READ_CLOSED) }) it('closeWrite marks it write-closed only', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - expect(s.streamState.state).to.equal(underTest.StreamStates.OPEN) - s.closeWrite() - expect(s.streamState.state).to.equal(underTest.StreamStates.WRITE_CLOSED) + const { webrtcStream } = setup() + + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.OPEN) + webrtcStream.closeWrite() + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.WRITE_CLOSED) }) it('closeWrite AND closeRead = close', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - s.closeWrite() - expect(s.streamState.state).to.equal(underTest.StreamStates.WRITE_CLOSED) - s.closeRead() - expect(s.streamState.state).to.equal(underTest.StreamStates.CLOSED) + const { webrtcStream } = setup() + + webrtcStream.closeWrite() + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.WRITE_CLOSED) + webrtcStream.closeRead() + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.CLOSED) }) it('abort = close', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - // expect(s.stat.timeline.close).to.not.exist(); - expect(s.streamState.state).to.equal(underTest.StreamStates.OPEN) - s.abort({ name: 'irrelevant', message: 'this parameter is actually ignored' }) - expect(s.streamState.state).to.equal(underTest.StreamStates.CLOSED) - // expect(s.stat.timeline.close).to.exist(); - expect(s.stat.timeline.close).to.be.greaterThan(s.stat.timeline.open) + const { webrtcStream } = setup() + + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.OPEN) + webrtcStream.abort({ name: 'irrelevant', message: 'this parameter is actually ignored' }) + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.CLOSED) + expect(webrtcStream.stat.timeline.close).to.be.greaterThan(webrtcStream.stat.timeline.open) }) it('reset = close', () => { - const pc = new RTCPeerConnection() - const dc = pc.createDataChannel('whatever', { negotiated: true, id: 91 }) - const s = new underTest.WebRTCStream({ channel: dc, stat: underTest.defaultStat('outbound') }) - // expect(s.stat.timeline.close).to.not.exist(); - expect(s.streamState.state).to.equal(underTest.StreamStates.OPEN) - s.reset() // only resets the write side - expect(s.streamState.state).to.equal(underTest.StreamStates.CLOSED) - // expect(s.stat.timeline.close).to.not.exist(); - expect(dc.readyState).to.be.oneOf(['closing', 'closed']) + const { datachannel, webrtcStream } = setup() + + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.OPEN) + webrtcStream.reset() // only resets the write side + expect(webrtcStream.streamState.state).to.equal(underTest.StreamStates.CLOSED) + expect(datachannel.readyState).to.be.oneOf(['closing', 'closed']) }) }) From a1df8c3c903c2ddf59f0db5805e9dac584ab2199 Mon Sep 17 00:00:00 2001 From: David DiMaria Date: Fri, 2 Dec 2022 08:30:52 -0700 Subject: [PATCH 16/18] Complete transport tests --- test/transport.browser.spec.ts | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/test/transport.browser.spec.ts b/test/transport.browser.spec.ts index ac190ed..8cc58cc 100644 --- a/test/transport.browser.spec.ts +++ b/test/transport.browser.spec.ts @@ -1,3 +1,5 @@ +/* eslint-disable @typescript-eslint/no-floating-promises */ + import * as underTest from './../src/transport' import { expectError } from './util' import { UnimplementedError } from './../src/error' @@ -26,7 +28,16 @@ describe('WebRTC Transport', () => { expect(t.constructor.name).to.equal('WebRTCTransport') }) - it('createListner does throw', () => { + it('can dial', async () => { + const ma = multiaddr('/ip4/1.2.3.4/udp/1234/webrtc/certhash/uEiAUqV7kzvM1wI5DYDc1RbcekYVmXli_Qprlw3IkiEg6tQ/p2p/12D3KooWGDMwwqrpcYKpKCgxuKT2NfqPqa94QnkoBBpqvCaiCzWd') + const transport = new underTest.WebRTCTransport(components) + const options = ignoredDialOption() + + // don't await as this isn't an e2e test + transport.dial(ma, options) + }) + + it('createListner throws', () => { const t = new underTest.WebRTCTransport(components) try { t.createListener(ignoredDialOption()) From dbf9bd44858165018fc25a030ab0e1576c163d5a Mon Sep 17 00:00:00 2001 From: David D Date: Mon, 5 Dec 2022 08:22:15 -0700 Subject: [PATCH 17/18] Update src/muxer.ts Co-authored-by: Glen De Cauwsemaecker --- src/muxer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/muxer.ts b/src/muxer.ts index 6e899ed..b4c9f9d 100644 --- a/src/muxer.ts +++ b/src/muxer.ts @@ -12,7 +12,7 @@ export class DataChannelMuxerFactory implements StreamMuxerFactory { private readonly peerConnection: RTCPeerConnection /** - * The string representation of the protocol, requried by StreamMuxerFactory + * The string representation of the protocol, required by `StreamMuxerFactory` */ protocol: string = '/webrtc' From f2548221bc33fad1ff7d8a6c0cdb1b16173f293b Mon Sep 17 00:00:00 2001 From: David D Date: Mon, 5 Dec 2022 08:23:04 -0700 Subject: [PATCH 18/18] Update examples/browser-to-server/README.md Co-authored-by: Glen De Cauwsemaecker --- examples/browser-to-server/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/browser-to-server/README.md b/examples/browser-to-server/README.md index 19d79ce..fb3d997 100644 --- a/examples/browser-to-server/README.md +++ b/examples/browser-to-server/README.md @@ -21,7 +21,7 @@ npm i && npm run start ``` The browser window will automatically open. -Using the copied multiaddress from the Go server, pasted that value into the `Server MultiAddress` input and click the `Connect` button. +Using the copied multiaddress from the Go server, paste it into the `Server MultiAddress` input and click the `Connect` button. Once the peer is connected, click the message section will appear. Enter a message and click the `Send` button. The output should look like: