@leofcoin/peernet 0.11.0 → 0.11.1
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/coverage/lcov-report/base.css +224 -0
- package/coverage/lcov-report/block-navigation.js +87 -0
- package/coverage/lcov-report/codec-format-interface.js.html +637 -0
- package/coverage/lcov-report/dht-response.js.html +193 -0
- package/coverage/lcov-report/favicon.png +0 -0
- package/coverage/lcov-report/index.html +131 -0
- package/coverage/lcov-report/prettify.css +1 -0
- package/coverage/lcov-report/prettify.js +2 -0
- package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
- package/coverage/lcov-report/sorter.js +196 -0
- package/coverage/lcov.info +199 -0
- package/dist/browser/peernet.js +1288 -1385
- package/dist/commonjs/{client-bd0caeb7.js → client-1a1f75e6.js} +41 -41
- package/dist/commonjs/{codec-4a768e5e.js → codec-8c8c652f.js} +118 -118
- package/dist/commonjs/codec-format-interface.js +167 -167
- package/dist/commonjs/codec.js +2 -2
- package/dist/commonjs/dht-response.js +11 -11
- package/dist/commonjs/dht.js +2 -2
- package/dist/commonjs/hash.js +149 -149
- package/dist/commonjs/{http-2b0735ef.js → http-7bbac90a.js} +1 -1
- package/dist/commonjs/peernet-message.js +2 -2
- package/dist/commonjs/peernet.js +843 -934
- package/dist/commonjs/request.js +2 -2
- package/dist/commonjs/response.js +2 -2
- package/dist/module/peernet.js +1357 -1448
- package/package.json +2 -18
- package/src/client.js +75 -75
- package/src/codec/codec-format-interface.js +172 -172
- package/src/codec/codec.js +124 -124
- package/src/dht/dht.js +121 -121
- package/src/discovery/peer-discovery.js +75 -75
- package/src/hash/hash.js +155 -155
- package/src/http/client/http-client.js +44 -44
- package/src/messages/dht-response.js +14 -14
- package/src/peer.js +67 -67
- package/src/peernet.js +612 -612
- package/src/proto/chat-message.proto.js +7 -7
- package/src/utils/utils.js +78 -78
package/dist/module/peernet.js
CHANGED
|
@@ -2,7 +2,7 @@ import LeofcoinStorage from '@leofcoin/storage';
|
|
|
2
2
|
import protons from 'protons';
|
|
3
3
|
import bs32 from '@vandeurenglenn/base32';
|
|
4
4
|
import bs58 from '@vandeurenglenn/base58';
|
|
5
|
-
import isHex from 'is-hex';
|
|
5
|
+
import isHex from '@vandeurenglenn/is-hex';
|
|
6
6
|
import varint from 'varint';
|
|
7
7
|
import createKeccakHash from 'keccak';
|
|
8
8
|
import fetch from 'node-fetch';
|
|
@@ -706,439 +706,439 @@ var codecs = {
|
|
|
706
706
|
},
|
|
707
707
|
};
|
|
708
708
|
|
|
709
|
-
class PeernetCodec {
|
|
710
|
-
get codecs() {
|
|
711
|
-
return {...globalThis.peernet.codecs, ...codecs}
|
|
712
|
-
}
|
|
713
|
-
constructor(buffer) {
|
|
714
|
-
if (buffer) {
|
|
715
|
-
if (buffer instanceof Uint8Array) {
|
|
716
|
-
const codec = varint.decode(buffer);
|
|
717
|
-
const name = this.getCodecName(codec);
|
|
718
|
-
if (name) {
|
|
719
|
-
this.name = name;
|
|
720
|
-
this.encoded = buffer;
|
|
721
|
-
this.decode(buffer);
|
|
722
|
-
} else {
|
|
723
|
-
this.encode(buffer);
|
|
724
|
-
}
|
|
725
|
-
} else if (buffer instanceof ArrayBuffer) {
|
|
726
|
-
const encoded = new Uint8Array(buffer.byteLength);
|
|
727
|
-
|
|
728
|
-
for (let i = 0; i < buffer.byteLength; i++) {
|
|
729
|
-
encoded[i] = buffer[i];
|
|
730
|
-
}
|
|
731
|
-
this.encoded = encoded;
|
|
732
|
-
// this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
|
|
733
|
-
this.decode(buffer);
|
|
734
|
-
return
|
|
735
|
-
}
|
|
736
|
-
if (typeof buffer === 'string') {
|
|
737
|
-
if (this.codecs[buffer]) this.fromName(buffer);
|
|
738
|
-
else if (isHex(buffer)) this.fromHex(buffer);
|
|
739
|
-
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
740
|
-
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
741
|
-
else throw new Error(`unsupported string ${buffer}`)
|
|
742
|
-
}
|
|
743
|
-
if (!isNaN(buffer)) if (this.codecs[this.getCodecName(buffer)]) this.fromCodec(buffer);
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
|
|
747
|
-
fromEncoded(encoded) {
|
|
748
|
-
const codec = varint.decode(encoded);
|
|
749
|
-
const name = this.getCodecName(codec);
|
|
750
|
-
this.name = name;
|
|
751
|
-
this.encoded = encoded;
|
|
752
|
-
this.decode(encoded);
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
fromHex(hex) {
|
|
756
|
-
this.encoded = Buffer.from(hex, 'hex');
|
|
757
|
-
this.decode();
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
fromBs32(input) {
|
|
761
|
-
this.encoded = bs32.decode(input);
|
|
762
|
-
this.decode();
|
|
763
|
-
}
|
|
764
|
-
|
|
765
|
-
fromBs58(input) {
|
|
766
|
-
this.encoded = bs58.decode(input);
|
|
767
|
-
this.decode();
|
|
768
|
-
}
|
|
769
|
-
|
|
770
|
-
getCodec(name) {
|
|
771
|
-
return this.codecs[name].codec
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
getCodecName(codec) {
|
|
775
|
-
return Object.keys(this.codecs).reduce((p, c) => {
|
|
776
|
-
const item = this.codecs[c];
|
|
777
|
-
if (item.codec === codec) return c;
|
|
778
|
-
else return p;
|
|
779
|
-
}, undefined)
|
|
780
|
-
}
|
|
781
|
-
|
|
782
|
-
getHashAlg(name) {
|
|
783
|
-
return this.codecs[name].hashAlg
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
fromCodec(codec) {
|
|
787
|
-
this.name = this.getCodecName(codec);
|
|
788
|
-
this.hashAlg = this.getHashAlg(this.name);
|
|
789
|
-
|
|
790
|
-
this.codec = this.getCodec(this.name);
|
|
791
|
-
this.codecBuffer = varint.encode(codec);
|
|
792
|
-
}
|
|
793
|
-
|
|
794
|
-
fromName(name) {
|
|
795
|
-
const codec = this.getCodec(name);
|
|
796
|
-
this.name = name;
|
|
797
|
-
this.codec = codec;
|
|
798
|
-
this.hashAlg = this.getHashAlg(name);
|
|
799
|
-
this.codecBuffer = varint.encode(codec);
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
toBs32() {
|
|
803
|
-
this.encode();
|
|
804
|
-
return bs32.encode(this.encoded)
|
|
805
|
-
}
|
|
806
|
-
|
|
807
|
-
toBs58() {
|
|
808
|
-
this.encode();
|
|
809
|
-
return bs58.encode(this.encoded)
|
|
810
|
-
}
|
|
811
|
-
|
|
812
|
-
toHex() {
|
|
813
|
-
return this.encoded.toString('hex')
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
decode() {
|
|
817
|
-
const codec = varint.decode(this.encoded);
|
|
818
|
-
this.fromCodec(codec);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
encode() {
|
|
822
|
-
const codec = varint.encode(this.decoded);
|
|
823
|
-
this.encoded = codec;
|
|
824
|
-
return this.encoded
|
|
825
|
-
}
|
|
709
|
+
class PeernetCodec {
|
|
710
|
+
get codecs() {
|
|
711
|
+
return {...globalThis.peernet.codecs, ...codecs}
|
|
712
|
+
}
|
|
713
|
+
constructor(buffer) {
|
|
714
|
+
if (buffer) {
|
|
715
|
+
if (buffer instanceof Uint8Array) {
|
|
716
|
+
const codec = varint.decode(buffer);
|
|
717
|
+
const name = this.getCodecName(codec);
|
|
718
|
+
if (name) {
|
|
719
|
+
this.name = name;
|
|
720
|
+
this.encoded = buffer;
|
|
721
|
+
this.decode(buffer);
|
|
722
|
+
} else {
|
|
723
|
+
this.encode(buffer);
|
|
724
|
+
}
|
|
725
|
+
} else if (buffer instanceof ArrayBuffer) {
|
|
726
|
+
const encoded = new Uint8Array(buffer.byteLength);
|
|
727
|
+
|
|
728
|
+
for (let i = 0; i < buffer.byteLength; i++) {
|
|
729
|
+
encoded[i] = buffer[i];
|
|
730
|
+
}
|
|
731
|
+
this.encoded = encoded;
|
|
732
|
+
// this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength)
|
|
733
|
+
this.decode(buffer);
|
|
734
|
+
return
|
|
735
|
+
}
|
|
736
|
+
if (typeof buffer === 'string') {
|
|
737
|
+
if (this.codecs[buffer]) this.fromName(buffer);
|
|
738
|
+
else if (isHex(buffer)) this.fromHex(buffer);
|
|
739
|
+
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
740
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
741
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
742
|
+
}
|
|
743
|
+
if (!isNaN(buffer)) if (this.codecs[this.getCodecName(buffer)]) this.fromCodec(buffer);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
|
|
747
|
+
fromEncoded(encoded) {
|
|
748
|
+
const codec = varint.decode(encoded);
|
|
749
|
+
const name = this.getCodecName(codec);
|
|
750
|
+
this.name = name;
|
|
751
|
+
this.encoded = encoded;
|
|
752
|
+
this.decode(encoded);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
fromHex(hex) {
|
|
756
|
+
this.encoded = Buffer.from(hex, 'hex');
|
|
757
|
+
this.decode();
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
fromBs32(input) {
|
|
761
|
+
this.encoded = bs32.decode(input);
|
|
762
|
+
this.decode();
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
fromBs58(input) {
|
|
766
|
+
this.encoded = bs58.decode(input);
|
|
767
|
+
this.decode();
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
getCodec(name) {
|
|
771
|
+
return this.codecs[name].codec
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
getCodecName(codec) {
|
|
775
|
+
return Object.keys(this.codecs).reduce((p, c) => {
|
|
776
|
+
const item = this.codecs[c];
|
|
777
|
+
if (item.codec === codec) return c;
|
|
778
|
+
else return p;
|
|
779
|
+
}, undefined)
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
getHashAlg(name) {
|
|
783
|
+
return this.codecs[name].hashAlg
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
fromCodec(codec) {
|
|
787
|
+
this.name = this.getCodecName(codec);
|
|
788
|
+
this.hashAlg = this.getHashAlg(this.name);
|
|
789
|
+
|
|
790
|
+
this.codec = this.getCodec(this.name);
|
|
791
|
+
this.codecBuffer = varint.encode(codec);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
fromName(name) {
|
|
795
|
+
const codec = this.getCodec(name);
|
|
796
|
+
this.name = name;
|
|
797
|
+
this.codec = codec;
|
|
798
|
+
this.hashAlg = this.getHashAlg(name);
|
|
799
|
+
this.codecBuffer = varint.encode(codec);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
toBs32() {
|
|
803
|
+
this.encode();
|
|
804
|
+
return bs32.encode(this.encoded)
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
toBs58() {
|
|
808
|
+
this.encode();
|
|
809
|
+
return bs58.encode(this.encoded)
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
toHex() {
|
|
813
|
+
return this.encoded.toString('hex')
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
decode() {
|
|
817
|
+
const codec = varint.decode(this.encoded);
|
|
818
|
+
this.fromCodec(codec);
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
encode() {
|
|
822
|
+
const codec = varint.encode(this.decoded);
|
|
823
|
+
this.encoded = codec;
|
|
824
|
+
return this.encoded
|
|
825
|
+
}
|
|
826
826
|
}
|
|
827
827
|
|
|
828
|
-
class PeernetHash {
|
|
829
|
-
constructor(buffer, options = {}) {
|
|
830
|
-
if (options.name) this.name = options.name;
|
|
831
|
-
else this.name = 'disco-hash';
|
|
832
|
-
if (options.codecs) this.codecs = options.codecs;
|
|
833
|
-
if (buffer) {
|
|
834
|
-
if (Buffer.isBuffer(buffer)) {
|
|
835
|
-
this.discoCodec = new PeernetCodec(buffer, this.codecs);
|
|
836
|
-
const name = this.discoCodec.name;
|
|
837
|
-
|
|
838
|
-
if (name) {
|
|
839
|
-
this.name = name;
|
|
840
|
-
this.decode(buffer);
|
|
841
|
-
} else {
|
|
842
|
-
this.encode(buffer);
|
|
843
|
-
}
|
|
844
|
-
}
|
|
845
|
-
|
|
846
|
-
if (typeof buffer === 'string') {
|
|
847
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
848
|
-
if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
849
|
-
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
850
|
-
else throw new Error(`unsupported string ${buffer}`)
|
|
851
|
-
} else if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
|
|
855
|
-
get prefix() {
|
|
856
|
-
const length = this.length;
|
|
857
|
-
const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length);
|
|
858
|
-
for (let i = 0; i < this.discoCodec.codecBuffer.length; i++) {
|
|
859
|
-
uint8Array[i] = this.discoCodec.codecBuffer[i];
|
|
860
|
-
}
|
|
861
|
-
|
|
862
|
-
for (let i = uint8Array.length - 1; i < length.length; i++) {
|
|
863
|
-
uint8Array[i] = length[i];
|
|
864
|
-
}
|
|
865
|
-
return uint8Array
|
|
866
|
-
}
|
|
867
|
-
|
|
868
|
-
get length() {
|
|
869
|
-
return varint.encode(this.size)
|
|
870
|
-
}
|
|
871
|
-
|
|
872
|
-
get buffer() {
|
|
873
|
-
return this.hash
|
|
874
|
-
}
|
|
875
|
-
|
|
876
|
-
toHex() {
|
|
877
|
-
return this.hash.toString('hex')
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
fromHex(hex) {
|
|
881
|
-
return this.decode(Buffer.from(hex, 'hex'))
|
|
882
|
-
}
|
|
883
|
-
|
|
884
|
-
fromJSON(json) {
|
|
885
|
-
return this.encode(Buffer.from(JSON.stringify(json)))
|
|
886
|
-
}
|
|
887
|
-
|
|
888
|
-
toBs32() {
|
|
889
|
-
return bs32.encode(this.hash)
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
fromBs32(bs) {
|
|
893
|
-
return this.decode(bs32.decode(bs))
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
toBs58() {
|
|
897
|
-
return bs58.encode(this.hash)
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
fromBs58(bs) {
|
|
901
|
-
return this.decode(bs58.decode(bs))
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
toString(encoding = 'utf8') {
|
|
905
|
-
return this.hash.toString(encoding)
|
|
906
|
-
}
|
|
907
|
-
|
|
908
|
-
encode(buffer, name) {
|
|
909
|
-
if (!this.name && name) this.name = name;
|
|
910
|
-
if (!buffer) buffer = this.buffer;
|
|
911
|
-
this.discoCodec = new PeernetCodec(this.name, this.codecs);
|
|
912
|
-
this.discoCodec.fromName(this.name);
|
|
913
|
-
let hashAlg = this.discoCodec.hashAlg;
|
|
914
|
-
if (hashAlg.includes('dbl')) {
|
|
915
|
-
hashAlg = hashAlg.replace('dbl-', '');
|
|
916
|
-
buffer = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
917
|
-
}
|
|
918
|
-
this.digest = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
919
|
-
this.size = this.digest.length;
|
|
920
|
-
|
|
921
|
-
this.codec = this.discoCodec.encode();
|
|
922
|
-
this.codec = this.discoCodec.codecBuffer;
|
|
923
|
-
this.hash = Buffer.concat([
|
|
924
|
-
this.prefix,
|
|
925
|
-
this.digest,
|
|
926
|
-
]);
|
|
927
|
-
|
|
928
|
-
return this.hash
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
validate(buffer) {
|
|
932
|
-
if (Buffer.isBuffer(buffer)) {
|
|
933
|
-
const codec = varint.decode(buffer);
|
|
934
|
-
if (this.codecs[codec]) {
|
|
935
|
-
this.decode(buffer);
|
|
936
|
-
} else {
|
|
937
|
-
this.encode(buffer);
|
|
938
|
-
}
|
|
939
|
-
}
|
|
940
|
-
if (typeof buffer === 'string') {
|
|
941
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
942
|
-
if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
943
|
-
}
|
|
944
|
-
if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
decode(buffer) {
|
|
948
|
-
this.hash = buffer;
|
|
949
|
-
const codec = varint.decode(buffer);
|
|
950
|
-
|
|
951
|
-
this.discoCodec = new PeernetCodec(codec, this.codecs);
|
|
952
|
-
// TODO: validate codec
|
|
953
|
-
buffer = buffer.slice(varint.decode.bytes);
|
|
954
|
-
this.size = varint.decode(buffer);
|
|
955
|
-
this.digest = buffer.slice(varint.decode.bytes);
|
|
956
|
-
if (this.digest.length !== this.size) {
|
|
957
|
-
throw new Error(`hash length inconsistent: 0x${this.hash.toString('hex')}`)
|
|
958
|
-
}
|
|
959
|
-
|
|
960
|
-
// const discoCodec = new Codec(codec, this.codecs)
|
|
961
|
-
|
|
962
|
-
this.name = this.discoCodec.name;
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
this.size = this.digest.length;
|
|
966
|
-
|
|
967
|
-
return {
|
|
968
|
-
codec: this.codec,
|
|
969
|
-
name: this.name,
|
|
970
|
-
size: this.size,
|
|
971
|
-
length: this.length,
|
|
972
|
-
digest: this.digest,
|
|
973
|
-
}
|
|
974
|
-
}
|
|
828
|
+
class PeernetHash {
|
|
829
|
+
constructor(buffer, options = {}) {
|
|
830
|
+
if (options.name) this.name = options.name;
|
|
831
|
+
else this.name = 'disco-hash';
|
|
832
|
+
if (options.codecs) this.codecs = options.codecs;
|
|
833
|
+
if (buffer) {
|
|
834
|
+
if (Buffer.isBuffer(buffer)) {
|
|
835
|
+
this.discoCodec = new PeernetCodec(buffer, this.codecs);
|
|
836
|
+
const name = this.discoCodec.name;
|
|
837
|
+
|
|
838
|
+
if (name) {
|
|
839
|
+
this.name = name;
|
|
840
|
+
this.decode(buffer);
|
|
841
|
+
} else {
|
|
842
|
+
this.encode(buffer);
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
if (typeof buffer === 'string') {
|
|
847
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
848
|
+
if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
849
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
850
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
851
|
+
} else if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
852
|
+
}
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
get prefix() {
|
|
856
|
+
const length = this.length;
|
|
857
|
+
const uint8Array = new Uint8Array(length.length + this.discoCodec.codecBuffer.length);
|
|
858
|
+
for (let i = 0; i < this.discoCodec.codecBuffer.length; i++) {
|
|
859
|
+
uint8Array[i] = this.discoCodec.codecBuffer[i];
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
for (let i = uint8Array.length - 1; i < length.length; i++) {
|
|
863
|
+
uint8Array[i] = length[i];
|
|
864
|
+
}
|
|
865
|
+
return uint8Array
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
get length() {
|
|
869
|
+
return varint.encode(this.size)
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
get buffer() {
|
|
873
|
+
return this.hash
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
toHex() {
|
|
877
|
+
return this.hash.toString('hex')
|
|
878
|
+
}
|
|
879
|
+
|
|
880
|
+
fromHex(hex) {
|
|
881
|
+
return this.decode(Buffer.from(hex, 'hex'))
|
|
882
|
+
}
|
|
883
|
+
|
|
884
|
+
fromJSON(json) {
|
|
885
|
+
return this.encode(Buffer.from(JSON.stringify(json)))
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
toBs32() {
|
|
889
|
+
return bs32.encode(this.hash)
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
fromBs32(bs) {
|
|
893
|
+
return this.decode(bs32.decode(bs))
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
toBs58() {
|
|
897
|
+
return bs58.encode(this.hash)
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
fromBs58(bs) {
|
|
901
|
+
return this.decode(bs58.decode(bs))
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
toString(encoding = 'utf8') {
|
|
905
|
+
return this.hash.toString(encoding)
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
encode(buffer, name) {
|
|
909
|
+
if (!this.name && name) this.name = name;
|
|
910
|
+
if (!buffer) buffer = this.buffer;
|
|
911
|
+
this.discoCodec = new PeernetCodec(this.name, this.codecs);
|
|
912
|
+
this.discoCodec.fromName(this.name);
|
|
913
|
+
let hashAlg = this.discoCodec.hashAlg;
|
|
914
|
+
if (hashAlg.includes('dbl')) {
|
|
915
|
+
hashAlg = hashAlg.replace('dbl-', '');
|
|
916
|
+
buffer = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
917
|
+
}
|
|
918
|
+
this.digest = createKeccakHash(hashAlg.replace('-', '')).update(buffer).digest();
|
|
919
|
+
this.size = this.digest.length;
|
|
920
|
+
|
|
921
|
+
this.codec = this.discoCodec.encode();
|
|
922
|
+
this.codec = this.discoCodec.codecBuffer;
|
|
923
|
+
this.hash = Buffer.concat([
|
|
924
|
+
this.prefix,
|
|
925
|
+
this.digest,
|
|
926
|
+
]);
|
|
927
|
+
|
|
928
|
+
return this.hash
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
validate(buffer) {
|
|
932
|
+
if (Buffer.isBuffer(buffer)) {
|
|
933
|
+
const codec = varint.decode(buffer);
|
|
934
|
+
if (this.codecs[codec]) {
|
|
935
|
+
this.decode(buffer);
|
|
936
|
+
} else {
|
|
937
|
+
this.encode(buffer);
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
if (typeof buffer === 'string') {
|
|
941
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
942
|
+
if (bs32.test(buffer)) this.fromBs32(buffer);
|
|
943
|
+
}
|
|
944
|
+
if (typeof buffer === 'object') this.fromJSON(buffer);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
decode(buffer) {
|
|
948
|
+
this.hash = buffer;
|
|
949
|
+
const codec = varint.decode(buffer);
|
|
950
|
+
|
|
951
|
+
this.discoCodec = new PeernetCodec(codec, this.codecs);
|
|
952
|
+
// TODO: validate codec
|
|
953
|
+
buffer = buffer.slice(varint.decode.bytes);
|
|
954
|
+
this.size = varint.decode(buffer);
|
|
955
|
+
this.digest = buffer.slice(varint.decode.bytes);
|
|
956
|
+
if (this.digest.length !== this.size) {
|
|
957
|
+
throw new Error(`hash length inconsistent: 0x${this.hash.toString('hex')}`)
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
// const discoCodec = new Codec(codec, this.codecs)
|
|
961
|
+
|
|
962
|
+
this.name = this.discoCodec.name;
|
|
963
|
+
|
|
964
|
+
|
|
965
|
+
this.size = this.digest.length;
|
|
966
|
+
|
|
967
|
+
return {
|
|
968
|
+
codec: this.codec,
|
|
969
|
+
name: this.name,
|
|
970
|
+
size: this.size,
|
|
971
|
+
length: this.length,
|
|
972
|
+
digest: this.digest,
|
|
973
|
+
}
|
|
974
|
+
}
|
|
975
975
|
}
|
|
976
976
|
|
|
977
|
-
class FormatInterface {
|
|
978
|
-
/**
|
|
979
|
-
* @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
|
|
980
|
-
* @param {Object} proto - {encode, decode}
|
|
981
|
-
* @param {Object} options - {hashFormat, name}
|
|
982
|
-
*/
|
|
983
|
-
constructor(buffer, proto, options = {}) {
|
|
984
|
-
this.protoEncode = proto.encode;
|
|
985
|
-
this.protoDecode = proto.decode;
|
|
986
|
-
this.hashFormat = options.hashFormat || 'bs32';
|
|
987
|
-
if (options.name) this.name = options.name;
|
|
988
|
-
if (buffer instanceof Uint8Array) return this.fromUint8Array(buffer)
|
|
989
|
-
else if (buffer instanceof ArrayBuffer) return this.fromArrayBuffer(buffer)
|
|
990
|
-
else if (buffer.name === options.name) return buffer
|
|
991
|
-
else if (typeof buffer === 'string') {
|
|
992
|
-
if (isHex(buffer)) this.fromHex(buffer);
|
|
993
|
-
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
994
|
-
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
995
|
-
else throw new Error(`unsupported string ${buffer}`)
|
|
996
|
-
} else {
|
|
997
|
-
this.create(buffer);
|
|
998
|
-
}
|
|
999
|
-
}
|
|
1000
|
-
|
|
1001
|
-
/**
|
|
1002
|
-
* @return {PeernetHash}
|
|
1003
|
-
*/
|
|
1004
|
-
get peernetHash() {
|
|
1005
|
-
return new PeernetHash(this.decoded, {name: this.name})
|
|
1006
|
-
}
|
|
1007
|
-
|
|
1008
|
-
/**
|
|
1009
|
-
* @return {peernetHash}
|
|
1010
|
-
*/
|
|
1011
|
-
get hash() {
|
|
1012
|
-
const upper = this.hashFormat.charAt(0).toUpperCase();
|
|
1013
|
-
const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`;
|
|
1014
|
-
return this.peernetHash[`to${format}`]()
|
|
1015
|
-
}
|
|
1016
|
-
|
|
1017
|
-
/**
|
|
1018
|
-
* @return {Object}
|
|
1019
|
-
*/
|
|
1020
|
-
decode() {
|
|
1021
|
-
let encoded = this.encoded;
|
|
1022
|
-
const discoCodec = new PeernetCodec(this.encoded);
|
|
1023
|
-
encoded = encoded.slice(discoCodec.codecBuffer.length);
|
|
1024
|
-
this.name = discoCodec.name;
|
|
1025
|
-
this.decoded = this.protoDecode(encoded);
|
|
1026
|
-
return this.decoded
|
|
1027
|
-
}
|
|
1028
|
-
|
|
1029
|
-
/**
|
|
1030
|
-
* @return {Buffer}
|
|
1031
|
-
*/
|
|
1032
|
-
encode(decoded) {
|
|
1033
|
-
if (!decoded) decoded = this.decoded;
|
|
1034
|
-
const codec = new PeernetCodec(this.name);
|
|
1035
|
-
const encoded = this.protoEncode(decoded);
|
|
1036
|
-
const uint8Array = new Uint8Array(encoded.length + codec.codecBuffer.length);
|
|
1037
|
-
uint8Array.set(codec.codecBuffer);
|
|
1038
|
-
uint8Array.set(encoded, codec.codecBuffer.length);
|
|
1039
|
-
this.encoded = uint8Array;
|
|
1040
|
-
return this.encoded
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
hasCodec() {
|
|
1044
|
-
if (!this.encoded) return false
|
|
1045
|
-
const codec = new PeernetCodec(this.encoded);
|
|
1046
|
-
if (codec.name) return true
|
|
1047
|
-
}
|
|
1048
|
-
|
|
1049
|
-
fromUint8Array(buffer) {
|
|
1050
|
-
this.encoded = buffer;
|
|
1051
|
-
if (!this.hasCodec()) this.create(
|
|
1052
|
-
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1053
|
-
);
|
|
1054
|
-
else this.decode();
|
|
1055
|
-
}
|
|
1056
|
-
|
|
1057
|
-
fromArrayBuffer(buffer) {
|
|
1058
|
-
this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength);
|
|
1059
|
-
if (!this.hasCodec()) this.create(
|
|
1060
|
-
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1061
|
-
);
|
|
1062
|
-
else this.decode();
|
|
1063
|
-
}
|
|
1064
|
-
|
|
1065
|
-
/**
|
|
1066
|
-
* @param {Buffer} encoded
|
|
1067
|
-
*/
|
|
1068
|
-
fromEncoded(encoded) {
|
|
1069
|
-
this.encoded = encoded;
|
|
1070
|
-
this.decode();
|
|
1071
|
-
}
|
|
1072
|
-
|
|
1073
|
-
/**
|
|
1074
|
-
* @param {String} encoded
|
|
1075
|
-
*/
|
|
1076
|
-
fromHex(encoded) {
|
|
1077
|
-
this.encoded = Buffer.from(encoded, 'hex');
|
|
1078
|
-
this.decode();
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
/**
|
|
1082
|
-
* @param {String} encoded
|
|
1083
|
-
*/
|
|
1084
|
-
fromBs32(encoded) {
|
|
1085
|
-
this.encoded = bs32.decode(encoded);
|
|
1086
|
-
this.decode();
|
|
1087
|
-
}
|
|
1088
|
-
|
|
1089
|
-
/**
|
|
1090
|
-
* @param {String} encoded
|
|
1091
|
-
*/
|
|
1092
|
-
fromBs58(encoded) {
|
|
1093
|
-
this.encoded = bs58.decode(encoded);
|
|
1094
|
-
this.decode();
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
|
-
/**
|
|
1098
|
-
* @return {String} encoded
|
|
1099
|
-
*/
|
|
1100
|
-
toHex() {
|
|
1101
|
-
if (!this.encoded) this.encode();
|
|
1102
|
-
return this.encoded.toString('hex')
|
|
1103
|
-
}
|
|
1104
|
-
|
|
1105
|
-
/**
|
|
1106
|
-
* @return {String} encoded
|
|
1107
|
-
*/
|
|
1108
|
-
toBs32() {
|
|
1109
|
-
if (!this.encoded) this.encode();
|
|
1110
|
-
return bs32.encode(this.encoded)
|
|
1111
|
-
}
|
|
1112
|
-
|
|
1113
|
-
/**
|
|
1114
|
-
* @return {String} encoded
|
|
1115
|
-
*/
|
|
1116
|
-
toBs58() {
|
|
1117
|
-
if (!this.encoded) this.encode();
|
|
1118
|
-
return bs58.encode(this.encoded)
|
|
1119
|
-
}
|
|
1120
|
-
|
|
1121
|
-
/**
|
|
1122
|
-
* @param {Object} data
|
|
1123
|
-
*/
|
|
1124
|
-
create(data) {
|
|
1125
|
-
const decoded = {};
|
|
1126
|
-
if (this.keys?.length > 0) {
|
|
1127
|
-
for (const key of this.keys) {
|
|
1128
|
-
Object.defineProperties(decoded, {
|
|
1129
|
-
[key]: {
|
|
1130
|
-
enumerable: true,
|
|
1131
|
-
configurable: true,
|
|
1132
|
-
set: (val) => value = data[key],
|
|
1133
|
-
get: () => data[key]
|
|
1134
|
-
}
|
|
1135
|
-
});
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
this.decoded = decoded;
|
|
1139
|
-
this.encode();
|
|
1140
|
-
}
|
|
1141
|
-
}
|
|
977
|
+
class FormatInterface {
|
|
978
|
+
/**
|
|
979
|
+
* @param {Buffer|String|Object} buffer - data - The data needed to create the desired message
|
|
980
|
+
* @param {Object} proto - {encode, decode}
|
|
981
|
+
* @param {Object} options - {hashFormat, name}
|
|
982
|
+
*/
|
|
983
|
+
constructor(buffer, proto, options = {}) {
|
|
984
|
+
this.protoEncode = proto.encode;
|
|
985
|
+
this.protoDecode = proto.decode;
|
|
986
|
+
this.hashFormat = options.hashFormat || 'bs32';
|
|
987
|
+
if (options.name) this.name = options.name;
|
|
988
|
+
if (buffer instanceof Uint8Array) return this.fromUint8Array(buffer)
|
|
989
|
+
else if (buffer instanceof ArrayBuffer) return this.fromArrayBuffer(buffer)
|
|
990
|
+
else if (buffer.name === options.name) return buffer
|
|
991
|
+
else if (typeof buffer === 'string') {
|
|
992
|
+
if (isHex(buffer)) this.fromHex(buffer);
|
|
993
|
+
else if (bs32.isBase32(buffer)) this.fromBs32(buffer);
|
|
994
|
+
else if (bs58.isBase58(buffer)) this.fromBs58(buffer);
|
|
995
|
+
else throw new Error(`unsupported string ${buffer}`)
|
|
996
|
+
} else {
|
|
997
|
+
this.create(buffer);
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
1001
|
+
/**
|
|
1002
|
+
* @return {PeernetHash}
|
|
1003
|
+
*/
|
|
1004
|
+
get peernetHash() {
|
|
1005
|
+
return new PeernetHash(this.decoded, {name: this.name})
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
/**
|
|
1009
|
+
* @return {peernetHash}
|
|
1010
|
+
*/
|
|
1011
|
+
get hash() {
|
|
1012
|
+
const upper = this.hashFormat.charAt(0).toUpperCase();
|
|
1013
|
+
const format = `${upper}${this.hashFormat.substring(1, this.hashFormat.length)}`;
|
|
1014
|
+
return this.peernetHash[`to${format}`]()
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
/**
|
|
1018
|
+
* @return {Object}
|
|
1019
|
+
*/
|
|
1020
|
+
decode() {
|
|
1021
|
+
let encoded = this.encoded;
|
|
1022
|
+
const discoCodec = new PeernetCodec(this.encoded);
|
|
1023
|
+
encoded = encoded.slice(discoCodec.codecBuffer.length);
|
|
1024
|
+
this.name = discoCodec.name;
|
|
1025
|
+
this.decoded = this.protoDecode(encoded);
|
|
1026
|
+
return this.decoded
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* @return {Buffer}
|
|
1031
|
+
*/
|
|
1032
|
+
encode(decoded) {
|
|
1033
|
+
if (!decoded) decoded = this.decoded;
|
|
1034
|
+
const codec = new PeernetCodec(this.name);
|
|
1035
|
+
const encoded = this.protoEncode(decoded);
|
|
1036
|
+
const uint8Array = new Uint8Array(encoded.length + codec.codecBuffer.length);
|
|
1037
|
+
uint8Array.set(codec.codecBuffer);
|
|
1038
|
+
uint8Array.set(encoded, codec.codecBuffer.length);
|
|
1039
|
+
this.encoded = uint8Array;
|
|
1040
|
+
return this.encoded
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
hasCodec() {
|
|
1044
|
+
if (!this.encoded) return false
|
|
1045
|
+
const codec = new PeernetCodec(this.encoded);
|
|
1046
|
+
if (codec.name) return true
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
fromUint8Array(buffer) {
|
|
1050
|
+
this.encoded = buffer;
|
|
1051
|
+
if (!this.hasCodec()) this.create(
|
|
1052
|
+
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1053
|
+
);
|
|
1054
|
+
else this.decode();
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
fromArrayBuffer(buffer) {
|
|
1058
|
+
this.encoded = new Uint8Array(buffer, buffer.byteOffset, buffer.byteLength);
|
|
1059
|
+
if (!this.hasCodec()) this.create(
|
|
1060
|
+
JSON.parse(new TextDecoder().decode(this.encoded))
|
|
1061
|
+
);
|
|
1062
|
+
else this.decode();
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
/**
|
|
1066
|
+
* @param {Buffer} encoded
|
|
1067
|
+
*/
|
|
1068
|
+
fromEncoded(encoded) {
|
|
1069
|
+
this.encoded = encoded;
|
|
1070
|
+
this.decode();
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
/**
|
|
1074
|
+
* @param {String} encoded
|
|
1075
|
+
*/
|
|
1076
|
+
fromHex(encoded) {
|
|
1077
|
+
this.encoded = Buffer.from(encoded, 'hex');
|
|
1078
|
+
this.decode();
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
/**
|
|
1082
|
+
* @param {String} encoded
|
|
1083
|
+
*/
|
|
1084
|
+
fromBs32(encoded) {
|
|
1085
|
+
this.encoded = bs32.decode(encoded);
|
|
1086
|
+
this.decode();
|
|
1087
|
+
}
|
|
1088
|
+
|
|
1089
|
+
/**
|
|
1090
|
+
* @param {String} encoded
|
|
1091
|
+
*/
|
|
1092
|
+
fromBs58(encoded) {
|
|
1093
|
+
this.encoded = bs58.decode(encoded);
|
|
1094
|
+
this.decode();
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
/**
|
|
1098
|
+
* @return {String} encoded
|
|
1099
|
+
*/
|
|
1100
|
+
toHex() {
|
|
1101
|
+
if (!this.encoded) this.encode();
|
|
1102
|
+
return this.encoded.toString('hex')
|
|
1103
|
+
}
|
|
1104
|
+
|
|
1105
|
+
/**
|
|
1106
|
+
* @return {String} encoded
|
|
1107
|
+
*/
|
|
1108
|
+
toBs32() {
|
|
1109
|
+
if (!this.encoded) this.encode();
|
|
1110
|
+
return bs32.encode(this.encoded)
|
|
1111
|
+
}
|
|
1112
|
+
|
|
1113
|
+
/**
|
|
1114
|
+
* @return {String} encoded
|
|
1115
|
+
*/
|
|
1116
|
+
toBs58() {
|
|
1117
|
+
if (!this.encoded) this.encode();
|
|
1118
|
+
return bs58.encode(this.encoded)
|
|
1119
|
+
}
|
|
1120
|
+
|
|
1121
|
+
/**
|
|
1122
|
+
* @param {Object} data
|
|
1123
|
+
*/
|
|
1124
|
+
create(data) {
|
|
1125
|
+
const decoded = {};
|
|
1126
|
+
if (this.keys?.length > 0) {
|
|
1127
|
+
for (const key of this.keys) {
|
|
1128
|
+
Object.defineProperties(decoded, {
|
|
1129
|
+
[key]: {
|
|
1130
|
+
enumerable: true,
|
|
1131
|
+
configurable: true,
|
|
1132
|
+
set: (val) => value = data[key],
|
|
1133
|
+
get: () => data[key]
|
|
1134
|
+
}
|
|
1135
|
+
});
|
|
1136
|
+
}
|
|
1137
|
+
|
|
1138
|
+
this.decoded = decoded;
|
|
1139
|
+
this.encode();
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
1142
|
}
|
|
1143
1143
|
|
|
1144
1144
|
class PeernetMessage extends FormatInterface {
|
|
@@ -1190,15 +1190,15 @@ message PeernetDHTMessageResponse {
|
|
|
1190
1190
|
}
|
|
1191
1191
|
`;
|
|
1192
1192
|
|
|
1193
|
-
class DHTMessageResponse extends FormatInterface {
|
|
1194
|
-
get keys() {
|
|
1195
|
-
return ['hash', 'has']
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
constructor(data) {
|
|
1199
|
-
const name = 'peernet-dht-response';
|
|
1200
|
-
super(data, protons(proto$8).PeernetDHTMessageResponse, {name});
|
|
1201
|
-
}
|
|
1193
|
+
class DHTMessageResponse extends FormatInterface {
|
|
1194
|
+
get keys() {
|
|
1195
|
+
return ['hash', 'has']
|
|
1196
|
+
}
|
|
1197
|
+
|
|
1198
|
+
constructor(data) {
|
|
1199
|
+
const name = 'peernet-dht-response';
|
|
1200
|
+
super(data, protons(proto$8).PeernetDHTMessageResponse, {name});
|
|
1201
|
+
}
|
|
1202
1202
|
}
|
|
1203
1203
|
|
|
1204
1204
|
var proto$7 = `
|
|
@@ -1333,12 +1333,12 @@ class DataMessageResponse extends FormatInterface {
|
|
|
1333
1333
|
}
|
|
1334
1334
|
}
|
|
1335
1335
|
|
|
1336
|
-
var proto = `
|
|
1337
|
-
message ChatMessage {
|
|
1338
|
-
required string value = 1;
|
|
1339
|
-
required string author = 2;
|
|
1340
|
-
required uint64 timestamp = 3;
|
|
1341
|
-
repeated string files = 4;
|
|
1336
|
+
var proto = `
|
|
1337
|
+
message ChatMessage {
|
|
1338
|
+
required string value = 1;
|
|
1339
|
+
required string author = 2;
|
|
1340
|
+
required uint64 timestamp = 3;
|
|
1341
|
+
repeated string files = 4;
|
|
1342
1342
|
}`;
|
|
1343
1343
|
|
|
1344
1344
|
class ChatMessage extends FormatInterface {
|
|
@@ -1352,259 +1352,259 @@ class ChatMessage extends FormatInterface {
|
|
|
1352
1352
|
}
|
|
1353
1353
|
}
|
|
1354
1354
|
|
|
1355
|
-
const debug = (log) => {
|
|
1356
|
-
if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
|
|
1357
|
-
};
|
|
1358
|
-
|
|
1359
|
-
const protoFor = (data) => {
|
|
1360
|
-
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
1361
|
-
const codec = new PeernetCodec(data);
|
|
1362
|
-
if (!codec.name) throw new Error('proto not found')
|
|
1363
|
-
const Proto = globalThis.peernet.protos[codec.name];
|
|
1364
|
-
if (!Proto) throw (new Error(`No proto defined for ${codec.name}`))
|
|
1365
|
-
return new Proto(data)
|
|
1366
|
-
};
|
|
1367
|
-
|
|
1368
|
-
/**
|
|
1369
|
-
* wether or not a peernet daemon is active
|
|
1370
|
-
* @return {Boolean}
|
|
1371
|
-
*/
|
|
1372
|
-
const hasDaemon = async () => {
|
|
1373
|
-
try {
|
|
1374
|
-
let response = await fetch('http://127.0.0.1:1000/api/version');
|
|
1375
|
-
response = await response.json();
|
|
1376
|
-
return Boolean(response.client === '@peernet/api/http')
|
|
1377
|
-
} catch (e) {
|
|
1378
|
-
return false
|
|
1379
|
-
}
|
|
1380
|
-
};
|
|
1381
|
-
|
|
1382
|
-
const https = () => {
|
|
1383
|
-
if (!globalThis.location) return false;
|
|
1384
|
-
return Boolean(globalThis.location.protocol === 'https:')
|
|
1385
|
-
};
|
|
1386
|
-
|
|
1387
|
-
/**
|
|
1388
|
-
* Get current environment
|
|
1389
|
-
* @return {String} current environment [node, electron, browser]
|
|
1390
|
-
*/
|
|
1391
|
-
const environment = () => {
|
|
1392
|
-
const _navigator = globalThis.navigator;
|
|
1393
|
-
if (!_navigator) {
|
|
1394
|
-
return 'node'
|
|
1395
|
-
} else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
1396
|
-
return 'electron'
|
|
1397
|
-
} else {
|
|
1398
|
-
return 'browser'
|
|
1399
|
-
}
|
|
1400
|
-
};
|
|
1401
|
-
|
|
1402
|
-
/**
|
|
1403
|
-
* * Get current environment
|
|
1404
|
-
* @return {Object} result
|
|
1405
|
-
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
1406
|
-
* @property {Boolean} reult.environment Current environment
|
|
1407
|
-
*/
|
|
1408
|
-
const target = async () => {
|
|
1409
|
-
let daemon = false;
|
|
1410
|
-
const env = await environment();
|
|
1411
|
-
if (!https()) daemon = await hasDaemon();
|
|
1412
|
-
|
|
1413
|
-
return {daemon, environment: env}
|
|
1414
|
-
};
|
|
1415
|
-
|
|
1416
|
-
class PeerDiscovery {
|
|
1417
|
-
constructor(id) {
|
|
1418
|
-
this.id = id;
|
|
1419
|
-
}
|
|
1420
|
-
|
|
1421
|
-
_getPeerId(id) {
|
|
1422
|
-
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
|
|
1423
|
-
|
|
1424
|
-
for (const entry of [...peernet.peerMap.entries()]) {
|
|
1425
|
-
for (const _id of entry[1]) {
|
|
1426
|
-
if (_id === id) return entry[0]
|
|
1427
|
-
}
|
|
1428
|
-
}
|
|
1429
|
-
}
|
|
1430
|
-
|
|
1431
|
-
async discover(peer) {
|
|
1432
|
-
let id = this._getPeerId(peer.id);
|
|
1433
|
-
if (id) return id
|
|
1434
|
-
const data = new peernet.protos['peernet-peer']({id: this.id});
|
|
1435
|
-
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
1436
|
-
|
|
1437
|
-
let response = await peer.request(node.encoded);
|
|
1438
|
-
response = protoFor(response);
|
|
1439
|
-
response = new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
1440
|
-
|
|
1441
|
-
id = response.decoded.id;
|
|
1442
|
-
if (id === this.id) return;
|
|
1443
|
-
|
|
1444
|
-
if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
|
|
1445
|
-
else {
|
|
1446
|
-
const connections = peernet.peerMap.get(id);
|
|
1447
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
1448
|
-
connections.push(peer.id);
|
|
1449
|
-
peernet.peerMap.set(peer.id, connections);
|
|
1450
|
-
}
|
|
1451
|
-
}
|
|
1452
|
-
return id
|
|
1453
|
-
}
|
|
1454
|
-
|
|
1455
|
-
async discoverHandler(message, peer) {
|
|
1456
|
-
const {id, proto} = message;
|
|
1457
|
-
// if (typeof message.data === 'string') message.data = Buffer.from(message.data)
|
|
1458
|
-
if (proto.name === 'peernet-peer') {
|
|
1459
|
-
const from = proto.decoded.id;
|
|
1460
|
-
if (from === this.id) return;
|
|
1461
|
-
|
|
1462
|
-
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1463
|
-
else {
|
|
1464
|
-
const connections = peernet.peerMap.get(from);
|
|
1465
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
1466
|
-
connections.push(peer.id);
|
|
1467
|
-
peernet.peerMap.set(from, connections);
|
|
1468
|
-
}
|
|
1469
|
-
}
|
|
1470
|
-
const data = new peernet.protos['peernet-peer-response']({id: this.id});
|
|
1471
|
-
const node = await peernet.prepareMessage(from, data.encoded);
|
|
1472
|
-
|
|
1473
|
-
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
1474
|
-
} else if (proto.name === 'peernet-peer-response') {
|
|
1475
|
-
const from = proto.decoded.id;
|
|
1476
|
-
if (from === this.id) return;
|
|
1477
|
-
|
|
1478
|
-
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1479
|
-
else {
|
|
1480
|
-
const connections = peernet.peerMap.get(from);
|
|
1481
|
-
if (connections.indexOf(peer.id) === -1) {
|
|
1482
|
-
connections.push(peer.id);
|
|
1483
|
-
peernet.peerMap.set(from, connections);
|
|
1484
|
-
}
|
|
1485
|
-
}
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
/**
|
|
1491
|
-
* Keep history of fetched address and ptr
|
|
1492
|
-
* @property {Object} address
|
|
1493
|
-
* @property {Object} ptr
|
|
1494
|
-
*/
|
|
1495
|
-
const lastFetched = {
|
|
1496
|
-
address: {
|
|
1497
|
-
value: undefined,
|
|
1498
|
-
timestamp: 0,
|
|
1499
|
-
},
|
|
1500
|
-
ptr: {
|
|
1501
|
-
value: undefined,
|
|
1502
|
-
timestamp: 0,
|
|
1503
|
-
},
|
|
1504
|
-
};
|
|
1505
|
-
|
|
1506
|
-
const getAddress = async () => {
|
|
1507
|
-
const {address} = lastFetched;
|
|
1508
|
-
const now = Math.round(new Date().getTime() / 1000);
|
|
1509
|
-
if (now - address.timestamp > 1200000) {
|
|
1510
|
-
address.value = await fetch('https://icanhazip.com/');
|
|
1511
|
-
address.value = await address.value.text();
|
|
1512
|
-
address.timestamp = Math.round(new Date().getTime() / 1000);
|
|
1513
|
-
lastFetched.address = address;
|
|
1514
|
-
}
|
|
1515
|
-
|
|
1516
|
-
return address.value
|
|
1517
|
-
};
|
|
1518
|
-
|
|
1519
|
-
const degreesToRadians = (degrees) => {
|
|
1520
|
-
return degrees * Math.PI / 180;
|
|
1521
|
-
};
|
|
1522
|
-
|
|
1523
|
-
const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
|
|
1524
|
-
const earthRadiusKm = 6371;
|
|
1525
|
-
|
|
1526
|
-
const dLat = degreesToRadians(lat2-lat1);
|
|
1527
|
-
const dLon = degreesToRadians(lon2-lon1);
|
|
1528
|
-
|
|
1529
|
-
lat1 = degreesToRadians(lat1);
|
|
1530
|
-
lat2 = degreesToRadians(lat2);
|
|
1531
|
-
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
1532
|
-
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
1533
|
-
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
1534
|
-
return earthRadiusKm * c;
|
|
1355
|
+
const debug = (log) => {
|
|
1356
|
+
if (globalThis.DEBUG || globalThis.debug) console.log(`%c ${log}`, 'color: #0080ff;');
|
|
1357
|
+
};
|
|
1358
|
+
|
|
1359
|
+
const protoFor = (data) => {
|
|
1360
|
+
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
1361
|
+
const codec = new PeernetCodec(data);
|
|
1362
|
+
if (!codec.name) throw new Error('proto not found')
|
|
1363
|
+
const Proto = globalThis.peernet.protos[codec.name];
|
|
1364
|
+
if (!Proto) throw (new Error(`No proto defined for ${codec.name}`))
|
|
1365
|
+
return new Proto(data)
|
|
1366
|
+
};
|
|
1367
|
+
|
|
1368
|
+
/**
|
|
1369
|
+
* wether or not a peernet daemon is active
|
|
1370
|
+
* @return {Boolean}
|
|
1371
|
+
*/
|
|
1372
|
+
const hasDaemon = async () => {
|
|
1373
|
+
try {
|
|
1374
|
+
let response = await fetch('http://127.0.0.1:1000/api/version');
|
|
1375
|
+
response = await response.json();
|
|
1376
|
+
return Boolean(response.client === '@peernet/api/http')
|
|
1377
|
+
} catch (e) {
|
|
1378
|
+
return false
|
|
1379
|
+
}
|
|
1380
|
+
};
|
|
1381
|
+
|
|
1382
|
+
const https = () => {
|
|
1383
|
+
if (!globalThis.location) return false;
|
|
1384
|
+
return Boolean(globalThis.location.protocol === 'https:')
|
|
1385
|
+
};
|
|
1386
|
+
|
|
1387
|
+
/**
|
|
1388
|
+
* Get current environment
|
|
1389
|
+
* @return {String} current environment [node, electron, browser]
|
|
1390
|
+
*/
|
|
1391
|
+
const environment = () => {
|
|
1392
|
+
const _navigator = globalThis.navigator;
|
|
1393
|
+
if (!_navigator) {
|
|
1394
|
+
return 'node'
|
|
1395
|
+
} else if (_navigator && /electron/i.test(_navigator.userAgent)) {
|
|
1396
|
+
return 'electron'
|
|
1397
|
+
} else {
|
|
1398
|
+
return 'browser'
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
1402
|
+
/**
|
|
1403
|
+
* * Get current environment
|
|
1404
|
+
* @return {Object} result
|
|
1405
|
+
* @property {Boolean} reult.daemon whether or not daemon is running
|
|
1406
|
+
* @property {Boolean} reult.environment Current environment
|
|
1407
|
+
*/
|
|
1408
|
+
const target = async () => {
|
|
1409
|
+
let daemon = false;
|
|
1410
|
+
const env = await environment();
|
|
1411
|
+
if (!https()) daemon = await hasDaemon();
|
|
1412
|
+
|
|
1413
|
+
return {daemon, environment: env}
|
|
1535
1414
|
};
|
|
1536
1415
|
|
|
1537
|
-
class
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1416
|
+
class PeerDiscovery {
|
|
1417
|
+
constructor(id) {
|
|
1418
|
+
this.id = id;
|
|
1419
|
+
}
|
|
1420
|
+
|
|
1421
|
+
_getPeerId(id) {
|
|
1422
|
+
if (!peernet.peerMap || peernet.peerMap && peernet.peerMap.size === 0) return false
|
|
1423
|
+
|
|
1424
|
+
for (const entry of [...peernet.peerMap.entries()]) {
|
|
1425
|
+
for (const _id of entry[1]) {
|
|
1426
|
+
if (_id === id) return entry[0]
|
|
1427
|
+
}
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
async discover(peer) {
|
|
1432
|
+
let id = this._getPeerId(peer.id);
|
|
1433
|
+
if (id) return id
|
|
1434
|
+
const data = new peernet.protos['peernet-peer']({id: this.id});
|
|
1435
|
+
const node = await peernet.prepareMessage(peer.id, data.encoded);
|
|
1436
|
+
|
|
1437
|
+
let response = await peer.request(node.encoded);
|
|
1438
|
+
response = protoFor(response);
|
|
1439
|
+
response = new peernet.protos['peernet-peer-response'](response.decoded.data);
|
|
1440
|
+
|
|
1441
|
+
id = response.decoded.id;
|
|
1442
|
+
if (id === this.id) return;
|
|
1443
|
+
|
|
1444
|
+
if (!peernet.peerMap.has(id)) peernet.peerMap.set(id, [peer.id]);
|
|
1445
|
+
else {
|
|
1446
|
+
const connections = peernet.peerMap.get(id);
|
|
1447
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1448
|
+
connections.push(peer.id);
|
|
1449
|
+
peernet.peerMap.set(peer.id, connections);
|
|
1450
|
+
}
|
|
1451
|
+
}
|
|
1452
|
+
return id
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
async discoverHandler(message, peer) {
|
|
1456
|
+
const {id, proto} = message;
|
|
1457
|
+
// if (typeof message.data === 'string') message.data = Buffer.from(message.data)
|
|
1458
|
+
if (proto.name === 'peernet-peer') {
|
|
1459
|
+
const from = proto.decoded.id;
|
|
1460
|
+
if (from === this.id) return;
|
|
1461
|
+
|
|
1462
|
+
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1463
|
+
else {
|
|
1464
|
+
const connections = peernet.peerMap.get(from);
|
|
1465
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1466
|
+
connections.push(peer.id);
|
|
1467
|
+
peernet.peerMap.set(from, connections);
|
|
1468
|
+
}
|
|
1469
|
+
}
|
|
1470
|
+
const data = new peernet.protos['peernet-peer-response']({id: this.id});
|
|
1471
|
+
const node = await peernet.prepareMessage(from, data.encoded);
|
|
1472
|
+
|
|
1473
|
+
peer.write(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
1474
|
+
} else if (proto.name === 'peernet-peer-response') {
|
|
1475
|
+
const from = proto.decoded.id;
|
|
1476
|
+
if (from === this.id) return;
|
|
1477
|
+
|
|
1478
|
+
if (!peernet.peerMap.has(from)) peernet.peerMap.set(from, [peer.id]);
|
|
1479
|
+
else {
|
|
1480
|
+
const connections = peernet.peerMap.get(from);
|
|
1481
|
+
if (connections.indexOf(peer.id) === -1) {
|
|
1482
|
+
connections.push(peer.id);
|
|
1483
|
+
peernet.peerMap.set(from, connections);
|
|
1484
|
+
}
|
|
1485
|
+
}
|
|
1486
|
+
}
|
|
1487
|
+
}
|
|
1488
|
+
}
|
|
1603
1489
|
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1490
|
+
/**
|
|
1491
|
+
* Keep history of fetched address and ptr
|
|
1492
|
+
* @property {Object} address
|
|
1493
|
+
* @property {Object} ptr
|
|
1494
|
+
*/
|
|
1495
|
+
const lastFetched = {
|
|
1496
|
+
address: {
|
|
1497
|
+
value: undefined,
|
|
1498
|
+
timestamp: 0,
|
|
1499
|
+
},
|
|
1500
|
+
ptr: {
|
|
1501
|
+
value: undefined,
|
|
1502
|
+
timestamp: 0,
|
|
1503
|
+
},
|
|
1504
|
+
};
|
|
1505
|
+
|
|
1506
|
+
const getAddress = async () => {
|
|
1507
|
+
const {address} = lastFetched;
|
|
1508
|
+
const now = Math.round(new Date().getTime() / 1000);
|
|
1509
|
+
if (now - address.timestamp > 1200000) {
|
|
1510
|
+
address.value = await fetch('https://icanhazip.com/');
|
|
1511
|
+
address.value = await address.value.text();
|
|
1512
|
+
address.timestamp = Math.round(new Date().getTime() / 1000);
|
|
1513
|
+
lastFetched.address = address;
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
return address.value
|
|
1517
|
+
};
|
|
1518
|
+
|
|
1519
|
+
const degreesToRadians = (degrees) => {
|
|
1520
|
+
return degrees * Math.PI / 180;
|
|
1521
|
+
};
|
|
1522
|
+
|
|
1523
|
+
const distanceInKmBetweenEarthCoordinates = (lat1, lon1, lat2, lon2) => {
|
|
1524
|
+
const earthRadiusKm = 6371;
|
|
1525
|
+
|
|
1526
|
+
const dLat = degreesToRadians(lat2-lat1);
|
|
1527
|
+
const dLon = degreesToRadians(lon2-lon1);
|
|
1528
|
+
|
|
1529
|
+
lat1 = degreesToRadians(lat1);
|
|
1530
|
+
lat2 = degreesToRadians(lat2);
|
|
1531
|
+
const a = Math.sin(dLat/2) * Math.sin(dLat/2) +
|
|
1532
|
+
Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) * Math.cos(lat2);
|
|
1533
|
+
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
|
|
1534
|
+
return earthRadiusKm * c;
|
|
1535
|
+
};
|
|
1536
|
+
|
|
1537
|
+
class DhtEarth {
|
|
1538
|
+
/**
|
|
1539
|
+
*
|
|
1540
|
+
*/
|
|
1541
|
+
constructor() {
|
|
1542
|
+
this.providerMap = new Map();
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/**
|
|
1546
|
+
* @param {Object} address
|
|
1547
|
+
* @return {Object} {latitude: lat, longitude: lon}
|
|
1548
|
+
*/
|
|
1549
|
+
async getCoordinates(address) {
|
|
1550
|
+
// const {address} = parseAddress(provider)
|
|
1551
|
+
const request = `https://whereis.leofcoin.org/?ip=${address}`;
|
|
1552
|
+
let response = await fetch(request);
|
|
1553
|
+
response = await response.json();
|
|
1554
|
+
const {lat, lon} = response;
|
|
1555
|
+
return {latitude: lat, longitude: lon}
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/**
|
|
1559
|
+
* @param {Object} peer
|
|
1560
|
+
* @param {Object} provider
|
|
1561
|
+
* @return {Object} {provider, distance}
|
|
1562
|
+
*/
|
|
1563
|
+
async getDistance(peer, provider) {
|
|
1564
|
+
const {latitude, longitude} = await this.getCoordinates(provider.address);
|
|
1565
|
+
return {provider, distance: distanceInKmBetweenEarthCoordinates(peer.latitude, peer.longitude, latitude, longitude)}
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
/**
|
|
1569
|
+
* @param {Array} providers
|
|
1570
|
+
* @return {Object} closestPeer
|
|
1571
|
+
*/
|
|
1572
|
+
async closestPeer(providers) {
|
|
1573
|
+
let all = [];
|
|
1574
|
+
const address = await getAddress();
|
|
1575
|
+
const peerLoc = await this.getCoordinates(address);
|
|
1576
|
+
|
|
1577
|
+
for (const provider of providers) {
|
|
1578
|
+
if (provider.address === '127.0.0.1') all.push({provider, distance: 0});
|
|
1579
|
+
else all.push(this.getDistance(peerLoc, provider));
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
all = await Promise.all(all);
|
|
1583
|
+
all = all.sort((previous, current) => previous.distance - current.distance);
|
|
1584
|
+
return all[0].provider;
|
|
1585
|
+
}
|
|
1586
|
+
|
|
1587
|
+
/**
|
|
1588
|
+
* @param {String} hash
|
|
1589
|
+
* @return {Array} providers
|
|
1590
|
+
*/
|
|
1591
|
+
providersFor(hash) {
|
|
1592
|
+
return this.providerMap.get(hash);
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
/**
|
|
1596
|
+
* @param {String} address
|
|
1597
|
+
* @param {String} hash
|
|
1598
|
+
* @return {Array} providers
|
|
1599
|
+
*/
|
|
1600
|
+
async addProvider(address, hash) {
|
|
1601
|
+
let providers = [];
|
|
1602
|
+
if (this.providerMap.has(hash)) providers = this.providerMap.get(hash);
|
|
1603
|
+
|
|
1604
|
+
providers = new Set([...providers, address]);
|
|
1605
|
+
this.providerMap.set(hash, providers);
|
|
1606
|
+
return providers;
|
|
1607
|
+
}
|
|
1608
1608
|
}
|
|
1609
1609
|
|
|
1610
1610
|
var testnets = {
|
|
@@ -1962,776 +1962,685 @@ class MultiWallet extends HDWallet {
|
|
|
1962
1962
|
return { version, multiCodec, bs58 };
|
|
1963
1963
|
}
|
|
1964
1964
|
|
|
1965
|
-
sign(hash) {
|
|
1966
|
-
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1967
|
-
.sign(hash, this.privateKeyBuffer);
|
|
1965
|
+
sign(hash) {
|
|
1966
|
+
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1967
|
+
.sign(hash, this.privateKeyBuffer);
|
|
1968
|
+
|
|
1969
|
+
}
|
|
1970
|
+
|
|
1971
|
+
verify(multiSignature, hash) {
|
|
1972
|
+
return new MultiSignature(this.version, this.network.multiCodec)
|
|
1973
|
+
.verify(multiSignature, hash, this.publicKeyBuffer)
|
|
1974
|
+
}
|
|
1975
|
+
|
|
1976
|
+
/**
|
|
1977
|
+
* @param {number} account - account to return chain for
|
|
1978
|
+
* @return { internal(addressIndex), external(addressIndex) }
|
|
1979
|
+
*/
|
|
1980
|
+
account(index) {
|
|
1981
|
+
return new HDAccount(new MultiWallet(this.network, this.hdnode), index);
|
|
1982
|
+
}
|
|
1983
|
+
|
|
1984
|
+
/**
|
|
1985
|
+
* m / purpose' / coin_type' / account' / change / aadress_index
|
|
1986
|
+
*
|
|
1987
|
+
* see https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki
|
|
1988
|
+
*/
|
|
1989
|
+
derivePath(path) {
|
|
1990
|
+
return new MultiWallet(this.network, this.hdnode.derivePath(path))
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
derive(index) {
|
|
1994
|
+
return new MultiWallet(this.network, this.hdnode.derive(index));
|
|
1995
|
+
}
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
class MessageHandler {
|
|
1999
|
+
constructor(network) {
|
|
2000
|
+
this.network = network;
|
|
2001
|
+
}
|
|
2002
|
+
/**
|
|
2003
|
+
* hash and sign message
|
|
2004
|
+
*
|
|
2005
|
+
* @param {object} message
|
|
2006
|
+
* @param {Buffer} message.from peer id
|
|
2007
|
+
* @param {Buffer} message.to peer id
|
|
2008
|
+
* @param {string} message.data Peernet message
|
|
2009
|
+
* (PeernetMessage excluded) encoded as a string
|
|
2010
|
+
* @return signature
|
|
2011
|
+
*/
|
|
2012
|
+
async hashAndSignMessage(message) {
|
|
2013
|
+
const hasher = new PeernetHash(message, {name: 'peernet-message'});
|
|
2014
|
+
const identity = await walletStore.get('identity');
|
|
2015
|
+
|
|
2016
|
+
const wallet = new MultiWallet(this.network);
|
|
2017
|
+
wallet.import(identity.multiWIF);
|
|
2018
|
+
return wallet.sign(hasher.hash.slice(0, 32))
|
|
2019
|
+
}
|
|
2020
|
+
|
|
2021
|
+
/**
|
|
2022
|
+
* @param {String} from - peer id
|
|
2023
|
+
* @param {String} to - peer id
|
|
2024
|
+
* @param {String|PeernetMessage} data - data encoded message string
|
|
2025
|
+
* or the messageNode itself
|
|
2026
|
+
*/
|
|
2027
|
+
async prepareMessage(from, to, data, id) {
|
|
2028
|
+
if (!Buffer.isBuffer(from)) from = new Buffer.from(from);
|
|
2029
|
+
if (!Buffer.isBuffer(to)) to = new Buffer.from(to);
|
|
2030
|
+
if (data.encoded) data = data.encoded;
|
|
2031
|
+
|
|
2032
|
+
const message = {
|
|
2033
|
+
from,
|
|
2034
|
+
to,
|
|
2035
|
+
data,
|
|
2036
|
+
};
|
|
2037
|
+
const signature = await this.hashAndSignMessage(message);
|
|
2038
|
+
const node = new PeernetMessage({
|
|
2039
|
+
...message,
|
|
2040
|
+
signature,
|
|
2041
|
+
});
|
|
2042
|
+
|
|
2043
|
+
return node
|
|
2044
|
+
}
|
|
2045
|
+
}
|
|
2046
|
+
|
|
2047
|
+
const encapsulatedError = () => {
|
|
2048
|
+
return new Error('Nodes/Data should be send encapsulated by peernet-message')
|
|
2049
|
+
};
|
|
2050
|
+
|
|
2051
|
+
const dhtError = (proto) => {
|
|
2052
|
+
const text = `Received proto ${proto.name} expected peernet-dht-response`;
|
|
2053
|
+
return new Error(`Routing error: ${text}`)
|
|
2054
|
+
};
|
|
2055
|
+
|
|
2056
|
+
const nothingFoundError = (hash) => {
|
|
2057
|
+
return new Error(`nothing found for ${hash}`)
|
|
2058
|
+
};
|
|
2059
|
+
|
|
2060
|
+
globalThis.leofcoin = globalThis.leofcoin || {};
|
|
2061
|
+
globalThis.globalSub = globalThis.globalSub || new PubSub({verbose: true});
|
|
2062
|
+
|
|
2063
|
+
/**
|
|
2064
|
+
* @access public
|
|
2065
|
+
* @example
|
|
2066
|
+
* const peernet = new Peernet();
|
|
2067
|
+
*/
|
|
2068
|
+
class Peernet {
|
|
2069
|
+
/**
|
|
2070
|
+
* @access public
|
|
2071
|
+
* @param {Object} options
|
|
2072
|
+
* @param {String} options.network - desired network
|
|
2073
|
+
* @param {String} options.root - path to root directory
|
|
2074
|
+
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
2075
|
+
*
|
|
2076
|
+
* @return {Promise} instance of Peernet
|
|
2077
|
+
*
|
|
2078
|
+
* @example
|
|
2079
|
+
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
2080
|
+
*/
|
|
2081
|
+
constructor(options = {}) {
|
|
2082
|
+
this._discovered = [];
|
|
2083
|
+
/**
|
|
2084
|
+
* @property {String} network - current network
|
|
2085
|
+
*/
|
|
2086
|
+
this.network = options.network || 'leofcoin';
|
|
2087
|
+
const parts = this.network.split(':');
|
|
2088
|
+
|
|
2089
|
+
if (!options.storePrefix) options.storePrefix = 'lfc';
|
|
2090
|
+
if (!options.port) options.port = 2000;
|
|
2091
|
+
if (!options.root) {
|
|
2092
|
+
if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
|
|
2093
|
+
else options.root = `.${this.network}/peernet`;
|
|
2094
|
+
}
|
|
2095
|
+
globalThis.peernet = this;
|
|
2096
|
+
this.bw = {
|
|
2097
|
+
up: 0,
|
|
2098
|
+
down: 0,
|
|
2099
|
+
};
|
|
2100
|
+
return this._init(options)
|
|
2101
|
+
}
|
|
2102
|
+
|
|
2103
|
+
get defaultStores() {
|
|
2104
|
+
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
2105
|
+
}
|
|
2106
|
+
|
|
2107
|
+
addProto(name, proto) {
|
|
2108
|
+
if (!this.protos[name]) this.protos[name] = proto;
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
addCodec(name, codec) {
|
|
2112
|
+
if (!this.codecs[name]) this.codecs[name] = codec;
|
|
2113
|
+
}
|
|
2114
|
+
|
|
2115
|
+
async addStore(name, prefix, root, isPrivate = true) {
|
|
2116
|
+
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
2117
|
+
name === 'data' || name === 'message') isPrivate = false;
|
|
2118
|
+
|
|
2119
|
+
let Storage;
|
|
2120
|
+
if (this.hasDaemon) {
|
|
2121
|
+
Storage = LeofcoinStorageClient;
|
|
2122
|
+
} else {
|
|
2123
|
+
Storage = LeofcoinStorage;
|
|
2124
|
+
}
|
|
2125
|
+
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
2126
|
+
await new Storage(`${prefix}-${name}`, root);
|
|
2127
|
+
|
|
2128
|
+
globalThis[`${name}Store`].private = isPrivate;
|
|
2129
|
+
if (!isPrivate) this.stores.push(name);
|
|
2130
|
+
}
|
|
2131
|
+
|
|
2132
|
+
|
|
2133
|
+
/**
|
|
2134
|
+
* @see MessageHandler
|
|
2135
|
+
*/
|
|
2136
|
+
prepareMessage(to, data) {
|
|
2137
|
+
return this._messageHandler.prepareMessage(this.id, to, data)
|
|
2138
|
+
}
|
|
2139
|
+
|
|
2140
|
+
/**
|
|
2141
|
+
* @access public
|
|
2142
|
+
*
|
|
2143
|
+
* @return {Array} peerId
|
|
2144
|
+
*/
|
|
2145
|
+
get peers() {
|
|
2146
|
+
return [...connections.values()]
|
|
2147
|
+
}
|
|
2148
|
+
|
|
2149
|
+
/**
|
|
2150
|
+
* @private
|
|
2151
|
+
*
|
|
2152
|
+
* @param {Object} options
|
|
2153
|
+
* @param {String} options.root - path to root directory
|
|
2154
|
+
*
|
|
2155
|
+
* @return {Promise} instance of Peernet
|
|
2156
|
+
*/
|
|
2157
|
+
async _init(options) {
|
|
2158
|
+
// peernetDHT aka closesPeer by coordinates
|
|
2159
|
+
/**
|
|
2160
|
+
* @type {Object}
|
|
2161
|
+
* @property {Object} peer Instance of Peer
|
|
2162
|
+
*/
|
|
2163
|
+
this.dht = new DhtEarth();
|
|
2164
|
+
/**
|
|
2165
|
+
* @type {Map}
|
|
2166
|
+
* @property {Object} peer Instance of Peer
|
|
2167
|
+
*/
|
|
2168
|
+
this.peerMap = new Map();
|
|
2169
|
+
this.stores = [];
|
|
2170
|
+
this.requestProtos = {};
|
|
2171
|
+
this.storePrefix = options.storePrefix;
|
|
2172
|
+
this.root = options.root;
|
|
2173
|
+
|
|
2174
|
+
/**
|
|
2175
|
+
* proto Object containing protos
|
|
2176
|
+
* @type {Object}
|
|
2177
|
+
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
2178
|
+
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
2179
|
+
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
2180
|
+
* @property {DataMessage} protos[peernet-data] messageNode
|
|
2181
|
+
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
2182
|
+
*/
|
|
2183
|
+
globalThis.peernet.protos = {
|
|
2184
|
+
'peernet-request': RequestMessage,
|
|
2185
|
+
'peernet-response': ResponseMessage,
|
|
2186
|
+
'peernet-peer': PeerMessage,
|
|
2187
|
+
'peernet-peer-response': PeerMessageResponse,
|
|
2188
|
+
'peernet-message': PeernetMessage,
|
|
2189
|
+
'peernet-dht': DHTMessage,
|
|
2190
|
+
'peernet-dht-response': DHTMessageResponse,
|
|
2191
|
+
'peernet-data': DataMessage,
|
|
2192
|
+
'peernet-data-response': DataMessageResponse,
|
|
2193
|
+
'peernet-ps': PsMessage,
|
|
2194
|
+
'chat-message': ChatMessage,
|
|
2195
|
+
};
|
|
2196
|
+
|
|
2197
|
+
this.protos = globalThis.peernet.protos;
|
|
2198
|
+
this.codecs = codecs;
|
|
2199
|
+
|
|
2200
|
+
this._messageHandler = new MessageHandler(this.network);
|
|
2201
|
+
|
|
2202
|
+
const {daemon, environment} = await target();
|
|
2203
|
+
this.hasDaemon = daemon;
|
|
2204
|
+
|
|
2205
|
+
HTTP_IMPORT;
|
|
2206
|
+
|
|
2207
|
+
for (const store of this.defaultStores) {
|
|
2208
|
+
await this.addStore(store, options.storePrefix, options.root);
|
|
2209
|
+
}
|
|
2210
|
+
|
|
2211
|
+
try {
|
|
2212
|
+
const pub = await accountStore.get('public');
|
|
2213
|
+
this.id = pub.walletId;
|
|
2214
|
+
} catch (e) {
|
|
2215
|
+
if (e.code === 'ERR_NOT_FOUND') {
|
|
2216
|
+
const wallet = {};
|
|
2217
|
+
const {identity, accounts, config} = await generateAccount(this.network);
|
|
2218
|
+
wallet.identity = identity;
|
|
2219
|
+
wallet.accounts = accounts;
|
|
2220
|
+
wallet.version = 1;
|
|
2221
|
+
walletStore.put(wallet);
|
|
2222
|
+
await accountStore.put('config', config);
|
|
2223
|
+
await accountStore.put('public', {walletId: wallet.identity.walletId});
|
|
2224
|
+
|
|
2225
|
+
this.id = wallet.identity.walletId;
|
|
2226
|
+
} else {
|
|
2227
|
+
throw e
|
|
2228
|
+
}
|
|
2229
|
+
}
|
|
2230
|
+
this._peerHandler = new PeerDiscovery(this.id);
|
|
2231
|
+
this.peerId = this.id;
|
|
2232
|
+
|
|
2233
|
+
pubsub.subscribe('peer:connected', async (peer) => {
|
|
2234
|
+
console.log(peer);
|
|
2235
|
+
// console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
2236
|
+
// peer.on('peernet.data', async (message) => {
|
|
2237
|
+
// const id = message.id
|
|
2238
|
+
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
2239
|
+
// const proto = protoFor(message.decoded.data)
|
|
2240
|
+
// this._protoHandler({id, proto}, peer)
|
|
2241
|
+
// })
|
|
2242
|
+
});
|
|
2243
|
+
|
|
2244
|
+
pubsub.subscribe('peer:data', async message => {
|
|
2245
|
+
if (!message.data) return
|
|
2246
|
+
const {id, data} = JSON.parse(new TextDecoder().decode(message.data));
|
|
2247
|
+
const uint8Array = new Uint8Array(Object.keys(data).length);
|
|
2248
|
+
for (var i = 0; i < Object.keys(data).length; i++) {
|
|
2249
|
+
uint8Array[i] = data[i];
|
|
2250
|
+
}
|
|
2251
|
+
message = new PeernetMessage(uint8Array);
|
|
2252
|
+
const proto = protoFor(message.decoded.data);
|
|
2253
|
+
|
|
2254
|
+
const from = new TextDecoder().decode(message.decoded.from);
|
|
2255
|
+
this._protoHandler({id, proto}, this.client.connections[from], from);
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2258
|
+
/**
|
|
2259
|
+
* @access public
|
|
2260
|
+
* @type {PeernetClient}
|
|
2261
|
+
*/
|
|
2262
|
+
this.client = new Client(this.id);
|
|
2263
|
+
if (globalThis.onbeforeunload) {
|
|
2264
|
+
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
2265
|
+
}
|
|
2266
|
+
return this
|
|
2267
|
+
}
|
|
2268
|
+
|
|
2269
|
+
_getPeerId(id) {
|
|
2270
|
+
for (const entry of [...this.peerMap.entries()]) {
|
|
2271
|
+
for (const _id of entry[1]) {
|
|
2272
|
+
if (_id === id) return entry[0]
|
|
2273
|
+
}
|
|
2274
|
+
}
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2277
|
+
addRequestHandler(name, method) {
|
|
2278
|
+
this.requestProtos[name] = method;
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/**
|
|
2282
|
+
* @private
|
|
2283
|
+
*
|
|
2284
|
+
* @param {Buffer} message - peernet message
|
|
2285
|
+
* @param {PeernetPeer} peer - peernet peer
|
|
2286
|
+
*/
|
|
2287
|
+
async _protoHandler(message, peer, from) {
|
|
2288
|
+
const {id, proto} = message;
|
|
2289
|
+
this.bw.down += proto.encoded.length;
|
|
2290
|
+
|
|
2291
|
+
if (proto.name === 'peernet-dht') {
|
|
2292
|
+
let { hash, store } = proto.decoded;
|
|
2293
|
+
let has;
|
|
2294
|
+
|
|
2295
|
+
if (!store) {
|
|
2296
|
+
has = await this.has(hash);
|
|
2297
|
+
} else {
|
|
2298
|
+
store = globalThis[`${store}Store`];
|
|
2299
|
+
if (store.private) has = false;
|
|
2300
|
+
else has = await store.has(hash);
|
|
2301
|
+
}
|
|
2302
|
+
const data = new DHTMessageResponse({hash, has});
|
|
2303
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2304
|
+
|
|
2305
|
+
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2306
|
+
this.bw.up += node.encoded.length;
|
|
2307
|
+
} else if (proto.name === 'peernet-data') {
|
|
2308
|
+
let { hash, store } = proto.decoded;
|
|
2309
|
+
let data;
|
|
2310
|
+
if (!store) {
|
|
2311
|
+
store = await this.whichStore([...this.stores], hash);
|
|
2312
|
+
} else {
|
|
2313
|
+
store = globalThis[`${store}Store`];
|
|
2314
|
+
}
|
|
2315
|
+
if (store && !store.private) {
|
|
2316
|
+
data = await store.get(hash);
|
|
2317
|
+
|
|
2318
|
+
if (data) {
|
|
2319
|
+
data = new DataMessageResponse({hash, data: data.decoded ? Buffer.from(JSON.stringify(data)) : Buffer.from(data)});
|
|
2320
|
+
|
|
2321
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2322
|
+
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2323
|
+
this.bw.up += node.encoded.length;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2326
|
+
|
|
2327
|
+
} else if (proto.name === 'peernet-request') {
|
|
2328
|
+
// TODO: make dynamic
|
|
2329
|
+
// exposeddevapi[proto.decoded.request](proto.decoded.params)
|
|
2330
|
+
const method = this.requestProtos[proto.decoded.request];
|
|
2331
|
+
if (method) {
|
|
2332
|
+
const data = await method();
|
|
2333
|
+
const node = await this.prepareMessage(from, data.encoded);
|
|
2334
|
+
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2335
|
+
this.bw.up += node.encoded.length;
|
|
2336
|
+
}
|
|
2337
|
+
} else if (proto.name === 'peernet-ps' &&
|
|
2338
|
+
this._getPeerId(peer.id) !== this.id.toString()) {
|
|
2339
|
+
globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
|
|
2340
|
+
}
|
|
2341
|
+
// }
|
|
2342
|
+
}
|
|
2343
|
+
|
|
2344
|
+
/**
|
|
2345
|
+
* performs a walk and resolves first encounter
|
|
2346
|
+
*
|
|
2347
|
+
* @param {String} hash
|
|
2348
|
+
*/
|
|
2349
|
+
async walk(hash) {
|
|
2350
|
+
if (!hash) throw new Error('hash expected, received undefined')
|
|
2351
|
+
const data = new DHTMessage({hash});
|
|
2352
|
+
this.client.id;
|
|
2353
|
+
for (const peer of this.peers) {
|
|
2354
|
+
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2355
|
+
|
|
2356
|
+
const result = await peer.request(node.encoded);
|
|
2357
|
+
|
|
2358
|
+
let proto = protoFor(result.data);
|
|
2359
|
+
|
|
2360
|
+
if (proto.name !== 'peernet-message') throw encapsulatedError()
|
|
2361
|
+
const from = proto.decoded.from;
|
|
2362
|
+
proto = protoFor(proto.decoded.data);
|
|
2363
|
+
|
|
2364
|
+
if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
|
|
2365
|
+
|
|
2366
|
+
// TODO: give ip and port (just used for location)
|
|
2367
|
+
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
2368
|
+
peer.connection.remoteFamily = 'ipv4';
|
|
2369
|
+
peer.connection.remoteAddress = '127.0.0.1';
|
|
2370
|
+
peer.connection.remotePort = '0000';
|
|
2371
|
+
}
|
|
2372
|
+
|
|
2373
|
+
const peerInfo = {
|
|
2374
|
+
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
2375
|
+
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
2376
|
+
port: peer.connection.remotePort || peer.connection.localPort,
|
|
2377
|
+
id: from,
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
|
|
2381
|
+
}
|
|
2382
|
+
return
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
/**
|
|
2386
|
+
* Override DHT behavior, try's finding the content three times
|
|
2387
|
+
*
|
|
2388
|
+
* @param {String} hash
|
|
2389
|
+
*/
|
|
2390
|
+
async providersFor(hash) {
|
|
2391
|
+
let providers = await this.dht.providersFor(hash);
|
|
2392
|
+
// walk the network to find a provider
|
|
2393
|
+
if (!providers || providers.length === 0) {
|
|
2394
|
+
await this.walk(hash);
|
|
2395
|
+
providers = await this.dht.providersFor(hash);
|
|
2396
|
+
// second walk the network to find a provider
|
|
2397
|
+
if (!providers || providers.length === 0) {
|
|
2398
|
+
await this.walk(hash);
|
|
2399
|
+
providers = await this.dht.providersFor(hash);
|
|
2400
|
+
}
|
|
2401
|
+
// last walk
|
|
2402
|
+
if (!providers || providers.length === 0) {
|
|
2403
|
+
await this.walk(hash);
|
|
2404
|
+
providers = await this.dht.providersFor(hash);
|
|
2405
|
+
}
|
|
2406
|
+
}
|
|
2407
|
+
// undefined if no providers given
|
|
2408
|
+
return providers
|
|
2409
|
+
}
|
|
2410
|
+
|
|
2411
|
+
get block() {
|
|
2412
|
+
return {
|
|
2413
|
+
get: async (hash) => {
|
|
2414
|
+
const data = await blockStore.has(hash);
|
|
2415
|
+
if (data) return await blockStore.get(hash)
|
|
2416
|
+
return this.requestData(hash, 'block')
|
|
2417
|
+
},
|
|
2418
|
+
put: async (hash, data) => {
|
|
2419
|
+
if (await blockStore.has(hash)) return
|
|
2420
|
+
return await blockStore.put(hash, data)
|
|
2421
|
+
},
|
|
2422
|
+
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
get transaction() {
|
|
2427
|
+
return {
|
|
2428
|
+
get: async (hash) => {
|
|
2429
|
+
const data = await transactionStore.has(hash);
|
|
2430
|
+
if (data) return await transactionStore.get(hash)
|
|
2431
|
+
return this.requestData(hash, 'transaction')
|
|
2432
|
+
},
|
|
2433
|
+
put: async (hash, data) => {
|
|
2434
|
+
if (await transactionStore.has(hash)) return
|
|
2435
|
+
return await transactionStore.put(hash, data)
|
|
2436
|
+
},
|
|
2437
|
+
has: async (hash) => await transactionStore.has(hash),
|
|
2438
|
+
}
|
|
2439
|
+
}
|
|
1968
2440
|
|
|
1969
|
-
|
|
2441
|
+
async requestData(hash, store) {
|
|
2442
|
+
const providers = await this.providersFor(hash);
|
|
2443
|
+
if (!providers || providers.size === 0) throw nothingFoundError(hash)
|
|
2444
|
+
debug(`found ${providers.size} provider(s) for ${hash}`);
|
|
2445
|
+
// get closest peer on earth
|
|
2446
|
+
const closestPeer = await this.dht.closestPeer(providers);
|
|
2447
|
+
// get peer instance by id
|
|
2448
|
+
if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
|
|
2449
|
+
|
|
2450
|
+
const id = closestPeer.id.toString();
|
|
2451
|
+
if (this.peers) {
|
|
2452
|
+
let closest = this.peers.filter((peer) => {
|
|
2453
|
+
if (this._getPeerId(peer.id) === id) return peer
|
|
2454
|
+
});
|
|
2455
|
+
|
|
2456
|
+
let data = new DataMessage({hash, store: store.name ? store.name : store});
|
|
2457
|
+
|
|
2458
|
+
const node = await this.prepareMessage(id, data.encoded);
|
|
2459
|
+
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2460
|
+
else {
|
|
2461
|
+
closest = this.peers.filter((peer) => {
|
|
2462
|
+
if (peer.id.toString() === id) return peer
|
|
2463
|
+
});
|
|
2464
|
+
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2465
|
+
}
|
|
2466
|
+
if (data.data) {
|
|
2467
|
+
let proto = protoFor(Buffer.from(data.data));
|
|
2468
|
+
proto = protoFor(proto.decoded.data);
|
|
2469
|
+
return proto.decoded.data
|
|
2470
|
+
}
|
|
1970
2471
|
|
|
1971
|
-
|
|
1972
|
-
|
|
1973
|
-
|
|
1974
|
-
|
|
2472
|
+
// this.put(hash, proto.decoded.data)
|
|
2473
|
+
}
|
|
2474
|
+
return null
|
|
2475
|
+
}
|
|
1975
2476
|
|
|
1976
|
-
/**
|
|
1977
|
-
* @param {number} account - account to return chain for
|
|
1978
|
-
* @return { internal(addressIndex), external(addressIndex) }
|
|
1979
|
-
*/
|
|
1980
|
-
account(index) {
|
|
1981
|
-
return new HDAccount(new MultiWallet(this.network, this.hdnode), index);
|
|
1982
|
-
}
|
|
1983
2477
|
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
2478
|
+
get message() {
|
|
2479
|
+
return {
|
|
2480
|
+
/**
|
|
2481
|
+
* Get content for given message hash
|
|
2482
|
+
*
|
|
2483
|
+
* @param {String} hash
|
|
2484
|
+
*/
|
|
2485
|
+
get: async (hash) => {
|
|
2486
|
+
debug(`get message ${hash}`);
|
|
2487
|
+
const message = await messageStore.has(hash);
|
|
2488
|
+
if (message) return await messageStore.get(hash)
|
|
2489
|
+
return this.requestData(hash, 'message')
|
|
2490
|
+
},
|
|
2491
|
+
/**
|
|
2492
|
+
* put message content
|
|
2493
|
+
*
|
|
2494
|
+
* @param {String} hash
|
|
2495
|
+
* @param {Buffer} message
|
|
2496
|
+
*/
|
|
2497
|
+
put: async (hash, message) => await messageStore.put(hash, message),
|
|
2498
|
+
/**
|
|
2499
|
+
* @param {String} hash
|
|
2500
|
+
* @return {Boolean}
|
|
2501
|
+
*/
|
|
2502
|
+
has: async (hash) => await messageStore.has(hash),
|
|
2503
|
+
}
|
|
2504
|
+
}
|
|
1992
2505
|
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2506
|
+
get data() {
|
|
2507
|
+
return {
|
|
2508
|
+
/**
|
|
2509
|
+
* Get content for given data hash
|
|
2510
|
+
*
|
|
2511
|
+
* @param {String} hash
|
|
2512
|
+
*/
|
|
2513
|
+
get: async (hash) => {
|
|
2514
|
+
debug(`get data ${hash}`);
|
|
2515
|
+
const data = await dataStore.has(hash);
|
|
2516
|
+
if (data) return await dataStore.get(hash)
|
|
2517
|
+
return this.requestData(hash, 'data')
|
|
2518
|
+
},
|
|
2519
|
+
/**
|
|
2520
|
+
* put data content
|
|
2521
|
+
*
|
|
2522
|
+
* @param {String} hash
|
|
2523
|
+
* @param {Buffer} data
|
|
2524
|
+
*/
|
|
2525
|
+
put: async (hash, data) => await dataStore.put(hash, data),
|
|
2526
|
+
/**
|
|
2527
|
+
* @param {String} hash
|
|
2528
|
+
* @return {Boolean}
|
|
2529
|
+
*/
|
|
2530
|
+
has: async (hash) => await dataStore.has(hash),
|
|
2531
|
+
}
|
|
2001
2532
|
}
|
|
2533
|
+
|
|
2002
2534
|
/**
|
|
2003
|
-
*
|
|
2535
|
+
* goes trough given stores and tries to find data for given hash
|
|
2536
|
+
* @param {Array} stores
|
|
2537
|
+
* @param {string} hash
|
|
2538
|
+
*/
|
|
2539
|
+
async whichStore(stores, hash) {
|
|
2540
|
+
let store = stores.pop();
|
|
2541
|
+
store = globalThis[`${store}Store`];
|
|
2542
|
+
if (store) {
|
|
2543
|
+
const has = await store.has(hash);
|
|
2544
|
+
if (has) return store
|
|
2545
|
+
if (stores.length > 0) return this.whichStore(stores, hash)
|
|
2546
|
+
} else return null
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
/**
|
|
2550
|
+
* Get content for given hash
|
|
2004
2551
|
*
|
|
2005
|
-
* @param {
|
|
2006
|
-
* @param {
|
|
2007
|
-
* @param {Buffer} message.to peer id
|
|
2008
|
-
* @param {string} message.data Peernet message
|
|
2009
|
-
* (PeernetMessage excluded) encoded as a string
|
|
2010
|
-
* @return signature
|
|
2552
|
+
* @param {String} hash - the hash of the wanted data
|
|
2553
|
+
* @param {String} store - storeName to access
|
|
2011
2554
|
*/
|
|
2012
|
-
async
|
|
2013
|
-
|
|
2014
|
-
|
|
2555
|
+
async get(hash, store) {
|
|
2556
|
+
debug(`get ${hash}`);
|
|
2557
|
+
let data;
|
|
2558
|
+
if (store) store = globalThis[`${store}Store`];
|
|
2559
|
+
if (!store) store = await this.whichStore([...this.stores], hash);
|
|
2560
|
+
if (store && await store.has(hash)) data = await store.get(hash);
|
|
2561
|
+
if (data) return data
|
|
2562
|
+
|
|
2563
|
+
return this.requestData(hash, store.name ? store.name : store)
|
|
2564
|
+
}
|
|
2015
2565
|
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2566
|
+
/**
|
|
2567
|
+
* put content
|
|
2568
|
+
*
|
|
2569
|
+
* @param {String} hash
|
|
2570
|
+
* @param {Buffer} data
|
|
2571
|
+
* @param {String} store - storeName to access
|
|
2572
|
+
*/
|
|
2573
|
+
async put(hash, data, store = 'data') {
|
|
2574
|
+
store = globalThis[`${store}Store`];
|
|
2575
|
+
return store.put(hash, data)
|
|
2019
2576
|
}
|
|
2020
2577
|
|
|
2021
2578
|
/**
|
|
2022
|
-
* @param {String}
|
|
2023
|
-
* @
|
|
2024
|
-
* @param {String|PeernetMessage} data - data encoded message string
|
|
2025
|
-
* or the messageNode itself
|
|
2579
|
+
* @param {String} hash
|
|
2580
|
+
* @return {Boolean}
|
|
2026
2581
|
*/
|
|
2027
|
-
async
|
|
2028
|
-
|
|
2029
|
-
if (
|
|
2030
|
-
|
|
2582
|
+
async has(hash) {
|
|
2583
|
+
const store = await this.whichStore([...this.stores], hash);
|
|
2584
|
+
if (store) {
|
|
2585
|
+
if (store.private) return false
|
|
2586
|
+
else return true
|
|
2587
|
+
}
|
|
2588
|
+
return false
|
|
2589
|
+
}
|
|
2031
2590
|
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2591
|
+
/**
|
|
2592
|
+
*
|
|
2593
|
+
* @param {String} topic
|
|
2594
|
+
* @param {String|Object|Array|Boolean|Buffer} data
|
|
2595
|
+
*/
|
|
2596
|
+
async publish(topic, data) {
|
|
2597
|
+
// globalSub.publish(topic, data)
|
|
2598
|
+
if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
|
|
2599
|
+
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
2600
|
+
const id = Math.random().toString(36).slice(-12);
|
|
2601
|
+
data = new PsMessage({data, topic});
|
|
2602
|
+
for (const peer of this.peers) {
|
|
2603
|
+
if (peer.connection._connected) {
|
|
2604
|
+
if (peer.id.toString() !== this.peerId.toString()) {
|
|
2605
|
+
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2606
|
+
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2607
|
+
}
|
|
2608
|
+
} else {
|
|
2609
|
+
this.removePeer(peer);
|
|
2610
|
+
}
|
|
2611
|
+
// TODO: if peer subscribed
|
|
2612
|
+
}
|
|
2613
|
+
}
|
|
2042
2614
|
|
|
2043
|
-
|
|
2615
|
+
createHash(data, name) {
|
|
2616
|
+
return new PeernetHash(data, {name})
|
|
2044
2617
|
}
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
}
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
}
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
* @param {Object} options
|
|
2072
|
-
* @param {String} options.network - desired network
|
|
2073
|
-
* @param {String} options.root - path to root directory
|
|
2074
|
-
* @param {String} options.storePrefix - prefix for datatores (lfc)
|
|
2075
|
-
*
|
|
2076
|
-
* @return {Promise} instance of Peernet
|
|
2077
|
-
*
|
|
2078
|
-
* @example
|
|
2079
|
-
* const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
|
|
2080
|
-
*/
|
|
2081
|
-
constructor(options = {}) {
|
|
2082
|
-
this._discovered = [];
|
|
2083
|
-
/**
|
|
2084
|
-
* @property {String} network - current network
|
|
2085
|
-
*/
|
|
2086
|
-
this.network = options.network || 'leofcoin';
|
|
2087
|
-
const parts = this.network.split(':');
|
|
2088
|
-
|
|
2089
|
-
if (!options.storePrefix) options.storePrefix = 'lfc';
|
|
2090
|
-
if (!options.port) options.port = 2000;
|
|
2091
|
-
if (!options.root) {
|
|
2092
|
-
if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
|
|
2093
|
-
else options.root = `.${this.network}/peernet`;
|
|
2094
|
-
}
|
|
2095
|
-
globalThis.peernet = this;
|
|
2096
|
-
this.bw = {
|
|
2097
|
-
up: 0,
|
|
2098
|
-
down: 0,
|
|
2099
|
-
};
|
|
2100
|
-
return this._init(options)
|
|
2101
|
-
}
|
|
2102
|
-
|
|
2103
|
-
get defaultStores() {
|
|
2104
|
-
return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
|
|
2105
|
-
}
|
|
2106
|
-
|
|
2107
|
-
addProto(name, proto) {
|
|
2108
|
-
if (!this.protos[name]) this.protos[name] = proto;
|
|
2109
|
-
}
|
|
2110
|
-
|
|
2111
|
-
addCodec(name, codec) {
|
|
2112
|
-
if (!this.codecs[name]) this.codecs[name] = codec;
|
|
2113
|
-
}
|
|
2114
|
-
|
|
2115
|
-
async addStore(name, prefix, root, isPrivate = true) {
|
|
2116
|
-
if (name === 'block' || name === 'transaction' || name === 'chain' ||
|
|
2117
|
-
name === 'data' || name === 'message') isPrivate = false;
|
|
2118
|
-
|
|
2119
|
-
let Storage;
|
|
2120
|
-
if (this.hasDaemon) {
|
|
2121
|
-
Storage = LeofcoinStorageClient;
|
|
2122
|
-
} else {
|
|
2123
|
-
Storage = LeofcoinStorage;
|
|
2124
|
-
}
|
|
2125
|
-
globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
|
|
2126
|
-
await new Storage(`${prefix}-${name}`, root);
|
|
2127
|
-
|
|
2128
|
-
globalThis[`${name}Store`].private = isPrivate;
|
|
2129
|
-
if (!isPrivate) this.stores.push(name);
|
|
2130
|
-
}
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
/**
|
|
2134
|
-
* @see MessageHandler
|
|
2135
|
-
*/
|
|
2136
|
-
prepareMessage(to, data) {
|
|
2137
|
-
return this._messageHandler.prepareMessage(this.id, to, data)
|
|
2138
|
-
}
|
|
2139
|
-
|
|
2140
|
-
/**
|
|
2141
|
-
* @access public
|
|
2142
|
-
*
|
|
2143
|
-
* @return {Array} peerId
|
|
2144
|
-
*/
|
|
2145
|
-
get peers() {
|
|
2146
|
-
return [...connections.values()]
|
|
2147
|
-
}
|
|
2148
|
-
|
|
2149
|
-
/**
|
|
2150
|
-
* @private
|
|
2151
|
-
*
|
|
2152
|
-
* @param {Object} options
|
|
2153
|
-
* @param {String} options.root - path to root directory
|
|
2154
|
-
*
|
|
2155
|
-
* @return {Promise} instance of Peernet
|
|
2156
|
-
*/
|
|
2157
|
-
async _init(options) {
|
|
2158
|
-
// peernetDHT aka closesPeer by coordinates
|
|
2159
|
-
/**
|
|
2160
|
-
* @type {Object}
|
|
2161
|
-
* @property {Object} peer Instance of Peer
|
|
2162
|
-
*/
|
|
2163
|
-
this.dht = new DhtEarth();
|
|
2164
|
-
/**
|
|
2165
|
-
* @type {Map}
|
|
2166
|
-
* @property {Object} peer Instance of Peer
|
|
2167
|
-
*/
|
|
2168
|
-
this.peerMap = new Map();
|
|
2169
|
-
this.stores = [];
|
|
2170
|
-
this.requestProtos = {};
|
|
2171
|
-
this.storePrefix = options.storePrefix;
|
|
2172
|
-
this.root = options.root;
|
|
2173
|
-
|
|
2174
|
-
/**
|
|
2175
|
-
* proto Object containing protos
|
|
2176
|
-
* @type {Object}
|
|
2177
|
-
* @property {PeernetMessage} protos[peernet-message] messageNode
|
|
2178
|
-
* @property {DHTMessage} protos[peernet-dht] messageNode
|
|
2179
|
-
* @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
|
|
2180
|
-
* @property {DataMessage} protos[peernet-data] messageNode
|
|
2181
|
-
* @property {DataMessageResponse} protos[peernet-data-response] messageNode
|
|
2182
|
-
*/
|
|
2183
|
-
globalThis.peernet.protos = {
|
|
2184
|
-
'peernet-request': RequestMessage,
|
|
2185
|
-
'peernet-response': ResponseMessage,
|
|
2186
|
-
'peernet-peer': PeerMessage,
|
|
2187
|
-
'peernet-peer-response': PeerMessageResponse,
|
|
2188
|
-
'peernet-message': PeernetMessage,
|
|
2189
|
-
'peernet-dht': DHTMessage,
|
|
2190
|
-
'peernet-dht-response': DHTMessageResponse,
|
|
2191
|
-
'peernet-data': DataMessage,
|
|
2192
|
-
'peernet-data-response': DataMessageResponse,
|
|
2193
|
-
'peernet-ps': PsMessage,
|
|
2194
|
-
'chat-message': ChatMessage,
|
|
2195
|
-
};
|
|
2196
|
-
|
|
2197
|
-
this.protos = globalThis.peernet.protos;
|
|
2198
|
-
this.codecs = codecs;
|
|
2199
|
-
|
|
2200
|
-
this._messageHandler = new MessageHandler(this.network);
|
|
2201
|
-
|
|
2202
|
-
const {daemon, environment} = await target();
|
|
2203
|
-
this.hasDaemon = daemon;
|
|
2204
|
-
|
|
2205
|
-
HTTP_IMPORT;
|
|
2206
|
-
|
|
2207
|
-
for (const store of this.defaultStores) {
|
|
2208
|
-
await this.addStore(store, options.storePrefix, options.root);
|
|
2209
|
-
}
|
|
2210
|
-
|
|
2211
|
-
try {
|
|
2212
|
-
const pub = await accountStore.get('public');
|
|
2213
|
-
this.id = pub.walletId;
|
|
2214
|
-
} catch (e) {
|
|
2215
|
-
if (e.code === 'ERR_NOT_FOUND') {
|
|
2216
|
-
const wallet = {};
|
|
2217
|
-
const {identity, accounts, config} = await generateAccount(this.network);
|
|
2218
|
-
wallet.identity = identity;
|
|
2219
|
-
wallet.accounts = accounts;
|
|
2220
|
-
wallet.version = 1;
|
|
2221
|
-
walletStore.put(wallet);
|
|
2222
|
-
await accountStore.put('config', config);
|
|
2223
|
-
await accountStore.put('public', {walletId: wallet.identity.walletId});
|
|
2224
|
-
|
|
2225
|
-
this.id = wallet.identity.walletId;
|
|
2226
|
-
} else {
|
|
2227
|
-
throw e
|
|
2228
|
-
}
|
|
2229
|
-
}
|
|
2230
|
-
this._peerHandler = new PeerDiscovery(this.id);
|
|
2231
|
-
this.peerId = this.id;
|
|
2232
|
-
|
|
2233
|
-
// pubsub.subscribe('peer:discovered', async (peer) => {
|
|
2234
|
-
// peer.on('peernet.data', async (message) => {
|
|
2235
|
-
// const id = message.id;
|
|
2236
|
-
// message = new PeernetMessage(Buffer.from(message.data.data));
|
|
2237
|
-
// const proto = protoFor(message.decoded.data);
|
|
2238
|
-
// await this._protoHandler({id, proto}, peer);
|
|
2239
|
-
// });
|
|
2240
|
-
// await this._peerHandler.discover(peer);
|
|
2241
|
-
// const fulldId = this._getPeerId(peer.id);
|
|
2242
|
-
// if (fulldId && this._discovered.indexOf(peer.id) === -1) {
|
|
2243
|
-
// this._discovered.push(peer.id);
|
|
2244
|
-
// pubsub.publish('peer:connected', peer);
|
|
2245
|
-
// }
|
|
2246
|
-
// });
|
|
2247
|
-
// pubsub.subscribe('peer:disconnected', async (peer) => {
|
|
2248
|
-
// let index = this._discovered.indexOf(peer.id)
|
|
2249
|
-
// if (index !== -1) this._discovered.splice(index, 1)
|
|
2250
|
-
// const id = this._getPeerId(peer.id)
|
|
2251
|
-
// let peerIds = this.peerMap.get(id)
|
|
2252
|
-
//
|
|
2253
|
-
// if (peerIds) {
|
|
2254
|
-
// index = peerIds.indexOf(peer.id)
|
|
2255
|
-
// if (index !== -1) peerIds.splice(index, 1)
|
|
2256
|
-
// } else {
|
|
2257
|
-
// peerIds = []
|
|
2258
|
-
// }
|
|
2259
|
-
//
|
|
2260
|
-
// if (peerIds.length === 0) this.peerMap.delete(id)
|
|
2261
|
-
// else this.peerMap.set(id, peerIds)
|
|
2262
|
-
// })
|
|
2263
|
-
pubsub.subscribe('peer:connected', async (peer) => {
|
|
2264
|
-
console.log(peer);
|
|
2265
|
-
// console.log({connected: peer.id, as: this._getPeerId(peer.id) });
|
|
2266
|
-
// peer.on('peernet.data', async (message) => {
|
|
2267
|
-
// const id = message.id
|
|
2268
|
-
// message = new PeernetMessage(Buffer.from(message.data.data))
|
|
2269
|
-
// const proto = protoFor(message.decoded.data)
|
|
2270
|
-
// this._protoHandler({id, proto}, peer)
|
|
2271
|
-
// })
|
|
2272
|
-
});
|
|
2273
|
-
|
|
2274
|
-
pubsub.subscribe('peer:data', async message => {
|
|
2275
|
-
console.log({message});
|
|
2276
|
-
if (!message.data) return
|
|
2277
|
-
const {id, data} = JSON.parse(new TextDecoder().decode(message.data));
|
|
2278
|
-
const uint8Array = new Uint8Array(Object.keys(data).length);
|
|
2279
|
-
for (var i = 0; i < Object.keys(data).length; i++) {
|
|
2280
|
-
uint8Array[i] = data[i];
|
|
2281
|
-
}
|
|
2282
|
-
message = new PeernetMessage(uint8Array);
|
|
2283
|
-
const proto = protoFor(message.decoded.data);
|
|
2284
|
-
|
|
2285
|
-
console.log(message.decoded);
|
|
2286
|
-
const from = new TextDecoder().decode(message.decoded.from);
|
|
2287
|
-
this._protoHandler({id, proto}, this.client.connections[from], from);
|
|
2288
|
-
});
|
|
2289
|
-
|
|
2290
|
-
/**
|
|
2291
|
-
* @access public
|
|
2292
|
-
* @type {PeernetClient}
|
|
2293
|
-
*/
|
|
2294
|
-
this.client = new Client(this.id);
|
|
2295
|
-
if (globalThis.onbeforeunload) {
|
|
2296
|
-
globalThis.addEventListener('beforeunload', async () => this.client.close());
|
|
2297
|
-
}
|
|
2298
|
-
return this
|
|
2299
|
-
}
|
|
2300
|
-
|
|
2301
|
-
_getPeerId(id) {
|
|
2302
|
-
for (const entry of [...this.peerMap.entries()]) {
|
|
2303
|
-
for (const _id of entry[1]) {
|
|
2304
|
-
if (_id === id) return entry[0]
|
|
2305
|
-
}
|
|
2306
|
-
}
|
|
2307
|
-
}
|
|
2308
|
-
|
|
2309
|
-
addRequestHandler(name, method) {
|
|
2310
|
-
this.requestProtos[name] = method;
|
|
2311
|
-
}
|
|
2312
|
-
|
|
2313
|
-
/**
|
|
2314
|
-
* @private
|
|
2315
|
-
*
|
|
2316
|
-
* @param {Buffer} message - peernet message
|
|
2317
|
-
* @param {PeernetPeer} peer - peernet peer
|
|
2318
|
-
*/
|
|
2319
|
-
async _protoHandler(message, peer, from) {
|
|
2320
|
-
const {id, proto} = message;
|
|
2321
|
-
console.log(message);
|
|
2322
|
-
this.bw.down += proto.encoded.length;
|
|
2323
|
-
// if (proto.name === 'peernet-peer') {
|
|
2324
|
-
// const from = proto.decoded.id
|
|
2325
|
-
// if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
2326
|
-
// else {
|
|
2327
|
-
// const connections = this.peerMap.get(from)
|
|
2328
|
-
// if (connections.indexOf(peer.id) === -1) {
|
|
2329
|
-
// connections.push(peer.id)
|
|
2330
|
-
// this.peerMap.set(from, connections)
|
|
2331
|
-
// }
|
|
2332
|
-
// }
|
|
2333
|
-
// const data = new PeerMessageResponse({id: this.id})
|
|
2334
|
-
// const node = await this.prepareMessage(from, data.encoded)
|
|
2335
|
-
//
|
|
2336
|
-
// peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})))
|
|
2337
|
-
// this.bw.up += node.encoded.length
|
|
2338
|
-
// } else if (proto.name === 'peernet-peer-response') {
|
|
2339
|
-
// const from = proto.decoded.id
|
|
2340
|
-
// if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
2341
|
-
// else {
|
|
2342
|
-
// const connections = this.peerMap.get(from)
|
|
2343
|
-
// if (connections.indexOf(peer.id) === -1) {
|
|
2344
|
-
// connections.push(peer.id)
|
|
2345
|
-
// this.peerMap.set(from, connections)
|
|
2346
|
-
// }
|
|
2347
|
-
// }
|
|
2348
|
-
// } else {
|
|
2349
|
-
// let from = this._getPeerId(peer.id)
|
|
2350
|
-
// if (!from) {
|
|
2351
|
-
// const data = new PeerMessage({id: this.id})
|
|
2352
|
-
// const node = await this.prepareMessage(peer.id, data.encoded)
|
|
2353
|
-
// this.bw.up += node.encoded.length
|
|
2354
|
-
// let response = await peer.request(node.encoded)
|
|
2355
|
-
// response = protoFor(response)
|
|
2356
|
-
//
|
|
2357
|
-
// response = new PeerMessageResponse(response.decoded.data)
|
|
2358
|
-
//
|
|
2359
|
-
// from = response.decoded.id
|
|
2360
|
-
// if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id])
|
|
2361
|
-
// else {
|
|
2362
|
-
// const connections = this.peerMap.get(from)
|
|
2363
|
-
// if (connections.indexOf(peer.id) === -1) {
|
|
2364
|
-
// connections.push(peer.id)
|
|
2365
|
-
// this.peerMap.set(from, connections)
|
|
2366
|
-
// }
|
|
2367
|
-
// }
|
|
2368
|
-
// }
|
|
2369
|
-
if (proto.name === 'peernet-dht') {
|
|
2370
|
-
let { hash, store } = proto.decoded;
|
|
2371
|
-
let has;
|
|
2372
|
-
|
|
2373
|
-
if (!store) {
|
|
2374
|
-
has = await this.has(hash);
|
|
2375
|
-
} else {
|
|
2376
|
-
store = globalThis[`${store}Store`];
|
|
2377
|
-
if (store.private) has = false;
|
|
2378
|
-
else has = await store.has(hash);
|
|
2379
|
-
}
|
|
2380
|
-
const data = new DHTMessageResponse({hash, has});
|
|
2381
|
-
const node = await this.prepareMessage(from, data.encoded);
|
|
2382
|
-
|
|
2383
|
-
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2384
|
-
this.bw.up += node.encoded.length;
|
|
2385
|
-
} else if (proto.name === 'peernet-data') {
|
|
2386
|
-
let { hash, store } = proto.decoded;
|
|
2387
|
-
let data;
|
|
2388
|
-
if (!store) {
|
|
2389
|
-
store = await this.whichStore([...this.stores], hash);
|
|
2390
|
-
} else {
|
|
2391
|
-
store = globalThis[`${store}Store`];
|
|
2392
|
-
}
|
|
2393
|
-
if (store && !store.private) {
|
|
2394
|
-
data = await store.get(hash);
|
|
2395
|
-
|
|
2396
|
-
if (data) {
|
|
2397
|
-
data = new DataMessageResponse({hash, data: data.decoded ? Buffer.from(JSON.stringify(data)) : Buffer.from(data)});
|
|
2398
|
-
|
|
2399
|
-
const node = await this.prepareMessage(from, data.encoded);
|
|
2400
|
-
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2401
|
-
this.bw.up += node.encoded.length;
|
|
2402
|
-
}
|
|
2403
|
-
}
|
|
2404
|
-
|
|
2405
|
-
} else if (proto.name === 'peernet-peer') {
|
|
2406
|
-
const from = proto.decoded.id;
|
|
2407
|
-
if (!this.peerMap.has(from)) this.peerMap.set(from, [peer.id]);
|
|
2408
|
-
else {
|
|
2409
|
-
const connections = this.peerMap.get(from);
|
|
2410
|
-
connections.push(peer.id);
|
|
2411
|
-
this.peerMap.set(from, connections);
|
|
2412
|
-
}
|
|
2413
|
-
const data = new PeerMessage({id: this.id});
|
|
2414
|
-
const node = await this.prepareMessage(from, data.encoded);
|
|
2415
|
-
|
|
2416
|
-
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2417
|
-
this.bw.up += node.encoded.length;
|
|
2418
|
-
} else if (proto.name === 'peernet-request') {
|
|
2419
|
-
// TODO: make dynamic
|
|
2420
|
-
// exposeddevapi[proto.decoded.request](proto.decoded.params)
|
|
2421
|
-
const method = this.requestProtos[proto.decoded.request];
|
|
2422
|
-
if (method) {
|
|
2423
|
-
const data = await method();
|
|
2424
|
-
const node = await this.prepareMessage(from, data.encoded);
|
|
2425
|
-
peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
|
|
2426
|
-
this.bw.up += node.encoded.length;
|
|
2427
|
-
}
|
|
2428
|
-
} else if (proto.name === 'peernet-ps' &&
|
|
2429
|
-
this._getPeerId(peer.id) !== this.id.toString()) {
|
|
2430
|
-
globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
|
|
2431
|
-
}
|
|
2432
|
-
// }
|
|
2433
|
-
}
|
|
2434
|
-
|
|
2435
|
-
/**
|
|
2436
|
-
* performs a walk and resolves first encounter
|
|
2437
|
-
*
|
|
2438
|
-
* @param {String} hash
|
|
2439
|
-
*/
|
|
2440
|
-
async walk(hash) {
|
|
2441
|
-
if (!hash) throw new Error('hash expected, received undefined')
|
|
2442
|
-
const data = new DHTMessage({hash});
|
|
2443
|
-
this.client.id;
|
|
2444
|
-
for (const peer of this.peers) {
|
|
2445
|
-
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2446
|
-
|
|
2447
|
-
const result = await peer.request(node.encoded);
|
|
2448
|
-
|
|
2449
|
-
let proto = protoFor(result.data);
|
|
2450
|
-
|
|
2451
|
-
if (proto.name !== 'peernet-message') throw encapsulatedError()
|
|
2452
|
-
const from = proto.decoded.from;
|
|
2453
|
-
proto = protoFor(proto.decoded.data);
|
|
2454
|
-
|
|
2455
|
-
if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
|
|
2456
|
-
|
|
2457
|
-
// TODO: give ip and port (just used for location)
|
|
2458
|
-
if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
|
|
2459
|
-
peer.connection.remoteFamily = 'ipv4';
|
|
2460
|
-
peer.connection.remoteAddress = '127.0.0.1';
|
|
2461
|
-
peer.connection.remotePort = '0000';
|
|
2462
|
-
}
|
|
2463
|
-
|
|
2464
|
-
const peerInfo = {
|
|
2465
|
-
family: peer.connection.remoteFamily || peer.connection.localFamily,
|
|
2466
|
-
address: peer.connection.remoteAddress || peer.connection.localAddress,
|
|
2467
|
-
port: peer.connection.remotePort || peer.connection.localPort,
|
|
2468
|
-
id: from,
|
|
2469
|
-
};
|
|
2470
|
-
|
|
2471
|
-
if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
|
|
2472
|
-
}
|
|
2473
|
-
return
|
|
2474
|
-
}
|
|
2475
|
-
|
|
2476
|
-
/**
|
|
2477
|
-
* Override DHT behavior, try's finding the content three times
|
|
2478
|
-
*
|
|
2479
|
-
* @param {String} hash
|
|
2480
|
-
*/
|
|
2481
|
-
async providersFor(hash) {
|
|
2482
|
-
let providers = await this.dht.providersFor(hash);
|
|
2483
|
-
// walk the network to find a provider
|
|
2484
|
-
if (!providers || providers.length === 0) {
|
|
2485
|
-
await this.walk(hash);
|
|
2486
|
-
providers = await this.dht.providersFor(hash);
|
|
2487
|
-
// second walk the network to find a provider
|
|
2488
|
-
if (!providers || providers.length === 0) {
|
|
2489
|
-
await this.walk(hash);
|
|
2490
|
-
providers = await this.dht.providersFor(hash);
|
|
2491
|
-
}
|
|
2492
|
-
// last walk
|
|
2493
|
-
if (!providers || providers.length === 0) {
|
|
2494
|
-
await this.walk(hash);
|
|
2495
|
-
providers = await this.dht.providersFor(hash);
|
|
2496
|
-
}
|
|
2497
|
-
}
|
|
2498
|
-
// undefined if no providers given
|
|
2499
|
-
return providers
|
|
2500
|
-
}
|
|
2501
|
-
|
|
2502
|
-
get block() {
|
|
2503
|
-
return {
|
|
2504
|
-
get: async (hash) => {
|
|
2505
|
-
const data = await blockStore.has(hash);
|
|
2506
|
-
if (data) return await blockStore.get(hash)
|
|
2507
|
-
return this.requestData(hash, 'block')
|
|
2508
|
-
},
|
|
2509
|
-
put: async (hash, data) => {
|
|
2510
|
-
if (await blockStore.has(hash)) return
|
|
2511
|
-
return await blockStore.put(hash, data)
|
|
2512
|
-
},
|
|
2513
|
-
has: async (hash) => await blockStore.has(hash, 'block'),
|
|
2514
|
-
}
|
|
2515
|
-
}
|
|
2516
|
-
|
|
2517
|
-
get transaction() {
|
|
2518
|
-
return {
|
|
2519
|
-
get: async (hash) => {
|
|
2520
|
-
const data = await transactionStore.has(hash);
|
|
2521
|
-
if (data) return await transactionStore.get(hash)
|
|
2522
|
-
return this.requestData(hash, 'transaction')
|
|
2523
|
-
},
|
|
2524
|
-
put: async (hash, data) => {
|
|
2525
|
-
if (await transactionStore.has(hash)) return
|
|
2526
|
-
return await transactionStore.put(hash, data)
|
|
2527
|
-
},
|
|
2528
|
-
has: async (hash) => await transactionStore.has(hash),
|
|
2529
|
-
}
|
|
2530
|
-
}
|
|
2531
|
-
|
|
2532
|
-
async requestData(hash, store) {
|
|
2533
|
-
const providers = await this.providersFor(hash);
|
|
2534
|
-
if (!providers || providers.size === 0) throw nothingFoundError(hash)
|
|
2535
|
-
debug(`found ${providers.size} provider(s) for ${hash}`);
|
|
2536
|
-
// get closest peer on earth
|
|
2537
|
-
const closestPeer = await this.dht.closestPeer(providers);
|
|
2538
|
-
// get peer instance by id
|
|
2539
|
-
if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
|
|
2540
|
-
|
|
2541
|
-
const id = closestPeer.id.toString();
|
|
2542
|
-
if (this.peers) {
|
|
2543
|
-
let closest = this.peers.filter((peer) => {
|
|
2544
|
-
if (this._getPeerId(peer.id) === id) return peer
|
|
2545
|
-
});
|
|
2546
|
-
|
|
2547
|
-
let data = new DataMessage({hash, store: store.name ? store.name : store});
|
|
2548
|
-
|
|
2549
|
-
const node = await this.prepareMessage(id, data.encoded);
|
|
2550
|
-
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2551
|
-
else {
|
|
2552
|
-
closest = this.peers.filter((peer) => {
|
|
2553
|
-
if (peer.id.toString() === id) return peer
|
|
2554
|
-
});
|
|
2555
|
-
if (closest[0]) data = await closest[0].request(node.encoded);
|
|
2556
|
-
}
|
|
2557
|
-
if (data.data) {
|
|
2558
|
-
let proto = protoFor(Buffer.from(data.data));
|
|
2559
|
-
proto = protoFor(proto.decoded.data);
|
|
2560
|
-
return proto.decoded.data
|
|
2561
|
-
}
|
|
2562
|
-
|
|
2563
|
-
// this.put(hash, proto.decoded.data)
|
|
2564
|
-
}
|
|
2565
|
-
return null
|
|
2566
|
-
}
|
|
2567
|
-
|
|
2568
|
-
|
|
2569
|
-
get message() {
|
|
2570
|
-
return {
|
|
2571
|
-
/**
|
|
2572
|
-
* Get content for given message hash
|
|
2573
|
-
*
|
|
2574
|
-
* @param {String} hash
|
|
2575
|
-
*/
|
|
2576
|
-
get: async (hash) => {
|
|
2577
|
-
debug(`get message ${hash}`);
|
|
2578
|
-
const message = await messageStore.has(hash);
|
|
2579
|
-
if (message) return await messageStore.get(hash)
|
|
2580
|
-
return this.requestData(hash, 'message')
|
|
2581
|
-
},
|
|
2582
|
-
/**
|
|
2583
|
-
* put message content
|
|
2584
|
-
*
|
|
2585
|
-
* @param {String} hash
|
|
2586
|
-
* @param {Buffer} message
|
|
2587
|
-
*/
|
|
2588
|
-
put: async (hash, message) => await messageStore.put(hash, message),
|
|
2589
|
-
/**
|
|
2590
|
-
* @param {String} hash
|
|
2591
|
-
* @return {Boolean}
|
|
2592
|
-
*/
|
|
2593
|
-
has: async (hash) => await messageStore.has(hash),
|
|
2594
|
-
}
|
|
2595
|
-
}
|
|
2596
|
-
|
|
2597
|
-
get data() {
|
|
2598
|
-
return {
|
|
2599
|
-
/**
|
|
2600
|
-
* Get content for given data hash
|
|
2601
|
-
*
|
|
2602
|
-
* @param {String} hash
|
|
2603
|
-
*/
|
|
2604
|
-
get: async (hash) => {
|
|
2605
|
-
debug(`get data ${hash}`);
|
|
2606
|
-
const data = await dataStore.has(hash);
|
|
2607
|
-
if (data) return await dataStore.get(hash)
|
|
2608
|
-
return this.requestData(hash, 'data')
|
|
2609
|
-
},
|
|
2610
|
-
/**
|
|
2611
|
-
* put data content
|
|
2612
|
-
*
|
|
2613
|
-
* @param {String} hash
|
|
2614
|
-
* @param {Buffer} data
|
|
2615
|
-
*/
|
|
2616
|
-
put: async (hash, data) => await dataStore.put(hash, data),
|
|
2617
|
-
/**
|
|
2618
|
-
* @param {String} hash
|
|
2619
|
-
* @return {Boolean}
|
|
2620
|
-
*/
|
|
2621
|
-
has: async (hash) => await dataStore.has(hash),
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
|
-
|
|
2625
|
-
/**
|
|
2626
|
-
* goes trough given stores and tries to find data for given hash
|
|
2627
|
-
* @param {Array} stores
|
|
2628
|
-
* @param {string} hash
|
|
2629
|
-
*/
|
|
2630
|
-
async whichStore(stores, hash) {
|
|
2631
|
-
let store = stores.pop();
|
|
2632
|
-
store = globalThis[`${store}Store`];
|
|
2633
|
-
if (store) {
|
|
2634
|
-
const has = await store.has(hash);
|
|
2635
|
-
if (has) return store
|
|
2636
|
-
if (stores.length > 0) return this.whichStore(stores, hash)
|
|
2637
|
-
} else return null
|
|
2638
|
-
}
|
|
2639
|
-
|
|
2640
|
-
/**
|
|
2641
|
-
* Get content for given hash
|
|
2642
|
-
*
|
|
2643
|
-
* @param {String} hash - the hash of the wanted data
|
|
2644
|
-
* @param {String} store - storeName to access
|
|
2645
|
-
*/
|
|
2646
|
-
async get(hash, store) {
|
|
2647
|
-
debug(`get ${hash}`);
|
|
2648
|
-
let data;
|
|
2649
|
-
if (store) store = globalThis[`${store}Store`];
|
|
2650
|
-
if (!store) store = await this.whichStore([...this.stores], hash);
|
|
2651
|
-
if (store && await store.has(hash)) data = await store.get(hash);
|
|
2652
|
-
if (data) return data
|
|
2653
|
-
|
|
2654
|
-
return this.requestData(hash, store.name ? store.name : store)
|
|
2655
|
-
}
|
|
2656
|
-
|
|
2657
|
-
/**
|
|
2658
|
-
* put content
|
|
2659
|
-
*
|
|
2660
|
-
* @param {String} hash
|
|
2661
|
-
* @param {Buffer} data
|
|
2662
|
-
* @param {String} store - storeName to access
|
|
2663
|
-
*/
|
|
2664
|
-
async put(hash, data, store = 'data') {
|
|
2665
|
-
store = globalThis[`${store}Store`];
|
|
2666
|
-
return store.put(hash, data)
|
|
2667
|
-
}
|
|
2668
|
-
|
|
2669
|
-
/**
|
|
2670
|
-
* @param {String} hash
|
|
2671
|
-
* @return {Boolean}
|
|
2672
|
-
*/
|
|
2673
|
-
async has(hash) {
|
|
2674
|
-
const store = await this.whichStore([...this.stores], hash);
|
|
2675
|
-
if (store) {
|
|
2676
|
-
if (store.private) return false
|
|
2677
|
-
else return true
|
|
2678
|
-
}
|
|
2679
|
-
return false
|
|
2680
|
-
}
|
|
2681
|
-
|
|
2682
|
-
/**
|
|
2683
|
-
*
|
|
2684
|
-
* @param {String} topic
|
|
2685
|
-
* @param {String|Object|Array|Boolean|Buffer} data
|
|
2686
|
-
*/
|
|
2687
|
-
async publish(topic, data) {
|
|
2688
|
-
// globalSub.publish(topic, data)
|
|
2689
|
-
if (!Buffer.isBuffer(topic)) topic = Buffer.from(topic);
|
|
2690
|
-
if (!Buffer.isBuffer(data)) data = Buffer.from(data);
|
|
2691
|
-
const id = Math.random().toString(36).slice(-12);
|
|
2692
|
-
data = new PsMessage({data, topic});
|
|
2693
|
-
for (const peer of this.peers) {
|
|
2694
|
-
if (peer.connection._connected) {
|
|
2695
|
-
if (peer.id.toString() !== this.peerId.toString()) {
|
|
2696
|
-
const node = await this.prepareMessage(peer.id, data.encoded);
|
|
2697
|
-
peer.send(Buffer.from(JSON.stringify({id, data: node.encoded})));
|
|
2698
|
-
}
|
|
2699
|
-
} else {
|
|
2700
|
-
this.removePeer(peer);
|
|
2701
|
-
}
|
|
2702
|
-
// TODO: if peer subscribed
|
|
2703
|
-
}
|
|
2704
|
-
}
|
|
2705
|
-
|
|
2706
|
-
createHash(data, name) {
|
|
2707
|
-
return new PeernetHash(data, {name})
|
|
2708
|
-
}
|
|
2709
|
-
|
|
2710
|
-
/**
|
|
2711
|
-
*
|
|
2712
|
-
* @param {String} topic
|
|
2713
|
-
* @param {Method} cb
|
|
2714
|
-
*/
|
|
2715
|
-
async subscribe(topic, cb) {
|
|
2716
|
-
// TODO: if peer subscribed
|
|
2717
|
-
globalSub.subscribe(topic, cb);
|
|
2718
|
-
}
|
|
2719
|
-
|
|
2720
|
-
async removePeer(peer) {
|
|
2721
|
-
connections.delete(peer.id);
|
|
2722
|
-
}
|
|
2723
|
-
|
|
2724
|
-
get Buffer() {
|
|
2725
|
-
return Buffer
|
|
2726
|
-
}
|
|
2727
|
-
// async block(index) {
|
|
2728
|
-
// const _values = []
|
|
2729
|
-
// for (const peer of this.peers) {
|
|
2730
|
-
// const value = await peer.request({type: 'block', index})
|
|
2731
|
-
// console.log(value);
|
|
2732
|
-
// }
|
|
2733
|
-
//
|
|
2734
|
-
// }
|
|
2618
|
+
|
|
2619
|
+
/**
|
|
2620
|
+
*
|
|
2621
|
+
* @param {String} topic
|
|
2622
|
+
* @param {Method} cb
|
|
2623
|
+
*/
|
|
2624
|
+
async subscribe(topic, cb) {
|
|
2625
|
+
// TODO: if peer subscribed
|
|
2626
|
+
globalSub.subscribe(topic, cb);
|
|
2627
|
+
}
|
|
2628
|
+
|
|
2629
|
+
async removePeer(peer) {
|
|
2630
|
+
connections.delete(peer.id);
|
|
2631
|
+
}
|
|
2632
|
+
|
|
2633
|
+
get Buffer() {
|
|
2634
|
+
return Buffer
|
|
2635
|
+
}
|
|
2636
|
+
// async block(index) {
|
|
2637
|
+
// const _values = []
|
|
2638
|
+
// for (const peer of this.peers) {
|
|
2639
|
+
// const value = await peer.request({type: 'block', index})
|
|
2640
|
+
// console.log(value);
|
|
2641
|
+
// }
|
|
2642
|
+
//
|
|
2643
|
+
// }
|
|
2735
2644
|
}
|
|
2736
2645
|
|
|
2737
2646
|
export { Peernet as default };
|