@libp2p/perf 0.0.0 → 1.0.0-18567b7c

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,4 @@
1
+ This project is dual licensed under MIT and Apache-2.0.
2
+
3
+ MIT: https://www.opensource.org/licenses/mit
4
+ Apache-2.0: https://www.apache.org/licenses/license-2.0
package/README.md ADDED
@@ -0,0 +1,45 @@
1
+ # @libp2p/perf <!-- omit in toc -->
2
+
3
+ [![libp2p.io](https://img.shields.io/badge/project-libp2p-yellow.svg?style=flat-square)](http://libp2p.io/)
4
+ [![Discuss](https://img.shields.io/discourse/https/discuss.libp2p.io/posts.svg?style=flat-square)](https://discuss.libp2p.io)
5
+ [![codecov](https://img.shields.io/codecov/c/github/libp2p/js-libp2p.svg?style=flat-square)](https://codecov.io/gh/libp2p/js-libp2p)
6
+ [![CI](https://img.shields.io/github/actions/workflow/status/libp2p/js-libp2p/main.yml?branch=master\&style=flat-square)](https://github.com/libp2p/js-libp2p/actions/workflows/main.yml?query=branch%3Amaster)
7
+
8
+ > Implementation of Perf Protocol
9
+
10
+ ## Table of contents <!-- omit in toc -->
11
+
12
+ - [Install](#install)
13
+ - [Browser `<script>` tag](#browser-script-tag)
14
+ - [API Docs](#api-docs)
15
+ - [License](#license)
16
+ - [Contribution](#contribution)
17
+
18
+ ## Install
19
+
20
+ ```console
21
+ $ npm i @libp2p/perf
22
+ ```
23
+
24
+ ### Browser `<script>` tag
25
+
26
+ Loading this module through a script tag will make it's exports available as `Libp2pPerf` in the global namespace.
27
+
28
+ ```html
29
+ <script src="https://unpkg.com/@libp2p/perf/dist/index.min.js"></script>
30
+ ```
31
+
32
+ ## API Docs
33
+
34
+ - <https://libp2p.github.io/js-libp2p/modules/_libp2p_perf.html>
35
+
36
+ ## License
37
+
38
+ Licensed under either of
39
+
40
+ - Apache 2.0, ([LICENSE-APACHE](LICENSE-APACHE) / <http://www.apache.org/licenses/LICENSE-2.0>)
41
+ - MIT ([LICENSE-MIT](LICENSE-MIT) / <http://opensource.org/licenses/MIT>)
42
+
43
+ ## Contribution
44
+
45
+ Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
@@ -0,0 +1,3 @@
1
+ (function (root, factory) {(typeof module === 'object' && module.exports) ? module.exports = factory() : root.Libp2PPerf = factory()}(typeof self !== 'undefined' ? self : this, function () {
2
+ "use strict";var Libp2PPerf=(()=>{var ae=Object.create;var N=Object.defineProperty;var ie=Object.getOwnPropertyDescriptor;var ce=Object.getOwnPropertyNames;var fe=Object.getPrototypeOf,le=Object.prototype.hasOwnProperty;var L=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports),ue=(e,t)=>{for(var r in t)N(e,r,{get:t[r],enumerable:!0})},V=(e,t,r,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of ce(t))!le.call(e,s)&&s!==r&&N(e,s,{get:()=>t[s],enumerable:!(o=ie(t,s))||o.enumerable});return e};var de=(e,t,r)=>(r=e!=null?ae(fe(e)):{},V(t||!e||!e.__esModule?N(r,"default",{value:e,enumerable:!0}):r,e)),pe=e=>V(N({},"__esModule",{value:!0}),e);var Z=L((Re,q)=>{var B=1e3,O=B*60,I=O*60,S=I*24,he=S*7,be=S*365.25;q.exports=function(e,t){t=t||{};var r=typeof e;if(r==="string"&&e.length>0)return Ce(e);if(r==="number"&&isFinite(e))return t.long?ge(e):me(e);throw new Error("val is not a non-empty string or a valid number. val="+JSON.stringify(e))};function Ce(e){if(e=String(e),!(e.length>100)){var t=/^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec(e);if(t){var r=parseFloat(t[1]),o=(t[2]||"ms").toLowerCase();switch(o){case"years":case"year":case"yrs":case"yr":case"y":return r*be;case"weeks":case"week":case"w":return r*he;case"days":case"day":case"d":return r*S;case"hours":case"hour":case"hrs":case"hr":case"h":return r*I;case"minutes":case"minute":case"mins":case"min":case"m":return r*O;case"seconds":case"second":case"secs":case"sec":case"s":return r*B;case"milliseconds":case"millisecond":case"msecs":case"msec":case"ms":return r;default:return}}}}function me(e){var t=Math.abs(e);return t>=S?Math.round(e/S)+"d":t>=I?Math.round(e/I)+"h":t>=O?Math.round(e/O)+"m":t>=B?Math.round(e/B)+"s":e+"ms"}function ge(e){var t=Math.abs(e);return t>=S?P(e,t,S,"day"):t>=I?P(e,t,I,"hour"):t>=O?P(e,t,O,"minute"):t>=B?P(e,t,B,"second"):e+" ms"}function P(e,t,r,o){var s=t>=r*1.5;return Math.round(e/r)+" "+o+(s?"s":"")}});var G=L((Le,K)=>{function we(e){r.debug=r,r.default=r,r.coerce=b,r.disable=d,r.enable=s,r.enabled=f,r.humanize=Z(),r.destroy=k,Object.keys(e).forEach(n=>{r[n]=e[n]}),r.names=[],r.skips=[],r.formatters={};function t(n){let a=0;for(let u=0;u<n.length;u++)a=(a<<5)-a+n.charCodeAt(u),a|=0;return r.colors[Math.abs(a)%r.colors.length]}r.selectColor=t;function r(n){let a,u=null,U,i;function c(...p){if(!c.enabled)return;let h=c,m=Number(new Date),F=m-(a||m);h.diff=F,h.prev=a,h.curr=m,a=m,p[0]=r.coerce(p[0]),typeof p[0]!="string"&&p.unshift("%O");let C=0;p[0]=p[0].replace(/%([a-zA-Z%])/g,(v,w)=>{if(v==="%%")return"%";C++;let A=r.formatters[w];if(typeof A=="function"){let z=p[C];v=A.call(h,z),p.splice(C,1),C--}return v}),r.formatArgs.call(h,p),(h.log||r.log).apply(h,p)}return c.namespace=n,c.useColors=r.useColors(),c.color=r.selectColor(n),c.extend=o,c.destroy=r.destroy,Object.defineProperty(c,"enabled",{enumerable:!0,configurable:!1,get:()=>u!==null?u:(U!==r.namespaces&&(U=r.namespaces,i=r.enabled(n)),i),set:p=>{u=p}}),typeof r.init=="function"&&r.init(c),c}function o(n,a){let u=r(this.namespace+(typeof a>"u"?":":a)+n);return u.log=this.log,u}function s(n){r.save(n),r.namespaces=n,r.names=[],r.skips=[];let a,u=(typeof n=="string"?n:"").split(/[\s,]+/),U=u.length;for(a=0;a<U;a++)u[a]&&(n=u[a].replace(/\*/g,".*?"),n[0]==="-"?r.skips.push(new RegExp("^"+n.slice(1)+"$")):r.names.push(new RegExp("^"+n+"$")))}function d(){let n=[...r.names.map(l),...r.skips.map(l).map(a=>"-"+a)].join(",");return r.enable(""),n}function f(n){if(n[n.length-1]==="*")return!0;let a,u;for(a=0,u=r.skips.length;a<u;a++)if(r.skips[a].test(n))return!1;for(a=0,u=r.names.length;a<u;a++)if(r.names[a].test(n))return!0;return!1}function l(n){return n.toString().substring(2,n.toString().length-2).replace(/\.\*\?$/,"*")}function b(n){return n instanceof Error?n.stack||n.message:n}function k(){console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.")}return r.enable(r.load()),r}K.exports=we});var W=L((y,R)=>{y.formatArgs=xe;y.save=Fe;y.load=ve;y.useColors=ye;y.storage=Ae();y.destroy=(()=>{let e=!1;return()=>{e||(e=!0,console.warn("Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`."))}})();y.colors=["#0000CC","#0000FF","#0033CC","#0033FF","#0066CC","#0066FF","#0099CC","#0099FF","#00CC00","#00CC33","#00CC66","#00CC99","#00CCCC","#00CCFF","#3300CC","#3300FF","#3333CC","#3333FF","#3366CC","#3366FF","#3399CC","#3399FF","#33CC00","#33CC33","#33CC66","#33CC99","#33CCCC","#33CCFF","#6600CC","#6600FF","#6633CC","#6633FF","#66CC00","#66CC33","#9900CC","#9900FF","#9933CC","#9933FF","#99CC00","#99CC33","#CC0000","#CC0033","#CC0066","#CC0099","#CC00CC","#CC00FF","#CC3300","#CC3333","#CC3366","#CC3399","#CC33CC","#CC33FF","#CC6600","#CC6633","#CC9900","#CC9933","#CCCC00","#CCCC33","#FF0000","#FF0033","#FF0066","#FF0099","#FF00CC","#FF00FF","#FF3300","#FF3333","#FF3366","#FF3399","#FF33CC","#FF33FF","#FF6600","#FF6633","#FF9900","#FF9933","#FFCC00","#FFCC33"];function ye(){return typeof window<"u"&&window.process&&(window.process.type==="renderer"||window.process.__nwjs)?!0:typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/(edge|trident)\/(\d+)/)?!1:typeof document<"u"&&document.documentElement&&document.documentElement.style&&document.documentElement.style.WebkitAppearance||typeof window<"u"&&window.console&&(window.console.firebug||window.console.exception&&window.console.table)||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/firefox\/(\d+)/)&&parseInt(RegExp.$1,10)>=31||typeof navigator<"u"&&navigator.userAgent&&navigator.userAgent.toLowerCase().match(/applewebkit\/(\d+)/)}function xe(e){if(e[0]=(this.useColors?"%c":"")+this.namespace+(this.useColors?" %c":" ")+e[0]+(this.useColors?"%c ":" ")+"+"+R.exports.humanize(this.diff),!this.useColors)return;let t="color: "+this.color;e.splice(1,0,t,"color: inherit");let r=0,o=0;e[0].replace(/%[a-zA-Z%]/g,s=>{s!=="%%"&&(r++,s==="%c"&&(o=r))}),e.splice(o,0,t)}y.log=console.debug||console.log||(()=>{});function Fe(e){try{e?y.storage.setItem("debug",e):y.storage.removeItem("debug")}catch{}}function ve(){let e;try{e=y.storage.getItem("debug")}catch{}return!e&&typeof process<"u"&&"env"in process&&(e=process.env.DEBUG),e}function Ae(){try{return localStorage}catch{}}R.exports=G()(y);var{formatters:Ee}=R.exports;Ee.j=function(e){try{return JSON.stringify(e)}catch(t){return"[UnexpectedJSONParseError]: "+t.message}}});var Ne={};ue(Ne,{defaultInit:()=>Ie,perfService:()=>Me});var x=de(W(),1);function Se(e,t){if(e.length>=255)throw new TypeError("Alphabet too long");for(var r=new Uint8Array(256),o=0;o<r.length;o++)r[o]=255;for(var s=0;s<e.length;s++){var d=e.charAt(s),f=d.charCodeAt(0);if(r[f]!==255)throw new TypeError(d+" is ambiguous");r[f]=s}var l=e.length,b=e.charAt(0),k=Math.log(l)/Math.log(256),n=Math.log(256)/Math.log(l);function a(i){if(i instanceof Uint8Array||(ArrayBuffer.isView(i)?i=new Uint8Array(i.buffer,i.byteOffset,i.byteLength):Array.isArray(i)&&(i=Uint8Array.from(i))),!(i instanceof Uint8Array))throw new TypeError("Expected Uint8Array");if(i.length===0)return"";for(var c=0,p=0,h=0,m=i.length;h!==m&&i[h]===0;)h++,c++;for(var F=(m-h)*n+1>>>0,C=new Uint8Array(F);h!==m;){for(var E=i[h],v=0,w=F-1;(E!==0||v<p)&&w!==-1;w--,v++)E+=256*C[w]>>>0,C[w]=E%l>>>0,E=E/l>>>0;if(E!==0)throw new Error("Non-zero carry");p=v,h++}for(var A=F-p;A!==F&&C[A]===0;)A++;for(var z=b.repeat(c);A<F;++A)z+=e.charAt(C[A]);return z}function u(i){if(typeof i!="string")throw new TypeError("Expected String");if(i.length===0)return new Uint8Array;var c=0;if(i[c]!==" "){for(var p=0,h=0;i[c]===b;)p++,c++;for(var m=(i.length-c)*k+1>>>0,F=new Uint8Array(m);i[c];){var C=r[i.charCodeAt(c)];if(C===255)return;for(var E=0,v=m-1;(C!==0||E<h)&&v!==-1;v--,E++)C+=l*F[v]>>>0,F[v]=C%256>>>0,C=C/256>>>0;if(C!==0)throw new Error("Non-zero carry");h=E,c++}if(i[c]!==" "){for(var w=m-h;w!==m&&F[w]===0;)w++;for(var A=new Uint8Array(p+(m-w)),z=p;w!==m;)A[z++]=F[w++];return A}}}function U(i){var c=u(i);if(c)return c;throw new Error(`Non-${t} character`)}return{encode:a,decodeUnsafe:u,decode:U}}var ke=Se,Ue=ke,X=Ue;var $e=new Uint8Array(0);var Q=e=>{if(e instanceof Uint8Array&&e.constructor.name==="Uint8Array")return e;if(e instanceof ArrayBuffer)return new Uint8Array(e);if(ArrayBuffer.isView(e))return new Uint8Array(e.buffer,e.byteOffset,e.byteLength);throw new Error("Unknown type, must be binary type")};var T=class{constructor(t,r,o){this.name=t,this.prefix=r,this.baseEncode=o}encode(t){if(t instanceof Uint8Array)return`${this.prefix}${this.baseEncode(t)}`;throw Error("Unknown type, must be binary type")}},$=class{constructor(t,r,o){if(this.name=t,this.prefix=r,r.codePointAt(0)===void 0)throw new Error("Invalid prefix character");this.prefixCodePoint=r.codePointAt(0),this.baseDecode=o}decode(t){if(typeof t=="string"){if(t.codePointAt(0)!==this.prefixCodePoint)throw Error(`Unable to decode multibase string ${JSON.stringify(t)}, ${this.name} decoder only supports inputs prefixed with ${this.prefix}`);return this.baseDecode(t.slice(this.prefix.length))}else throw Error("Can only multibase decode strings")}or(t){return Y(this,t)}},j=class{constructor(t){this.decoders=t}or(t){return Y(this,t)}decode(t){let r=t[0],o=this.decoders[r];if(o)return o.decode(t);throw RangeError(`Unable to decode multibase string ${JSON.stringify(t)}, only inputs prefixed with ${Object.keys(this.decoders)} are supported`)}},Y=(e,t)=>new j({...e.decoders||{[e.prefix]:e},...t.decoders||{[t.prefix]:t}}),D=class{constructor(t,r,o,s){this.name=t,this.prefix=r,this.baseEncode=o,this.baseDecode=s,this.encoder=new T(t,r,o),this.decoder=new $(t,r,s)}encode(t){return this.encoder.encode(t)}decode(t){return this.decoder.decode(t)}},H=({name:e,prefix:t,encode:r,decode:o})=>new D(e,t,r,o),_=({prefix:e,name:t,alphabet:r})=>{let{encode:o,decode:s}=X(r,t);return H({prefix:e,name:t,encode:o,decode:d=>Q(s(d))})},ze=(e,t,r,o)=>{let s={};for(let n=0;n<t.length;++n)s[t[n]]=n;let d=e.length;for(;e[d-1]==="=";)--d;let f=new Uint8Array(d*r/8|0),l=0,b=0,k=0;for(let n=0;n<d;++n){let a=s[e[n]];if(a===void 0)throw new SyntaxError(`Non-${o} character`);b=b<<r|a,l+=r,l>=8&&(l-=8,f[k++]=255&b>>l)}if(l>=r||255&b<<8-l)throw new SyntaxError("Unexpected end of data");return f},Be=(e,t,r)=>{let o=t[t.length-1]==="=",s=(1<<r)-1,d="",f=0,l=0;for(let b=0;b<e.length;++b)for(l=l<<8|e[b],f+=8;f>r;)f-=r,d+=t[s&l>>f];if(f&&(d+=t[s&l<<r-f]),o)for(;d.length*r&7;)d+="=";return d},g=({name:e,prefix:t,bitsPerChar:r,alphabet:o})=>H({prefix:t,name:e,encode(s){return Be(s,o,r)},decode(s){return ze(s,o,r,e)}});var ee=g({prefix:"b",name:"base32",alphabet:"abcdefghijklmnopqrstuvwxyz234567",bitsPerChar:5}),qe=g({prefix:"B",name:"base32upper",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567",bitsPerChar:5}),Ze=g({prefix:"c",name:"base32pad",alphabet:"abcdefghijklmnopqrstuvwxyz234567=",bitsPerChar:5}),Ke=g({prefix:"C",name:"base32padupper",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZ234567=",bitsPerChar:5}),Ge=g({prefix:"v",name:"base32hex",alphabet:"0123456789abcdefghijklmnopqrstuv",bitsPerChar:5}),We=g({prefix:"V",name:"base32hexupper",alphabet:"0123456789ABCDEFGHIJKLMNOPQRSTUV",bitsPerChar:5}),Xe=g({prefix:"t",name:"base32hexpad",alphabet:"0123456789abcdefghijklmnopqrstuv=",bitsPerChar:5}),Qe=g({prefix:"T",name:"base32hexpadupper",alphabet:"0123456789ABCDEFGHIJKLMNOPQRSTUV=",bitsPerChar:5}),Ye=g({prefix:"h",name:"base32z",alphabet:"ybndrfg8ejkmcpqxot1uwisza345h769",bitsPerChar:5});var re=_({name:"base58btc",prefix:"z",alphabet:"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"}),rr=_({name:"base58flickr",prefix:"Z",alphabet:"123456789abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ"});var te=g({prefix:"m",name:"base64",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",bitsPerChar:6}),or=g({prefix:"M",name:"base64pad",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",bitsPerChar:6}),sr=g({prefix:"u",name:"base64url",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_",bitsPerChar:6}),ar=g({prefix:"U",name:"base64urlpad",alphabet:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",bitsPerChar:6});x.default.formatters.b=e=>e==null?"undefined":re.baseEncode(e);x.default.formatters.t=e=>e==null?"undefined":ee.baseEncode(e);x.default.formatters.m=e=>e==null?"undefined":te.baseEncode(e);x.default.formatters.p=e=>e==null?"undefined":e.toString();x.default.formatters.c=e=>e==null?"undefined":e.toString();x.default.formatters.k=e=>e==null?"undefined":e.toString();x.default.formatters.a=e=>e==null?"undefined":e.toString();function Oe(e){let t=()=>{};return t.enabled=!1,t.color="",t.diff=0,t.log=()=>{},t.namespace=e,t.destroy=()=>!0,t.extend=()=>t,t}function ne(e){let t=Oe(`${e}:trace`);return x.default.enabled(`${e}:trace`)&&x.default.names.map(r=>r.toString()).find(r=>r.includes(":trace"))!=null&&(t=(0,x.default)(`${e}:trace`)),Object.assign((0,x.default)(e),{error:(0,x.default)(`${e}:error`),trace:t})}var oe="/perf/1.0.0",se=BigInt(65536);var M=ne("libp2p:perf"),Ie={protocolName:"/perf/1.0.0",writeBlockSize:BigInt(65536)},J=class{protocol;components;started;databuf;writeBlockSize;constructor(t,r){this.components=t,this.started=!1,this.protocol=r.protocolName??oe,this.writeBlockSize=r.writeBlockSize??se,this.databuf=new ArrayBuffer(Number(r.writeBlockSize))}async start(){await this.components.registrar.handle(this.protocol,t=>{this.handleMessage(t).catch(r=>{M.error("error handling perf protocol message",r)})}),this.started=!0}async stop(){await this.components.registrar.unhandle(this.protocol),this.started=!1}isStarted(){return this.started}async handleMessage(t){let{stream:r}=t,o=this.writeBlockSize,s=null;for await(let f of r.source)s===null&&(s=BigInt(f.getBigUint64(0,!1)));let d=new Uint8Array(this.databuf);if(s===null)throw new Error("bytesToSendBack was not set");await r.sink(async function*(){for(;s>0n;){let f=o;f>s&&(f=s),s=s-f,yield d.subarray(0,Number(f))}}())}async measurePerformance(t,r,o,s,d={}){M("opening stream on protocol %s to %p",this.protocol,r.remotePeer);let f=new Uint8Array(this.databuf),l=this.writeBlockSize,b=await r.newStream([this.protocol]);new DataView(this.databuf).setBigInt64(0,s,!1),M("sending %i bytes to %p",o,r.remotePeer);try{await b.sink(async function*(){for(yield f.subarray(0,8);o>0n;){let a=l;a>o&&(a=o),o=o-a,yield f.subarray(0,Number(a))}}());let n=BigInt(0);for await(let a of b.source)n+=BigInt(a.length);if(n!==s)throw new Error(`Expected to receive ${s} bytes, but received ${n}`)}catch(n){throw M("error sending %i bytes to %p: %s",o,r.remotePeer,n),n}finally{M("performed %s to %p",this.protocol,r.remotePeer),await b.close()}return Date.now()-t}};function Me(e={}){return t=>new J(t,e)}return pe(Ne);})();
3
+ return Libp2PPerf}));
@@ -0,0 +1,3 @@
1
+ export declare const PROTOCOL_NAME = "/perf/1.0.0";
2
+ export declare const WRITE_BLOCK_SIZE: bigint;
3
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,gBAAgB,CAAA;AAC1C,eAAO,MAAM,gBAAgB,QAAmB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export const PROTOCOL_NAME = '/perf/1.0.0';
2
+ export const WRITE_BLOCK_SIZE = BigInt(64 << 10);
3
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAA;AAC1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAA"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations
5
+ * addresses.
6
+ *
7
+ * @example
8
+ *
9
+ * ```typescript
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { perfService } from '@libp2p/perf'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * service: [
15
+ * perfService()
16
+ * ]
17
+ * })
18
+ * ```
19
+ *
20
+ * The `measurePerformance` function can be used to measure the latency and throughput of a connection.
21
+ * server. This will not work in browsers.
22
+ *
23
+ * @example
24
+ *
25
+ * ```typescript
26
+ * import { createLibp2p } from 'libp2p'
27
+ * import { perfService } from 'libp2p/perf'
28
+ *
29
+ * const node = await createLibp2p({
30
+ * services: [
31
+ * perf: perfService()
32
+ * ]
33
+ * })
34
+ *
35
+ * const connection = await node.dial(multiaddr(multiaddrAddress))
36
+ *
37
+ * const startTime = Date.now()
38
+ *
39
+ * await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
40
+ *
41
+ * ```
42
+ */
43
+ import type { Connection } from '@libp2p/interface/connection';
44
+ import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager';
45
+ import type { Registrar } from '@libp2p/interface-internal/registrar';
46
+ import type { AbortOptions } from '@libp2p/interfaces';
47
+ export declare const defaultInit: PerfServiceInit;
48
+ export interface PerfService {
49
+ measurePerformance: (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options?: AbortOptions) => Promise<number>;
50
+ }
51
+ export interface PerfServiceInit {
52
+ protocolName?: string;
53
+ maxInboundStreams?: number;
54
+ maxOutboundStreams?: number;
55
+ timeout?: number;
56
+ writeBlockSize?: bigint;
57
+ }
58
+ export interface PerfServiceComponents {
59
+ registrar: Registrar;
60
+ connectionManager: ConnectionManager;
61
+ }
62
+ export declare function perfService(init?: PerfServiceInit): (components: PerfServiceComponents) => PerfService;
63
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAE9D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+CAA+C,CAAA;AACtF,OAAO,KAAK,EAAsB,SAAS,EAAE,MAAM,sCAAsC,CAAA;AACzF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAA;AAItD,eAAO,MAAM,WAAW,EAAE,eAGzB,CAAA;AAED,MAAM,WAAW,WAAW;IAC1B,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,YAAY,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;CACjJ;AAED,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAED,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,SAAS,CAAA;IACpB,iBAAiB,EAAE,iBAAiB,CAAA;CACrC;AAsHD,wBAAgB,WAAW,CAAE,IAAI,GAAE,eAAoB,GAAG,CAAC,UAAU,EAAE,qBAAqB,KAAK,WAAW,CAE3G"}
@@ -0,0 +1,150 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations
5
+ * addresses.
6
+ *
7
+ * @example
8
+ *
9
+ * ```typescript
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { perfService } from '@libp2p/perf'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * service: [
15
+ * perfService()
16
+ * ]
17
+ * })
18
+ * ```
19
+ *
20
+ * The `measurePerformance` function can be used to measure the latency and throughput of a connection.
21
+ * server. This will not work in browsers.
22
+ *
23
+ * @example
24
+ *
25
+ * ```typescript
26
+ * import { createLibp2p } from 'libp2p'
27
+ * import { perfService } from 'libp2p/perf'
28
+ *
29
+ * const node = await createLibp2p({
30
+ * services: [
31
+ * perf: perfService()
32
+ * ]
33
+ * })
34
+ *
35
+ * const connection = await node.dial(multiaddr(multiaddrAddress))
36
+ *
37
+ * const startTime = Date.now()
38
+ *
39
+ * await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
40
+ *
41
+ * ```
42
+ */
43
+ import { logger } from '@libp2p/logger';
44
+ import { PROTOCOL_NAME, WRITE_BLOCK_SIZE } from './constants.js';
45
+ const log = logger('libp2p:perf');
46
+ export const defaultInit = {
47
+ protocolName: '/perf/1.0.0',
48
+ writeBlockSize: BigInt(64 << 10)
49
+ };
50
+ class DefaultPerfService {
51
+ protocol;
52
+ components;
53
+ started;
54
+ databuf;
55
+ writeBlockSize;
56
+ constructor(components, init) {
57
+ this.components = components;
58
+ this.started = false;
59
+ this.protocol = init.protocolName ?? PROTOCOL_NAME;
60
+ this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE;
61
+ this.databuf = new ArrayBuffer(Number(init.writeBlockSize));
62
+ }
63
+ async start() {
64
+ await this.components.registrar.handle(this.protocol, (data) => {
65
+ void this.handleMessage(data).catch((err) => {
66
+ log.error('error handling perf protocol message', err);
67
+ });
68
+ });
69
+ this.started = true;
70
+ }
71
+ async stop() {
72
+ await this.components.registrar.unhandle(this.protocol);
73
+ this.started = false;
74
+ }
75
+ isStarted() {
76
+ return this.started;
77
+ }
78
+ async handleMessage(data) {
79
+ const { stream } = data;
80
+ const writeBlockSize = this.writeBlockSize;
81
+ let bytesToSendBack = null;
82
+ for await (const buf of stream.source) {
83
+ if (bytesToSendBack === null) {
84
+ bytesToSendBack = BigInt(buf.getBigUint64(0, false));
85
+ }
86
+ // Ingest all the bufs and wait for the read side to close
87
+ }
88
+ const uint8Buf = new Uint8Array(this.databuf);
89
+ if (bytesToSendBack === null) {
90
+ throw new Error('bytesToSendBack was not set');
91
+ }
92
+ await stream.sink(async function* () {
93
+ while (bytesToSendBack > 0n) {
94
+ let toSend = writeBlockSize;
95
+ if (toSend > bytesToSendBack) {
96
+ toSend = bytesToSendBack;
97
+ }
98
+ bytesToSendBack = bytesToSendBack - toSend;
99
+ yield uint8Buf.subarray(0, Number(toSend));
100
+ }
101
+ }());
102
+ }
103
+ async measurePerformance(startTime, connection, sendBytes, recvBytes, options = {}) {
104
+ log('opening stream on protocol %s to %p', this.protocol, connection.remotePeer);
105
+ const uint8Buf = new Uint8Array(this.databuf);
106
+ const writeBlockSize = this.writeBlockSize;
107
+ const stream = await connection.newStream([this.protocol]);
108
+ // Convert sendBytes to uint64 big endian buffer
109
+ const view = new DataView(this.databuf);
110
+ view.setBigInt64(0, recvBytes, false);
111
+ log('sending %i bytes to %p', sendBytes, connection.remotePeer);
112
+ try {
113
+ await stream.sink((async function* () {
114
+ // Send the number of bytes to receive
115
+ yield uint8Buf.subarray(0, 8);
116
+ // Send the number of bytes to send
117
+ while (sendBytes > 0n) {
118
+ let toSend = writeBlockSize;
119
+ if (toSend > sendBytes) {
120
+ toSend = sendBytes;
121
+ }
122
+ sendBytes = sendBytes - toSend;
123
+ yield uint8Buf.subarray(0, Number(toSend));
124
+ }
125
+ })());
126
+ // Read the received bytes
127
+ let actualRecvdBytes = BigInt(0);
128
+ for await (const buf of stream.source) {
129
+ actualRecvdBytes += BigInt(buf.length);
130
+ }
131
+ if (actualRecvdBytes !== recvBytes) {
132
+ throw new Error(`Expected to receive ${recvBytes} bytes, but received ${actualRecvdBytes}`);
133
+ }
134
+ }
135
+ catch (err) {
136
+ log('error sending %i bytes to %p: %s', sendBytes, connection.remotePeer, err);
137
+ throw err;
138
+ }
139
+ finally {
140
+ log('performed %s to %p', this.protocol, connection.remotePeer);
141
+ await stream.close();
142
+ }
143
+ // Return the latency
144
+ return Date.now() - startTime;
145
+ }
146
+ }
147
+ export function perfService(init = {}) {
148
+ return (components) => new DefaultPerfService(components, init);
149
+ }
150
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function main(runServer: boolean, serverIpAddress: string, transport: string, uploadBytes: number, downloadBytes: number): Promise<void>;
2
+ //# sourceMappingURL=main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"AAkDA,wBAAsB,IAAI,CAAE,SAAS,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CA2DrJ"}
@@ -0,0 +1,114 @@
1
+ import { yamux } from '@chainsafe/libp2p-yamux';
2
+ import { unmarshalPrivateKey } from '@libp2p/crypto/keys';
3
+ import { createFromPrivKey } from '@libp2p/peer-id-factory';
4
+ import { tcp } from '@libp2p/tcp';
5
+ import { multiaddr } from '@multiformats/multiaddr';
6
+ import { createLibp2p } from 'libp2p';
7
+ import { plaintext } from 'libp2p/insecure';
8
+ import pWaitFor from 'p-wait-for';
9
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string';
10
+ import yargs from 'yargs';
11
+ import { hideBin } from 'yargs/helpers';
12
+ import { defaultInit, perfService } from '../src/index.js';
13
+ const argv = yargs(hideBin(process.argv))
14
+ .options({
15
+ 'run-server': {
16
+ type: 'boolean',
17
+ demandOption: true,
18
+ default: false,
19
+ description: 'Whether to run as a server'
20
+ },
21
+ 'server-address': {
22
+ type: 'string',
23
+ demandOption: false,
24
+ description: 'Server IP address',
25
+ default: ''
26
+ },
27
+ transport: {
28
+ type: 'string',
29
+ demandOption: false,
30
+ description: 'Transport to use',
31
+ default: 'tcp'
32
+ },
33
+ 'upload-bytes': {
34
+ type: 'number',
35
+ demandOption: false,
36
+ description: 'Number of bytes to upload',
37
+ default: 0
38
+ },
39
+ 'download-bytes': {
40
+ type: 'number',
41
+ demandOption: false,
42
+ description: 'Number of bytes to download',
43
+ default: 0
44
+ }
45
+ })
46
+ .command('help', 'Print usage information', yargs.help)
47
+ .parseSync();
48
+ export async function main(runServer, serverIpAddress, transport, uploadBytes, downloadBytes) {
49
+ const listenAddrs = [];
50
+ const { host, port } = splitHostPort(serverIpAddress);
51
+ // #TODO: right now we only support tcp
52
+ const tcpMultiaddr = multiaddr(`/ip4/${host}/tcp/${port}`);
53
+ const config = {
54
+ transports: [tcp()],
55
+ streamMuxers: [yamux()],
56
+ connectionEncryption: [
57
+ plaintext()
58
+ ],
59
+ services: {
60
+ perf: perfService(defaultInit)
61
+ }
62
+ };
63
+ const testPrivKey = 'CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw==';
64
+ const encoded = uint8ArrayFromString(testPrivKey, 'base64pad');
65
+ const privateKey = await unmarshalPrivateKey(encoded);
66
+ const peerId = await createFromPrivKey(privateKey);
67
+ const tcpMultiaddrAddress = `${tcpMultiaddr.toString()}/p2p/${peerId.toString()}`;
68
+ if (runServer) {
69
+ listenAddrs.push(tcpMultiaddrAddress);
70
+ Object.assign(config, {
71
+ peerId,
72
+ addresses: {
73
+ listen: listenAddrs
74
+ }
75
+ });
76
+ }
77
+ const node = await createLibp2p(config);
78
+ await node.start();
79
+ const startTime = Date.now();
80
+ let connection = null;
81
+ if (runServer) {
82
+ node.addEventListener('connection:open', (eventInfo) => {
83
+ connection = eventInfo.detail;
84
+ });
85
+ }
86
+ else {
87
+ connection = await node.dial(multiaddr(tcpMultiaddrAddress));
88
+ }
89
+ await pWaitFor(() => connection != null);
90
+ const duration = await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes));
91
+ await node.stop();
92
+ // eslint-disable-next-line no-console
93
+ console.log('latency: ' + JSON.stringify({ latency: duration }));
94
+ }
95
+ function splitHostPort(address) {
96
+ try {
97
+ const parts = address.split(':');
98
+ const host = parts[0];
99
+ const port = parts[1];
100
+ return {
101
+ host,
102
+ port
103
+ };
104
+ }
105
+ catch (error) {
106
+ throw Error('Invalid server address');
107
+ }
108
+ }
109
+ main(argv['run-server'], argv['server-address'], argv.transport, argv['upload-bytes'], argv['download-bytes']).catch((err) => {
110
+ // eslint-disable-next-line no-console
111
+ console.error(err);
112
+ process.exit(1);
113
+ });
114
+ //# sourceMappingURL=main.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/main.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,11 +1,68 @@
1
1
  {
2
2
  "name": "@libp2p/perf",
3
- "version": "0.0.0",
4
- "description": "",
5
- "main": "index.js",
3
+ "version": "1.0.0-18567b7c",
4
+ "description": "Implementation of Perf Protocol",
5
+ "author": "@maschad / @marcopolo",
6
+ "license": "Apache-2.0 OR MIT",
7
+ "homepage": "https://github.com/libp2p/js-libp2p/tree/master/packages/protocol-perf#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/libp2p/js-libp2p.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/libp2p/js-libp2p/issues"
14
+ },
15
+ "type": "module",
16
+ "types": "./dist/src/index.d.ts",
17
+ "files": [
18
+ "src",
19
+ "dist",
20
+ "!dist/test",
21
+ "!**/*.tsbuildinfo"
22
+ ],
23
+ "exports": {
24
+ ".": {
25
+ "types": "./dist/src/index.d.ts",
26
+ "import": "./dist/src/index.js"
27
+ }
28
+ },
29
+ "eslintConfig": {
30
+ "extends": "ipfs",
31
+ "parserOptions": {
32
+ "sourceType": "module"
33
+ }
34
+ },
6
35
  "scripts": {
7
- "test": "echo \"Error: no test specified\" && exit 1"
36
+ "start": "node dist/src/main.js",
37
+ "build": "aegir build",
38
+ "test": "aegir test",
39
+ "clean": "aegir clean",
40
+ "lint": "aegir lint",
41
+ "test:chrome": "aegir test -t browser --cov",
42
+ "test:chrome-webworker": "aegir test -t webworker",
43
+ "test:firefox": "aegir test -t browser -- --browser firefox",
44
+ "test:firefox-webworker": "aegir test -t webworker -- --browser firefox",
45
+ "test:node": "aegir test -t node --cov",
46
+ "dep-check": "aegir dep-check",
47
+ "renderResults": "node dist/src/renderResults.js"
48
+ },
49
+ "dependencies": {
50
+ "@chainsafe/libp2p-yamux": "^5.0.0",
51
+ "@libp2p/crypto": "2.0.2-18567b7c",
52
+ "@libp2p/interface": "0.1.1-18567b7c",
53
+ "@libp2p/interface-compliance-tests": "4.0.2-18567b7c",
54
+ "@libp2p/interface-internal": "0.1.2-18567b7c",
55
+ "@libp2p/interfaces": "3.3.2",
56
+ "@libp2p/logger": "3.0.1-18567b7c",
57
+ "@libp2p/peer-id-factory": "3.0.2-18567b7c",
58
+ "@libp2p/tcp": "8.0.2-18567b7c",
59
+ "@multiformats/multiaddr": "^12.1.5",
60
+ "libp2p": "0.46.3-18567b7c",
61
+ "p-wait-for": "^5.0.2",
62
+ "uint8arrays": "^4.0.6",
63
+ "yargs": "^17.7.2"
8
64
  },
9
- "author": "",
10
- "license": "MIT"
65
+ "devDependencies": {
66
+ "aegir": "^40.0.8"
67
+ }
11
68
  }
@@ -0,0 +1,2 @@
1
+ export const PROTOCOL_NAME = '/perf/1.0.0'
2
+ export const WRITE_BLOCK_SIZE = BigInt(64 << 10)
package/src/index.ts ADDED
@@ -0,0 +1,194 @@
1
+ /**
2
+ * @packageDocumentation
3
+ *
4
+ * The `performanceService` implements the [perf protocol](https://github.com/libp2p/specs/blob/master/perf/perf.md), which is used to measure performance within and across libp2p implementations
5
+ * addresses.
6
+ *
7
+ * @example
8
+ *
9
+ * ```typescript
10
+ * import { createLibp2p } from 'libp2p'
11
+ * import { perfService } from '@libp2p/perf'
12
+ *
13
+ * const node = await createLibp2p({
14
+ * service: [
15
+ * perfService()
16
+ * ]
17
+ * })
18
+ * ```
19
+ *
20
+ * The `measurePerformance` function can be used to measure the latency and throughput of a connection.
21
+ * server. This will not work in browsers.
22
+ *
23
+ * @example
24
+ *
25
+ * ```typescript
26
+ * import { createLibp2p } from 'libp2p'
27
+ * import { perfService } from 'libp2p/perf'
28
+ *
29
+ * const node = await createLibp2p({
30
+ * services: [
31
+ * perf: perfService()
32
+ * ]
33
+ * })
34
+ *
35
+ * const connection = await node.dial(multiaddr(multiaddrAddress))
36
+ *
37
+ * const startTime = Date.now()
38
+ *
39
+ * await node.services.perf.measurePerformance(startTime, connection, BigInt(uploadBytes), BigInt(downloadBytes))
40
+ *
41
+ * ```
42
+ */
43
+
44
+ import { logger } from '@libp2p/logger'
45
+ import { PROTOCOL_NAME, WRITE_BLOCK_SIZE } from './constants.js'
46
+ import type { Connection } from '@libp2p/interface/connection'
47
+ import type { Startable } from '@libp2p/interface/startable'
48
+ import type { ConnectionManager } from '@libp2p/interface-internal/connection-manager'
49
+ import type { IncomingStreamData, Registrar } from '@libp2p/interface-internal/registrar'
50
+ import type { AbortOptions } from '@libp2p/interfaces'
51
+
52
+ const log = logger('libp2p:perf')
53
+
54
+ export const defaultInit: PerfServiceInit = {
55
+ protocolName: '/perf/1.0.0',
56
+ writeBlockSize: BigInt(64 << 10)
57
+ }
58
+
59
+ export interface PerfService {
60
+ measurePerformance: (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options?: AbortOptions) => Promise<number>
61
+ }
62
+
63
+ export interface PerfServiceInit {
64
+ protocolName?: string
65
+ maxInboundStreams?: number
66
+ maxOutboundStreams?: number
67
+ timeout?: number
68
+ writeBlockSize?: bigint
69
+ }
70
+
71
+ export interface PerfServiceComponents {
72
+ registrar: Registrar
73
+ connectionManager: ConnectionManager
74
+ }
75
+
76
+ class DefaultPerfService implements Startable, PerfService {
77
+ public readonly protocol: string
78
+ private readonly components: PerfServiceComponents
79
+ private started: boolean
80
+ private readonly databuf: ArrayBuffer
81
+ private readonly writeBlockSize: bigint
82
+
83
+ constructor (components: PerfServiceComponents, init: PerfServiceInit) {
84
+ this.components = components
85
+ this.started = false
86
+ this.protocol = init.protocolName ?? PROTOCOL_NAME
87
+ this.writeBlockSize = init.writeBlockSize ?? WRITE_BLOCK_SIZE
88
+ this.databuf = new ArrayBuffer(Number(init.writeBlockSize))
89
+ }
90
+
91
+ async start (): Promise<void> {
92
+ await this.components.registrar.handle(this.protocol, (data: IncomingStreamData) => {
93
+ void this.handleMessage(data).catch((err) => {
94
+ log.error('error handling perf protocol message', err)
95
+ })
96
+ })
97
+ this.started = true
98
+ }
99
+
100
+ async stop (): Promise<void> {
101
+ await this.components.registrar.unhandle(this.protocol)
102
+ this.started = false
103
+ }
104
+
105
+ isStarted (): boolean {
106
+ return this.started
107
+ }
108
+
109
+ async handleMessage (data: IncomingStreamData): Promise<void> {
110
+ const { stream } = data
111
+
112
+ const writeBlockSize = this.writeBlockSize
113
+
114
+ let bytesToSendBack: bigint | null = null
115
+
116
+ for await (const buf of stream.source) {
117
+ if (bytesToSendBack === null) {
118
+ bytesToSendBack = BigInt(buf.getBigUint64(0, false))
119
+ }
120
+ // Ingest all the bufs and wait for the read side to close
121
+ }
122
+
123
+ const uint8Buf = new Uint8Array(this.databuf)
124
+
125
+ if (bytesToSendBack === null) {
126
+ throw new Error('bytesToSendBack was not set')
127
+ }
128
+
129
+ await stream.sink(async function * () {
130
+ while (bytesToSendBack > 0n) {
131
+ let toSend: bigint = writeBlockSize
132
+ if (toSend > bytesToSendBack) {
133
+ toSend = bytesToSendBack
134
+ }
135
+ bytesToSendBack = bytesToSendBack - toSend
136
+ yield uint8Buf.subarray(0, Number(toSend))
137
+ }
138
+ }())
139
+ }
140
+
141
+ async measurePerformance (startTime: number, connection: Connection, sendBytes: bigint, recvBytes: bigint, options: AbortOptions = {}): Promise<number> {
142
+ log('opening stream on protocol %s to %p', this.protocol, connection.remotePeer)
143
+
144
+ const uint8Buf = new Uint8Array(this.databuf)
145
+
146
+ const writeBlockSize = this.writeBlockSize
147
+
148
+ const stream = await connection.newStream([this.protocol])
149
+
150
+ // Convert sendBytes to uint64 big endian buffer
151
+ const view = new DataView(this.databuf)
152
+ view.setBigInt64(0, recvBytes, false)
153
+
154
+ log('sending %i bytes to %p', sendBytes, connection.remotePeer)
155
+ try {
156
+ await stream.sink((async function * () {
157
+ // Send the number of bytes to receive
158
+ yield uint8Buf.subarray(0, 8)
159
+ // Send the number of bytes to send
160
+ while (sendBytes > 0n) {
161
+ let toSend: bigint = writeBlockSize
162
+ if (toSend > sendBytes) {
163
+ toSend = sendBytes
164
+ }
165
+ sendBytes = sendBytes - toSend
166
+ yield uint8Buf.subarray(0, Number(toSend))
167
+ }
168
+ })())
169
+
170
+ // Read the received bytes
171
+ let actualRecvdBytes = BigInt(0)
172
+ for await (const buf of stream.source) {
173
+ actualRecvdBytes += BigInt(buf.length)
174
+ }
175
+
176
+ if (actualRecvdBytes !== recvBytes) {
177
+ throw new Error(`Expected to receive ${recvBytes} bytes, but received ${actualRecvdBytes}`)
178
+ }
179
+ } catch (err) {
180
+ log('error sending %i bytes to %p: %s', sendBytes, connection.remotePeer, err)
181
+ throw err
182
+ } finally {
183
+ log('performed %s to %p', this.protocol, connection.remotePeer)
184
+ await stream.close()
185
+ }
186
+
187
+ // Return the latency
188
+ return Date.now() - startTime
189
+ }
190
+ }
191
+
192
+ export function perfService (init: PerfServiceInit = {}): (components: PerfServiceComponents) => PerfService {
193
+ return (components) => new DefaultPerfService(components, init)
194
+ }
package/src/main.ts ADDED
@@ -0,0 +1,130 @@
1
+ import { yamux } from '@chainsafe/libp2p-yamux'
2
+ import { unmarshalPrivateKey } from '@libp2p/crypto/keys'
3
+ import { createFromPrivKey } from '@libp2p/peer-id-factory'
4
+ import { tcp } from '@libp2p/tcp'
5
+ import { multiaddr } from '@multiformats/multiaddr'
6
+ import { createLibp2p } from 'libp2p'
7
+ import { plaintext } from 'libp2p/insecure'
8
+ import pWaitFor from 'p-wait-for'
9
+ import { fromString as uint8ArrayFromString } from 'uint8arrays/from-string'
10
+ import yargs from 'yargs'
11
+ import { hideBin } from 'yargs/helpers'
12
+ import { defaultInit, perfService } from '../src/index.js'
13
+ import type { Connection } from '@libp2p/interface/connection'
14
+
15
+ const argv = yargs(hideBin(process.argv))
16
+ .options({
17
+ 'run-server': {
18
+ type: 'boolean',
19
+ demandOption: true,
20
+ default: false,
21
+ description: 'Whether to run as a server'
22
+ },
23
+ 'server-address': {
24
+ type: 'string',
25
+ demandOption: false,
26
+ description: 'Server IP address',
27
+ default: ''
28
+ },
29
+ transport: {
30
+ type: 'string',
31
+ demandOption: false,
32
+ description: 'Transport to use',
33
+ default: 'tcp'
34
+ },
35
+ 'upload-bytes': {
36
+ type: 'number',
37
+ demandOption: false,
38
+ description: 'Number of bytes to upload',
39
+ default: 0
40
+ },
41
+ 'download-bytes': {
42
+ type: 'number',
43
+ demandOption: false,
44
+ description: 'Number of bytes to download',
45
+ default: 0
46
+ }
47
+ })
48
+ .command('help', 'Print usage information', yargs.help)
49
+ .parseSync()
50
+
51
+ export async function main (runServer: boolean, serverIpAddress: string, transport: string, uploadBytes: number, downloadBytes: number): Promise<void> {
52
+ const listenAddrs: string[] = []
53
+
54
+ const { host, port } = splitHostPort(serverIpAddress)
55
+ // #TODO: right now we only support tcp
56
+ const tcpMultiaddr = multiaddr(`/ip4/${host}/tcp/${port}`)
57
+
58
+ const config = {
59
+ transports: [tcp()],
60
+ streamMuxers: [yamux()],
61
+ connectionEncryption: [
62
+ plaintext()
63
+ ],
64
+ services: {
65
+ perf: perfService(defaultInit)
66
+ }
67
+ }
68
+
69
+ const testPrivKey = 'CAASpgkwggSiAgEAAoIBAQC2SKo/HMFZeBml1AF3XijzrxrfQXdJzjePBZAbdxqKR1Mc6juRHXij6HXYPjlAk01BhF1S3Ll4Lwi0cAHhggf457sMg55UWyeGKeUv0ucgvCpBwlR5cQ020i0MgzjPWOLWq1rtvSbNcAi2ZEVn6+Q2EcHo3wUvWRtLeKz+DZSZfw2PEDC+DGPJPl7f8g7zl56YymmmzH9liZLNrzg/qidokUv5u1pdGrcpLuPNeTODk0cqKB+OUbuKj9GShYECCEjaybJDl9276oalL9ghBtSeEv20kugatTvYy590wFlJkkvyl+nPxIH0EEYMKK9XRWlu9XYnoSfboiwcv8M3SlsjAgMBAAECggEAZtju/bcKvKFPz0mkHiaJcpycy9STKphorpCT83srBVQi59CdFU6Mj+aL/xt0kCPMVigJw8P3/YCEJ9J+rS8BsoWE+xWUEsJvtXoT7vzPHaAtM3ci1HZd302Mz1+GgS8Epdx+7F5p80XAFLDUnELzOzKftvWGZmWfSeDnslwVONkL/1VAzwKy7Ce6hk4SxRE7l2NE2OklSHOzCGU1f78ZzVYKSnS5Ag9YrGjOAmTOXDbKNKN/qIorAQ1bovzGoCwx3iGIatQKFOxyVCyO1PsJYT7JO+kZbhBWRRE+L7l+ppPER9bdLFxs1t5CrKc078h+wuUr05S1P1JjXk68pk3+kQKBgQDeK8AR11373Mzib6uzpjGzgNRMzdYNuExWjxyxAzz53NAR7zrPHvXvfIqjDScLJ4NcRO2TddhXAfZoOPVH5k4PJHKLBPKuXZpWlookCAyENY7+Pd55S8r+a+MusrMagYNljb5WbVTgN8cgdpim9lbbIFlpN6SZaVjLQL3J8TWH6wKBgQDSChzItkqWX11CNstJ9zJyUE20I7LrpyBJNgG1gtvz3ZMUQCn3PxxHtQzN9n1P0mSSYs+jBKPuoSyYLt1wwe10/lpgL4rkKWU3/m1Myt0tveJ9WcqHh6tzcAbb/fXpUFT/o4SWDimWkPkuCb+8j//2yiXk0a/T2f36zKMuZvujqQKBgC6B7BAQDG2H2B/ijofp12ejJU36nL98gAZyqOfpLJ+FeMz4TlBDQ+phIMhnHXA5UkdDapQ+zA3SrFk+6yGk9Vw4Hf46B+82SvOrSbmnMa+PYqKYIvUzR4gg34rL/7AhwnbEyD5hXq4dHwMNsIDq+l2elPjwm/U9V0gdAl2+r50HAoGALtsKqMvhv8HucAMBPrLikhXP/8um8mMKFMrzfqZ+otxfHzlhI0L08Bo3jQrb0Z7ByNY6M8epOmbCKADsbWcVre/AAY0ZkuSZK/CaOXNX/AhMKmKJh8qAOPRY02LIJRBCpfS4czEdnfUhYV/TYiFNnKRj57PPYZdTzUsxa/yVTmECgYBr7slQEjb5Onn5mZnGDh+72BxLNdgwBkhO0OCdpdISqk0F0Pxby22DFOKXZEpiyI9XYP1C8wPiJsShGm2yEwBPWXnrrZNWczaVuCbXHrZkWQogBDG3HGXNdU4MAWCyiYlyinIBpPpoAJZSzpGLmWbMWh28+RJS6AQX6KHrK1o2uw=='
70
+ const encoded = uint8ArrayFromString(testPrivKey, 'base64pad')
71
+ const privateKey = await unmarshalPrivateKey(encoded)
72
+ const peerId = await createFromPrivKey(privateKey)
73
+ const tcpMultiaddrAddress = `${tcpMultiaddr.toString()}/p2p/${peerId.toString()}`
74
+
75
+ if (runServer) {
76
+ listenAddrs.push(tcpMultiaddrAddress)
77
+
78
+ Object.assign(config, {
79
+ peerId,
80
+ addresses: {
81
+ listen: listenAddrs
82
+ }
83
+ })
84
+ }
85
+
86
+ const node = await createLibp2p(config)
87
+
88
+ await node.start()
89
+
90
+ const startTime = Date.now()
91
+
92
+ let connection: any = null
93
+
94
+ if (runServer) {
95
+ node.addEventListener('connection:open', (eventInfo) => {
96
+ connection = eventInfo.detail
97
+ })
98
+ } else {
99
+ connection = await node.dial(multiaddr(tcpMultiaddrAddress))
100
+ }
101
+
102
+ await pWaitFor(() => connection != null)
103
+
104
+ const duration = await node.services.perf.measurePerformance(startTime, connection as Connection, BigInt(uploadBytes), BigInt(downloadBytes))
105
+
106
+ await node.stop()
107
+
108
+ // eslint-disable-next-line no-console
109
+ console.log('latency: ' + JSON.stringify({ latency: duration }))
110
+ }
111
+
112
+ function splitHostPort (address: string): { host: string, port?: string } {
113
+ try {
114
+ const parts = address.split(':')
115
+ const host = parts[0]
116
+ const port = parts[1]
117
+ return {
118
+ host,
119
+ port
120
+ }
121
+ } catch (error) {
122
+ throw Error('Invalid server address')
123
+ }
124
+ }
125
+
126
+ main(argv['run-server'], argv['server-address'], argv.transport, argv['upload-bytes'], argv['download-bytes']).catch((err) => {
127
+ // eslint-disable-next-line no-console
128
+ console.error(err)
129
+ process.exit(1)
130
+ })