@libp2p/perf 0.0.0 → 1.0.0-364e0592
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 +4 -0
- package/README.md +45 -0
- package/dist/index.min.js +3 -0
- package/dist/src/constants.d.ts +3 -0
- package/dist/src/constants.d.ts.map +1 -0
- package/dist/src/constants.js +3 -0
- package/dist/src/constants.js.map +1 -0
- package/dist/src/index.d.ts +63 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +150 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/main.d.ts +2 -0
- package/dist/src/main.d.ts.map +1 -0
- package/dist/src/main.js +114 -0
- package/dist/src/main.js.map +1 -0
- package/package.json +63 -6
- package/src/constants.ts +2 -0
- package/src/index.ts +194 -0
- package/src/main.ts +130 -0
package/LICENSE
ADDED
package/README.md
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# @libp2p/perf <!-- omit in toc -->
|
2
|
+
|
3
|
+
[](http://libp2p.io/)
|
4
|
+
[](https://discuss.libp2p.io)
|
5
|
+
[](https://codecov.io/gh/libp2p/js-libp2p)
|
6
|
+
[](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 @@
|
|
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 @@
|
|
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 @@
|
|
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"}
|
package/dist/src/main.js
ADDED
@@ -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": "
|
4
|
-
"description": "",
|
5
|
-
"
|
3
|
+
"version": "1.0.0-364e0592",
|
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
|
-
"
|
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-364e0592",
|
52
|
+
"@libp2p/interface": "0.1.1-364e0592",
|
53
|
+
"@libp2p/interface-compliance-tests": "4.0.2-364e0592",
|
54
|
+
"@libp2p/interface-internal": "0.1.2-364e0592",
|
55
|
+
"@libp2p/interfaces": "3.3.2",
|
56
|
+
"@libp2p/logger": "3.0.1-364e0592",
|
57
|
+
"@libp2p/peer-id-factory": "3.0.2-364e0592",
|
58
|
+
"@libp2p/tcp": "8.0.2-364e0592",
|
59
|
+
"@multiformats/multiaddr": "^12.1.5",
|
60
|
+
"libp2p": "0.46.3-364e0592",
|
61
|
+
"p-wait-for": "^5.0.2",
|
62
|
+
"uint8arrays": "^4.0.6",
|
63
|
+
"yargs": "^17.7.2"
|
8
64
|
},
|
9
|
-
"
|
10
|
-
|
65
|
+
"devDependencies": {
|
66
|
+
"aegir": "^40.0.8"
|
67
|
+
}
|
11
68
|
}
|
package/src/constants.ts
ADDED
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
|
+
})
|