@leofcoin/peernet 0.11.0 → 0.11.3

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.
Files changed (46) hide show
  1. package/coverage/lcov-report/base.css +224 -0
  2. package/coverage/lcov-report/block-navigation.js +87 -0
  3. package/coverage/lcov-report/codec-format-interface.js.html +637 -0
  4. package/coverage/lcov-report/dht-response.js.html +193 -0
  5. package/coverage/lcov-report/favicon.png +0 -0
  6. package/coverage/lcov-report/index.html +131 -0
  7. package/coverage/lcov-report/prettify.css +1 -0
  8. package/coverage/lcov-report/prettify.js +2 -0
  9. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  10. package/coverage/lcov-report/sorter.js +196 -0
  11. package/coverage/lcov.info +199 -0
  12. package/dist/browser/326.peernet.js +1 -0
  13. package/dist/browser/peernet.js +2 -108930
  14. package/dist/browser/peernet.js.LICENSE.txt +40 -0
  15. package/dist/commonjs/{client-bd0caeb7.js → client-1a1f75e6.js} +41 -41
  16. package/dist/commonjs/{codec-4a768e5e.js → codec-8c8c652f.js} +118 -118
  17. package/dist/commonjs/codec-format-interface.js +167 -167
  18. package/dist/commonjs/codec.js +2 -2
  19. package/dist/commonjs/dht-response.js +11 -11
  20. package/dist/commonjs/dht.js +2 -2
  21. package/dist/commonjs/hash.js +149 -149
  22. package/dist/commonjs/{http-2b0735ef.js → http-e088ccdb.js} +1 -1
  23. package/dist/commonjs/peernet-message.js +4 -4
  24. package/dist/commonjs/peernet.js +850 -943
  25. package/dist/commonjs/request.js +2 -2
  26. package/dist/commonjs/response.js +3 -3
  27. package/dist/module/peernet.js +1373 -1466
  28. package/index.html +2 -2
  29. package/package.json +6 -21
  30. package/src/client.js +75 -75
  31. package/src/codec/codec-format-interface.js +172 -172
  32. package/src/codec/codec.js +124 -124
  33. package/src/dht/dht.js +121 -121
  34. package/src/discovery/peer-discovery.js +75 -75
  35. package/src/handlers/message.js +2 -4
  36. package/src/hash/hash.js +155 -155
  37. package/src/http/client/http-client.js +44 -44
  38. package/src/messages/dht-response.js +14 -14
  39. package/src/peer.js +67 -67
  40. package/src/peernet.js +613 -612
  41. package/src/proto/chat-message.proto.js +7 -7
  42. package/src/proto/peernet.proto.js +2 -2
  43. package/src/proto/response.proto.js +1 -1
  44. package/src/utils/utils.js +78 -78
  45. package/test.js +1 -1
  46. package/webpack.config.js +10 -4
@@ -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';
@@ -621,8 +621,8 @@ var proto$a = `
621
621
  message PeernetMessage {
622
622
  required bytes data = 1;
623
623
  required bytes signature = 2;
624
- optional bytes from = 3;
625
- optional bytes to = 4;
624
+ optional string from = 3;
625
+ optional string to = 4;
626
626
  optional string id = 5;
627
627
  }`;
628
628
 
@@ -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 = `
@@ -1281,7 +1281,7 @@ class RequestMessage extends FormatInterface {
1281
1281
  var proto$3 = `
1282
1282
  // PeernetResponseMessage
1283
1283
  message PeernetResponseMessage {
1284
- required string response = 1;
1284
+ required bytes response = 1;
1285
1285
  }
1286
1286
  `;
1287
1287
 
@@ -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}
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
1414
  };
1415
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
- }
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
1488
  }
1489
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;
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
- }
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 = {
@@ -1799,10 +1799,10 @@ class HDWallet {
1799
1799
  return network;
1800
1800
  }
1801
1801
 
1802
- async generate(network) {
1802
+ async generate(password, network) {
1803
1803
  network = this.validateNetwork(network);
1804
- const mnemonic = generateMnemonic();
1805
- const seed = await mnemonicToSeed(mnemonic);
1804
+ const mnemonic = generateMnemonic(256);
1805
+ const seed = await mnemonicToSeed(mnemonic, password);
1806
1806
  this.defineHDNode(bip32.fromSeed(seed, network));
1807
1807
  return mnemonic; // userpw
1808
1808
  }
@@ -1810,8 +1810,8 @@ class HDWallet {
1810
1810
  /**
1811
1811
  * recover using mnemonic (recovery word list)
1812
1812
  */
1813
- async recover(mnemonic, network) {
1814
- network = this.validateNetwork(network);
1813
+ async recover(mnemonic, password, network) {
1814
+ network = this.validateNetwork(network, password);
1815
1815
  const seed = await mnemonicToSeed(mnemonic);
1816
1816
  this.defineHDNode(bip32.fromSeed(seed, network));
1817
1817
  }
@@ -1950,788 +1950,695 @@ class MultiWallet extends HDWallet {
1950
1950
  return encode(buffer);
1951
1951
  }
1952
1952
 
1953
- decode(bs58) {
1954
- let buffer = decode(bs58);
1955
- const version = varint.decode(buffer);
1956
- buffer = buffer.slice(varint.decode.bytes);
1957
- const multiCodec = varint.decode(buffer);
1958
- buffer = buffer.slice(varint.decode.bytes);
1959
- bs58 = encode(buffer);
1960
- if (version !== this.version) throw TypeError('Invalid version');
1961
- if (this.multiCodec !== multiCodec) throw TypeError('Invalid multiCodec');
1962
- return { version, multiCodec, bs58 };
1963
- }
1953
+ decode(bs58) {
1954
+ let buffer = decode(bs58);
1955
+ const version = varint.decode(buffer);
1956
+ buffer = buffer.slice(varint.decode.bytes);
1957
+ const multiCodec = varint.decode(buffer);
1958
+ buffer = buffer.slice(varint.decode.bytes);
1959
+ bs58 = encode(buffer);
1960
+ if (version !== this.version) throw TypeError('Invalid version');
1961
+ if (this.multiCodec !== multiCodec) throw TypeError('Invalid multiCodec');
1962
+ return { version, multiCodec, bs58 };
1963
+ }
1964
+
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
+ let identity = await walletStore.get('identity');
2015
+ identity = JSON.parse(new TextDecoder().decode(identity));
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 (data.encoded) data = data.encoded;
2029
+
2030
+ const message = {
2031
+ from,
2032
+ to,
2033
+ data,
2034
+ };
2035
+ const signature = await this.hashAndSignMessage(message);
2036
+ const node = new PeernetMessage({
2037
+ ...message,
2038
+ signature,
2039
+ });
2040
+
2041
+ return node
2042
+ }
2043
+ }
2044
+
2045
+ const encapsulatedError = () => {
2046
+ return new Error('Nodes/Data should be send encapsulated by peernet-message')
2047
+ };
2048
+
2049
+ const dhtError = (proto) => {
2050
+ const text = `Received proto ${proto.name} expected peernet-dht-response`;
2051
+ return new Error(`Routing error: ${text}`)
2052
+ };
2053
+
2054
+ const nothingFoundError = (hash) => {
2055
+ return new Error(`nothing found for ${hash}`)
2056
+ };
2057
+
2058
+ globalThis.leofcoin = globalThis.leofcoin || {};
2059
+ globalThis.globalSub = globalThis.globalSub || new PubSub({verbose: true});
2060
+
2061
+ /**
2062
+ * @access public
2063
+ * @example
2064
+ * const peernet = new Peernet();
2065
+ */
2066
+ class Peernet {
2067
+ /**
2068
+ * @access public
2069
+ * @param {Object} options
2070
+ * @param {String} options.network - desired network
2071
+ * @param {String} options.root - path to root directory
2072
+ * @param {String} options.storePrefix - prefix for datatores (lfc)
2073
+ *
2074
+ * @return {Promise} instance of Peernet
2075
+ *
2076
+ * @example
2077
+ * const peernet = new Peernet({network: 'leofcoin', root: '.leofcoin'});
2078
+ */
2079
+ constructor(options = {}) {
2080
+ this._discovered = [];
2081
+ /**
2082
+ * @property {String} network - current network
2083
+ */
2084
+ this.network = options.network || 'leofcoin';
2085
+ const parts = this.network.split(':');
2086
+
2087
+ if (!options.storePrefix) options.storePrefix = 'lfc';
2088
+ if (!options.port) options.port = 2000;
2089
+ if (!options.root) {
2090
+ if (parts[1]) options.root = `.${parts[0]}/peernet/${parts[1]}`;
2091
+ else options.root = `.${this.network}/peernet`;
2092
+ }
2093
+ globalThis.peernet = this;
2094
+ this.bw = {
2095
+ up: 0,
2096
+ down: 0,
2097
+ };
2098
+ return this._init(options)
2099
+ }
2100
+
2101
+ get defaultStores() {
2102
+ return ['account', 'wallet', 'block', 'transaction', 'chain', 'data', 'message']
2103
+ }
2104
+
2105
+ addProto(name, proto) {
2106
+ if (!this.protos[name]) this.protos[name] = proto;
2107
+ }
2108
+
2109
+ addCodec(name, codec) {
2110
+ if (!this.codecs[name]) this.codecs[name] = codec;
2111
+ }
2112
+
2113
+ async addStore(name, prefix, root, isPrivate = true) {
2114
+ if (name === 'block' || name === 'transaction' || name === 'chain' ||
2115
+ name === 'data' || name === 'message') isPrivate = false;
2116
+
2117
+ let Storage;
2118
+ if (this.hasDaemon) {
2119
+ Storage = LeofcoinStorageClient;
2120
+ } else {
2121
+ Storage = globalThis.LeofcoinStorage?.default ? globalThis.LeofcoinStorage.default : LeofcoinStorage;
2122
+ }
2123
+ globalThis[`${name}Store`] = globalThis[`${name}Store`] ||
2124
+ await new Storage(`${prefix}-${name}`, root);
2125
+
2126
+ globalThis[`${name}Store`].private = isPrivate;
2127
+ if (!isPrivate) this.stores.push(name);
2128
+ }
2129
+
2130
+
2131
+ /**
2132
+ * @see MessageHandler
2133
+ */
2134
+ prepareMessage(to, data) {
2135
+ return this._messageHandler.prepareMessage(this.id, to, data)
2136
+ }
2137
+
2138
+ /**
2139
+ * @access public
2140
+ *
2141
+ * @return {Array} peerId
2142
+ */
2143
+ get peers() {
2144
+ return [...connections.values()]
2145
+ }
2146
+
2147
+ /**
2148
+ * @private
2149
+ *
2150
+ * @param {Object} options
2151
+ * @param {String} options.root - path to root directory
2152
+ *
2153
+ * @return {Promise} instance of Peernet
2154
+ */
2155
+ async _init(options) {
2156
+ // peernetDHT aka closesPeer by coordinates
2157
+ /**
2158
+ * @type {Object}
2159
+ * @property {Object} peer Instance of Peer
2160
+ */
2161
+ this.dht = new DhtEarth();
2162
+ /**
2163
+ * @type {Map}
2164
+ * @property {Object} peer Instance of Peer
2165
+ */
2166
+ this.peerMap = new Map();
2167
+ this.stores = [];
2168
+ this.requestProtos = {};
2169
+ this.storePrefix = options.storePrefix;
2170
+ this.root = options.root;
2171
+
2172
+ /**
2173
+ * proto Object containing protos
2174
+ * @type {Object}
2175
+ * @property {PeernetMessage} protos[peernet-message] messageNode
2176
+ * @property {DHTMessage} protos[peernet-dht] messageNode
2177
+ * @property {DHTMessageResponse} protos[peernet-dht-response] messageNode
2178
+ * @property {DataMessage} protos[peernet-data] messageNode
2179
+ * @property {DataMessageResponse} protos[peernet-data-response] messageNode
2180
+ */
2181
+ globalThis.peernet.protos = {
2182
+ 'peernet-request': RequestMessage,
2183
+ 'peernet-response': ResponseMessage,
2184
+ 'peernet-peer': PeerMessage,
2185
+ 'peernet-peer-response': PeerMessageResponse,
2186
+ 'peernet-message': PeernetMessage,
2187
+ 'peernet-dht': DHTMessage,
2188
+ 'peernet-dht-response': DHTMessageResponse,
2189
+ 'peernet-data': DataMessage,
2190
+ 'peernet-data-response': DataMessageResponse,
2191
+ 'peernet-ps': PsMessage,
2192
+ 'chat-message': ChatMessage,
2193
+ };
2194
+
2195
+ this.protos = globalThis.peernet.protos;
2196
+ this.codecs = codecs;
2197
+
2198
+ this._messageHandler = new MessageHandler(this.network);
2199
+
2200
+ const {daemon, environment} = await target();
2201
+ this.hasDaemon = daemon;
2202
+
2203
+ HTTP_IMPORT;
2204
+
2205
+ for (const store of this.defaultStores) {
2206
+ await this.addStore(store, options.storePrefix, options.root);
2207
+ }
2208
+
2209
+ try {
2210
+ const pub = await accountStore.get('public');
2211
+ this.id = JSON.parse(new TextDecoder().decode(pub)).walletId;
2212
+ } catch (e) {
2213
+ if (e.code === 'ERR_NOT_FOUND') {
2214
+ const {identity, accounts, config} = await generateAccount(this.network);
2215
+ walletStore.put('version', new TextEncoder().encode(1));
2216
+ walletStore.put('accounts', new TextEncoder().encode(accounts));
2217
+ walletStore.put('identity', new TextEncoder().encode(JSON.stringify(identity)));
2218
+ await accountStore.put('config', new TextEncoder().encode(JSON.stringify(config)));
2219
+ await accountStore.put('public', new TextEncoder().encode(JSON.stringify({walletId: identity.walletId})));
2220
+
2221
+ this.id = identity.walletId;
2222
+ } else {
2223
+ throw e
2224
+ }
2225
+ }
2226
+ this._peerHandler = new PeerDiscovery(this.id);
2227
+ this.peerId = this.id;
2228
+
2229
+ pubsub.subscribe('peer:connected', async (peer) => {
2230
+ console.log(peer);
2231
+ // console.log({connected: peer.id, as: this._getPeerId(peer.id) });
2232
+ // peer.on('peernet.data', async (message) => {
2233
+ // const id = message.id
2234
+ // message = new PeernetMessage(Buffer.from(message.data.data))
2235
+ // const proto = protoFor(message.decoded.data)
2236
+ // this._protoHandler({id, proto}, peer)
2237
+ // })
2238
+ });
2239
+
2240
+ pubsub.subscribe('peer:data', async message => {
2241
+ if (!message.data) return
2242
+ const {id, data} = JSON.parse(new TextDecoder().decode(message.data));
2243
+ const uint8Array = new Uint8Array(Object.keys(data).length);
2244
+ for (var i = 0; i < Object.keys(data).length; i++) {
2245
+ uint8Array[i] = data[i];
2246
+ }
2247
+ message = new PeernetMessage(uint8Array);
2248
+ const proto = protoFor(message.decoded.data);
2249
+
2250
+ const from = message.decoded.from;
2251
+ this._protoHandler({id, proto}, this.client.connections[from], from);
2252
+ });
2253
+
2254
+ /**
2255
+ * @access public
2256
+ * @type {PeernetClient}
2257
+ */
2258
+ this.client = new Client(this.id);
2259
+ if (globalThis.onbeforeunload) {
2260
+ globalThis.addEventListener('beforeunload', async () => this.client.close());
2261
+ }
2262
+ return this
2263
+ }
2264
+
2265
+ _getPeerId(id) {
2266
+ for (const entry of [...this.peerMap.entries()]) {
2267
+ for (const _id of entry[1]) {
2268
+ if (_id === id) return entry[0]
2269
+ }
2270
+ }
2271
+ }
2272
+
2273
+ addRequestHandler(name, method) {
2274
+ this.requestProtos[name] = method;
2275
+ }
2276
+
2277
+ /**
2278
+ * @private
2279
+ *
2280
+ * @param {Buffer} message - peernet message
2281
+ * @param {PeernetPeer} peer - peernet peer
2282
+ */
2283
+ async _protoHandler(message, peer, from) {
2284
+ const {id, proto} = message;
2285
+ this.bw.down += proto.encoded.length;
2286
+
2287
+ if (proto.name === 'peernet-dht') {
2288
+ let { hash, store } = proto.decoded;
2289
+ let has;
2290
+
2291
+ if (!store) {
2292
+ has = await this.has(hash);
2293
+ } else {
2294
+ store = globalThis[`${store}Store`];
2295
+ if (store.private) has = false;
2296
+ else has = await store.has(hash);
2297
+ }
2298
+ const data = new DHTMessageResponse({hash, has});
2299
+ const node = await this.prepareMessage(from, data.encoded);
2300
+
2301
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
2302
+ this.bw.up += node.encoded.length;
2303
+ } else if (proto.name === 'peernet-data') {
2304
+ let { hash, store } = proto.decoded;
2305
+ let data;
2306
+ if (!store) {
2307
+ store = await this.whichStore([...this.stores], hash);
2308
+ } else {
2309
+ store = globalThis[`${store}Store`];
2310
+ }
2311
+ if (store && !store.private) {
2312
+ data = await store.get(hash);
2313
+
2314
+ if (data) {
2315
+ data = new DataMessageResponse({hash, data: data.decoded ? new TextEncoder().encode(JSON.stringify(data.decoded)) : data});
2316
+
2317
+ const node = await this.prepareMessage(from, data.encoded);
2318
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
2319
+ this.bw.up += node.encoded.length;
2320
+ }
2321
+ }
2322
+
2323
+ } else if (proto.name === 'peernet-request') {
2324
+ // TODO: make dynamic
2325
+ // exposeddevapi[proto.decoded.request](proto.decoded.params)
2326
+ const method = this.requestProtos[proto.decoded.request];
2327
+ if (method) {
2328
+ const data = await method();
2329
+ const node = await this.prepareMessage(from, data.encoded);
2330
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
2331
+
2332
+ this.bw.up += node.encoded.length;
2333
+ }
2334
+ } else if (proto.name === 'peernet-ps' &&
2335
+ this._getPeerId(peer.id) !== this.id.toString()) {
2336
+ globalSub.publish(proto.decoded.topic.toString(), proto.decoded.data.toString());
2337
+ }
2338
+ // }
2339
+ }
2340
+
2341
+ /**
2342
+ * performs a walk and resolves first encounter
2343
+ *
2344
+ * @param {String} hash
2345
+ */
2346
+ async walk(hash) {
2347
+ if (!hash) throw new Error('hash expected, received undefined')
2348
+ const data = new DHTMessage({hash});
2349
+ this.client.id;
2350
+ for (const peer of this.peers) {
2351
+ const node = await this.prepareMessage(peer.id, data.encoded);
2352
+
2353
+ const result = await peer.request(node.encoded);
2354
+
2355
+ let proto = protoFor(result.data);
2356
+
2357
+ if (proto.name !== 'peernet-message') throw encapsulatedError()
2358
+ const from = proto.decoded.from;
2359
+ proto = protoFor(proto.decoded.data);
2360
+
2361
+ if (proto.name !== 'peernet-dht-response') throw dhtError(proto.name)
2362
+
2363
+ // TODO: give ip and port (just used for location)
2364
+ if (!peer.connection.remoteAddress || !peer.connection.localAddress) {
2365
+ peer.connection.remoteFamily = 'ipv4';
2366
+ peer.connection.remoteAddress = '127.0.0.1';
2367
+ peer.connection.remotePort = '0000';
2368
+ }
2369
+
2370
+ const peerInfo = {
2371
+ family: peer.connection.remoteFamily || peer.connection.localFamily,
2372
+ address: peer.connection.remoteAddress || peer.connection.localAddress,
2373
+ port: peer.connection.remotePort || peer.connection.localPort,
2374
+ id: from,
2375
+ };
2376
+
2377
+ if (proto.decoded.has) this.dht.addProvider(peerInfo, proto.decoded.hash);
2378
+ }
2379
+ return
2380
+ }
2381
+
2382
+ /**
2383
+ * Override DHT behavior, try's finding the content three times
2384
+ *
2385
+ * @param {String} hash
2386
+ */
2387
+ async providersFor(hash) {
2388
+ let providers = await this.dht.providersFor(hash);
2389
+ // walk the network to find a provider
2390
+ if (!providers || providers.length === 0) {
2391
+ await this.walk(hash);
2392
+ providers = await this.dht.providersFor(hash);
2393
+ // second walk the network to find a provider
2394
+ if (!providers || providers.length === 0) {
2395
+ await this.walk(hash);
2396
+ providers = await this.dht.providersFor(hash);
2397
+ }
2398
+ // last walk
2399
+ if (!providers || providers.length === 0) {
2400
+ await this.walk(hash);
2401
+ providers = await this.dht.providersFor(hash);
2402
+ }
2403
+ }
2404
+ // undefined if no providers given
2405
+ return providers
2406
+ }
2407
+
2408
+ get block() {
2409
+ return {
2410
+ get: async (hash) => {
2411
+ const data = await blockStore.has(hash);
2412
+ if (data) return await blockStore.get(hash)
2413
+ return this.requestData(hash, 'block')
2414
+ },
2415
+ put: async (hash, data) => {
2416
+ if (await blockStore.has(hash)) return
2417
+ return await blockStore.put(hash, data)
2418
+ },
2419
+ has: async (hash) => await blockStore.has(hash, 'block'),
2420
+ }
2421
+ }
2422
+
2423
+ get transaction() {
2424
+ return {
2425
+ get: async (hash) => {
2426
+ const data = await transactionStore.has(hash);
2427
+ if (data) return await transactionStore.get(hash)
2428
+ return this.requestData(hash, 'transaction')
2429
+ },
2430
+ put: async (hash, data) => {
2431
+ if (await transactionStore.has(hash)) return
2432
+ return await transactionStore.put(hash, data)
2433
+ },
2434
+ has: async (hash) => await transactionStore.has(hash),
2435
+ }
2436
+ }
2437
+
2438
+ async requestData(hash, store) {
2439
+ const providers = await this.providersFor(hash);
2440
+ if (!providers || providers.size === 0) throw nothingFoundError(hash)
2441
+ debug(`found ${providers.size} provider(s) for ${hash}`);
2442
+ // get closest peer on earth
2443
+ const closestPeer = await this.dht.closestPeer(providers);
2444
+ // get peer instance by id
2445
+ if (!closestPeer || !closestPeer.id) return this.requestData(hash, store.name ? store.name : store)
2446
+
2447
+ const id = closestPeer.id.toString();
2448
+ if (this.peers) {
2449
+ let closest = this.peers.filter((peer) => {
2450
+ if (this._getPeerId(peer.id) === id) return peer
2451
+ });
2452
+
2453
+ let data = new DataMessage({hash, store: store.name ? store.name : store});
2454
+
2455
+ const node = await this.prepareMessage(id, data.encoded);
2456
+ if (closest[0]) data = await closest[0].request(node.encoded);
2457
+ else {
2458
+ closest = this.peers.filter((peer) => {
2459
+ if (peer.id.toString() === id) return peer
2460
+ });
2461
+ if (closest[0]) data = await closest[0].request(node.encoded);
2462
+ }
2463
+ if (data.data) {
2464
+ console.log(data.data);
2465
+ let proto = protoFor(data.data);
2466
+ proto = protoFor(proto.decoded.data);
2467
+ return proto.decoded.data
2468
+ }
2469
+
2470
+ // this.put(hash, proto.decoded.data)
2471
+ }
2472
+ return null
2473
+ }
2474
+
1964
2475
 
1965
- sign(hash) {
1966
- return new MultiSignature(this.version, this.network.multiCodec)
1967
- .sign(hash, this.privateKeyBuffer);
2476
+ get message() {
2477
+ return {
2478
+ /**
2479
+ * Get content for given message hash
2480
+ *
2481
+ * @param {String} hash
2482
+ */
2483
+ get: async (hash) => {
2484
+ debug(`get message ${hash}`);
2485
+ const message = await messageStore.has(hash);
2486
+ if (message) return await messageStore.get(hash)
2487
+ return this.requestData(hash, 'message')
2488
+ },
2489
+ /**
2490
+ * put message content
2491
+ *
2492
+ * @param {String} hash
2493
+ * @param {Buffer} message
2494
+ */
2495
+ put: async (hash, message) => await messageStore.put(hash, message),
2496
+ /**
2497
+ * @param {String} hash
2498
+ * @return {Boolean}
2499
+ */
2500
+ has: async (hash) => await messageStore.has(hash),
2501
+ }
2502
+ }
1968
2503
 
1969
- }
2504
+ get data() {
2505
+ return {
2506
+ /**
2507
+ * Get content for given data hash
2508
+ *
2509
+ * @param {String} hash
2510
+ */
2511
+ get: async (hash) => {
2512
+ debug(`get data ${hash}`);
2513
+ const data = await dataStore.has(hash);
2514
+ if (data) return await dataStore.get(hash)
2515
+ return this.requestData(hash, 'data')
2516
+ },
2517
+ /**
2518
+ * put data content
2519
+ *
2520
+ * @param {String} hash
2521
+ * @param {Buffer} data
2522
+ */
2523
+ put: async (hash, data) => await dataStore.put(hash, data),
2524
+ /**
2525
+ * @param {String} hash
2526
+ * @return {Boolean}
2527
+ */
2528
+ has: async (hash) => await dataStore.has(hash),
2529
+ }
2530
+ }
1970
2531
 
1971
- verify(multiSignature, hash) {
1972
- return new MultiSignature(this.version, this.network.multiCodec)
1973
- .verify(multiSignature, hash, this.publicKeyBuffer)
1974
- }
2532
+ /**
2533
+ * goes trough given stores and tries to find data for given hash
2534
+ * @param {Array} stores
2535
+ * @param {string} hash
2536
+ */
2537
+ async whichStore(stores, hash) {
2538
+ let store = stores.pop();
2539
+ store = globalThis[`${store}Store`];
2540
+ if (store) {
2541
+ const has = await store.has(hash);
2542
+ if (has) return store
2543
+ if (stores.length > 0) return this.whichStore(stores, hash)
2544
+ } else return null
2545
+ }
1975
2546
 
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
- }
2547
+ /**
2548
+ * Get content for given hash
2549
+ *
2550
+ * @param {String} hash - the hash of the wanted data
2551
+ * @param {String} store - storeName to access
2552
+ */
2553
+ async get(hash, store) {
2554
+ debug(`get ${hash}`);
2555
+ let data;
2556
+ if (store) store = globalThis[`${store}Store`];
2557
+ if (!store) store = await this.whichStore([...this.stores], hash);
2558
+ if (store && await store.has(hash)) data = await store.get(hash);
2559
+ if (data) return data
2560
+
2561
+ return this.requestData(hash, store.name ? store.name : store)
2562
+ }
1983
2563
 
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
- }
2564
+ /**
2565
+ * put content
2566
+ *
2567
+ * @param {String} hash
2568
+ * @param {Buffer} data
2569
+ * @param {String} store - storeName to access
2570
+ */
2571
+ async put(hash, data, store = 'data') {
2572
+ store = globalThis[`${store}Store`];
2573
+ return store.put(hash, data)
2574
+ }
1992
2575
 
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;
2576
+ /**
2577
+ * @param {String} hash
2578
+ * @return {Boolean}
2579
+ */
2580
+ async has(hash) {
2581
+ const store = await this.whichStore([...this.stores], hash);
2582
+ if (store) {
2583
+ if (store.private) return false
2584
+ else return true
2585
+ }
2586
+ return false
2001
2587
  }
2588
+
2002
2589
  /**
2003
- * hash and sign message
2004
2590
  *
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
2591
+ * @param {String} topic
2592
+ * @param {String|Object|Array|Boolean|Buffer} data
2011
2593
  */
2012
- async hashAndSignMessage(message) {
2013
- const hasher = new PeernetHash(message, {name: 'peernet-message'});
2014
- const identity = await walletStore.get('identity');
2594
+ async publish(topic, data) {
2595
+ // globalSub.publish(topic, data)
2596
+ if (topic instanceof Uint8Array === false) topic = new TextEncoder().encode(topic);
2597
+ if (data instanceof Uint8Array === false) data = new TextEncoder().encode(JSON.stringify(data));
2598
+ const id = Math.random().toString(36).slice(-12);
2599
+ data = new PsMessage({data, topic});
2600
+ for (const peer of this.peers) {
2601
+ if (peer.connection._connected) {
2602
+ if (peer.id.toString() !== this.peerId.toString()) {
2603
+ const node = await this.prepareMessage(peer.id, data.encoded);
2604
+ peer.send(new TextEncoder().encode(JSON.stringify({id, data: node.encoded})));
2605
+ }
2606
+ } else {
2607
+ this.removePeer(peer);
2608
+ }
2609
+ // TODO: if peer subscribed
2610
+ }
2611
+ }
2015
2612
 
2016
- const wallet = new MultiWallet(this.network);
2017
- wallet.import(identity.multiWIF);
2018
- return wallet.sign(hasher.hash.slice(0, 32))
2613
+ createHash(data, name) {
2614
+ return new PeernetHash(data, {name})
2019
2615
  }
2020
2616
 
2021
2617
  /**
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
2618
+ *
2619
+ * @param {String} topic
2620
+ * @param {Method} cb
2026
2621
  */
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;
2622
+ async subscribe(topic, cb) {
2623
+ // TODO: if peer subscribed
2624
+ globalSub.subscribe(topic, cb);
2625
+ }
2031
2626
 
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
- });
2627
+ async removePeer(peer) {
2628
+ connections.delete(peer.id);
2629
+ }
2042
2630
 
2043
- return node
2631
+ get Buffer() {
2632
+ return Buffer
2044
2633
  }
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: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
- // }
2634
+ // async block(index) {
2635
+ // const _values = []
2636
+ // for (const peer of this.peers) {
2637
+ // const value = await peer.request({type: 'block', index})
2638
+ // console.log(value);
2639
+ // }
2640
+ //
2641
+ // }
2735
2642
  }
2736
2643
 
2737
2644
  export { Peernet as default };