├── logo.png ├── src ├── client │ ├── index.js │ ├── consts.js │ ├── webrtc.js │ └── message.js ├── wallet │ ├── index.js │ ├── consts.js │ ├── account.js │ ├── address.js │ └── transaction.js ├── multiclient │ ├── index.js │ ├── consts.js │ └── util.js ├── worker │ ├── webpack.worker.js │ └── worker.js ├── common │ ├── pb │ │ ├── index.js │ │ ├── payloads.proto │ │ ├── sigchain.proto │ │ ├── messages.proto │ │ └── transaction.proto │ ├── amount.js │ ├── index.js │ ├── hash.js │ ├── aes.js │ ├── serialize.js │ ├── util.js │ ├── crypto.js │ ├── key.js │ ├── errors.js │ └── rpc.js └── index.js ├── jest.config.js ├── docs └── assets │ ├── fonts │ ├── EOT │ │ ├── SourceCodePro-Bold.eot │ │ └── SourceCodePro-Regular.eot │ ├── OTF │ │ ├── SourceCodePro-Bold.otf │ │ └── SourceCodePro-Regular.otf │ ├── TTF │ │ ├── SourceCodePro-Bold.ttf │ │ └── SourceCodePro-Regular.ttf │ ├── WOFF │ │ ├── OTF │ │ │ ├── SourceCodePro-Bold.otf.woff │ │ │ └── SourceCodePro-Regular.otf.woff │ │ └── TTF │ │ │ ├── SourceCodePro-Bold.ttf.woff │ │ │ └── SourceCodePro-Regular.ttf.woff │ ├── WOFF2 │ │ ├── OTF │ │ │ ├── SourceCodePro-Bold.otf.woff2 │ │ │ └── SourceCodePro-Regular.otf.woff2 │ │ └── TTF │ │ │ ├── SourceCodePro-Bold.ttf.woff2 │ │ │ └── SourceCodePro-Regular.ttf.woff2 │ ├── source-code-pro.css │ └── LICENSE.txt │ ├── bass-addons.css │ ├── split.css │ ├── github.css │ ├── style.css │ ├── site.js │ └── bass.css ├── .flowconfig ├── lib ├── worker │ ├── webpack.worker.js │ └── worker.js ├── client │ ├── index.js │ ├── consts.js │ └── webrtc.js ├── wallet │ ├── index.js │ ├── consts.js │ ├── account.js │ └── address.js ├── multiclient │ ├── index.js │ ├── consts.js │ └── util.js ├── common │ ├── aes.js │ ├── hash.js │ ├── pb │ │ └── index.js │ ├── serialize.js │ ├── index.js │ ├── amount.js │ └── util.js └── index.js ├── babel.config.json ├── .circleci └── config.yml ├── .gitignore ├── examples ├── pubsub.js ├── client.js ├── wallet.js ├── session.js └── webrtc.html ├── __tests__ ├── client.js ├── wallet.js └── session.js ├── package.json ├── LICENSE └── README.md /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/logo.png -------------------------------------------------------------------------------- /src/client/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export { default } from "./client"; 4 | -------------------------------------------------------------------------------- /src/wallet/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export { default } from "./wallet"; 4 | -------------------------------------------------------------------------------- /src/multiclient/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export { default } from "./multiclient"; 4 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | testTimeout: 60000, 3 | collectCoverage: true, 4 | coverageReporters: ['text'], 5 | }; 6 | -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/EOT/SourceCodePro-Bold.eot -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Bold.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/OTF/SourceCodePro-Bold.otf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/TTF/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /docs/assets/fonts/EOT/SourceCodePro-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/EOT/SourceCodePro-Regular.eot -------------------------------------------------------------------------------- /docs/assets/fonts/OTF/SourceCodePro-Regular.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/OTF/SourceCodePro-Regular.otf -------------------------------------------------------------------------------- /docs/assets/fonts/TTF/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/TTF/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | .*/node_modules/module-deps 3 | 4 | [include] 5 | 6 | [libs] 7 | 8 | [lints] 9 | 10 | [options] 11 | 12 | [strict] 13 | -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF/OTF/SourceCodePro-Bold.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF/TTF/SourceCodePro-Bold.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Bold.ttf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF/OTF/SourceCodePro-Regular.otf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF/TTF/SourceCodePro-Regular.ttf.woff -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF2/OTF/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nknorg/nkn-sdk-js/master/docs/assets/fonts/WOFF2/TTF/SourceCodePro-Regular.ttf.woff2 -------------------------------------------------------------------------------- /src/worker/webpack.worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import workify from "./worker"; 4 | 5 | if ( 6 | typeof WorkerGlobalScope !== "undefined" && 7 | self instanceof WorkerGlobalScope 8 | ) { 9 | workify(self); 10 | } 11 | -------------------------------------------------------------------------------- /src/common/pb/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | export * as messages from './messages_pb'; 4 | export * as payloads from './payloads_pb'; 5 | export * as sigchain from './sigchain_pb'; 6 | export * as transaction from './transaction_pb'; 7 | -------------------------------------------------------------------------------- /src/wallet/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export const defaultOptions = { 4 | rpcServerAddr: "https://mainnet-rpc-node-0001.nkn.org/mainnet/api/wallet", 5 | worker: false, 6 | }; 7 | 8 | export const scryptParams = { 9 | saltLen: 8, 10 | N: 1 << 15, 11 | r: 8, 12 | p: 1, 13 | }; 14 | -------------------------------------------------------------------------------- /docs/assets/bass-addons.css: -------------------------------------------------------------------------------- 1 | .input { 2 | font-family: inherit; 3 | display: block; 4 | width: 100%; 5 | height: 2rem; 6 | padding: .5rem; 7 | margin-bottom: 1rem; 8 | border: 1px solid #ccc; 9 | font-size: .875rem; 10 | border-radius: 3px; 11 | box-sizing: border-box; 12 | } 13 | -------------------------------------------------------------------------------- /lib/worker/webpack.worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | var _worker = _interopRequireDefault(require("./worker")); 4 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 5 | if (typeof WorkerGlobalScope !== "undefined" && self instanceof WorkerGlobalScope) { 6 | (0, _worker["default"])(self); 7 | } -------------------------------------------------------------------------------- /babel.config.json: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env", 5 | { 6 | "modules": "commonjs" 7 | } 8 | ], 9 | "@babel/preset-flow" 10 | ], 11 | "plugins": [ 12 | "@babel/plugin-proposal-class-properties", 13 | "@babel/plugin-proposal-export-default-from", 14 | "@babel/plugin-proposal-export-namespace-from" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /src/multiclient/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export const defaultOptions = { 4 | numSubClients: 4, 5 | originalClient: false, 6 | msgCacheExpiration: 300 * 1000, 7 | sessionConfig: {}, 8 | }; 9 | 10 | export const acceptSessionBufSize = 128; 11 | export const defaultSessionAllowAddr = /.*/; 12 | export const multiclientIdentifierRe = /^__\d+__$/; 13 | export const sessionIDSize = 8; 14 | -------------------------------------------------------------------------------- /lib/client/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _client["default"]; 10 | } 11 | }); 12 | var _client = _interopRequireDefault(require("./client")); 13 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } -------------------------------------------------------------------------------- /lib/wallet/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _wallet["default"]; 10 | } 11 | }); 12 | var _wallet = _interopRequireDefault(require("./wallet")); 13 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } -------------------------------------------------------------------------------- /lib/multiclient/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | Object.defineProperty(exports, "default", { 7 | enumerable: true, 8 | get: function get() { 9 | return _multiclient["default"]; 10 | } 11 | }); 12 | var _multiclient = _interopRequireDefault(require("./multiclient")); 13 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } -------------------------------------------------------------------------------- /src/common/amount.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { Decimal } from "decimal.js"; 4 | 5 | Decimal.set({ minE: -8 }); 6 | 7 | /** 8 | * Amount of NKN tokens. See documentation at 9 | * [decimal.js](https://mikemcl.github.io/decimal.js/). 10 | */ 11 | export default class Amount extends Decimal { 12 | static unit = new Decimal("100000000"); 13 | 14 | value() { 15 | return this.times(Amount.unit).floor(); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/common/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export Amount from "./amount"; 4 | export Key from "./key"; 5 | export * as aes from "./aes"; 6 | export * as crypto from "./crypto"; 7 | export * as errors from "./errors"; 8 | export * as hash from "./hash"; 9 | export * as key from "./key"; 10 | export * as pb from "./pb"; 11 | export * as rpc from "./rpc"; 12 | export * as serialize from "./serialize"; 13 | export * as util from "./util"; 14 | -------------------------------------------------------------------------------- /lib/wallet/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.scryptParams = exports.defaultOptions = void 0; 7 | var defaultOptions = exports.defaultOptions = { 8 | rpcServerAddr: "https://mainnet-rpc-node-0001.nkn.org/mainnet/api/wallet", 9 | worker: false 10 | }; 11 | var scryptParams = exports.scryptParams = { 12 | saltLen: 8, 13 | N: 1 << 15, 14 | r: 8, 15 | p: 1 16 | }; -------------------------------------------------------------------------------- /src/common/pb/payloads.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package payloads; 4 | 5 | enum PayloadType { 6 | BINARY = 0; 7 | TEXT = 1; 8 | ACK = 2; 9 | SESSION = 3; 10 | } 11 | 12 | message Message { 13 | bytes payload = 1; 14 | bool encrypted = 2; 15 | bytes nonce = 3; 16 | bytes encrypted_key = 4; 17 | } 18 | 19 | message Payload { 20 | PayloadType type = 1; 21 | bytes message_id = 2; 22 | bytes data = 3; 23 | bytes reply_to_id = 4; 24 | bool no_reply = 5; 25 | } 26 | 27 | message TextData { 28 | string text = 1; 29 | } 30 | -------------------------------------------------------------------------------- /docs/assets/split.css: -------------------------------------------------------------------------------- 1 | .gutter { 2 | background-color: #f5f5f5; 3 | background-repeat: no-repeat; 4 | background-position: 50%; 5 | } 6 | 7 | .gutter.gutter-vertical { 8 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII='); 9 | cursor: ns-resize; 10 | } 11 | 12 | .gutter.gutter-horizontal { 13 | background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=='); 14 | cursor: ew-resize; 15 | } 16 | -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # JavaScript Node CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-javascript/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | - image: circleci/node:10.15 10 | working_directory: ~/repo 11 | steps: 12 | - checkout 13 | - restore_cache: 14 | keys: 15 | - v1-dependencies-{{ checksum "package.json" }} 16 | - v1-dependencies- 17 | - run: yarn install 18 | - save_cache: 19 | paths: 20 | - node_modules 21 | key: v1-dependencies-{{ checksum "package.json" }} 22 | - run: yarn build 23 | -------------------------------------------------------------------------------- /lib/multiclient/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.sessionIDSize = exports.multiclientIdentifierRe = exports.defaultSessionAllowAddr = exports.defaultOptions = exports.acceptSessionBufSize = void 0; 7 | var defaultOptions = exports.defaultOptions = { 8 | numSubClients: 4, 9 | originalClient: false, 10 | msgCacheExpiration: 300 * 1000, 11 | sessionConfig: {} 12 | }; 13 | var acceptSessionBufSize = exports.acceptSessionBufSize = 128; 14 | var defaultSessionAllowAddr = exports.defaultSessionAllowAddr = /.*/; 15 | var multiclientIdentifierRe = exports.multiclientIdentifierRe = /^__\d+__$/; 16 | var sessionIDSize = exports.sessionIDSize = 8; -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import { ready } from "libsodium-wrappers"; 4 | 5 | import * as nkn from "./common"; 6 | import Client from "./client"; 7 | import MultiClient from "./multiclient"; 8 | import Wallet from "./wallet"; 9 | import * as address from "./wallet/address"; 10 | import { setDisableWASM } from "./common/crypto"; 11 | 12 | var setPRNG = nkn.util.setPRNG; 13 | 14 | nkn.ready = ready; 15 | nkn.Client = Client; 16 | nkn.MultiClient = MultiClient; 17 | nkn.Wallet = Wallet; 18 | nkn.setPRNG = setPRNG; 19 | nkn.address = address; 20 | nkn.setDisableWASM = setDisableWASM; 21 | 22 | export default nkn; 23 | 24 | export * from "./common"; 25 | export { ready, Client, MultiClient, Wallet, setPRNG }; 26 | -------------------------------------------------------------------------------- /src/client/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export const defaultOptions = { 4 | reconnectIntervalMin: 1000, 5 | reconnectIntervalMax: 64000, 6 | responseTimeout: 5000, 7 | connectTimeout: 10000, 8 | msgHoldingSeconds: 0, 9 | encrypt: true, 10 | rpcServerAddr: "https://mainnet-rpc-node-0001.nkn.org/mainnet/api/wallet", 11 | stunServerAddr: [ 12 | "stun:stun.l.google.com:19302", 13 | "stun:stun.cloudflare.com:3478", 14 | "stun:stunserver.stunprotocol.org:3478", 15 | ], 16 | worker: false, 17 | }; 18 | 19 | export const defaultPublishOptions = { 20 | txPool: false, 21 | offset: 0, 22 | limit: 1000, 23 | }; 24 | 25 | export const checkTimeoutInterval = 250; 26 | 27 | export const waitForChallengeTimeout = 5000; 28 | -------------------------------------------------------------------------------- /src/common/pb/sigchain.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package sigchain; 4 | 5 | enum SigAlgo { 6 | SIGNATURE = 0; 7 | VRF = 1; 8 | } 9 | 10 | message SigChainElem { 11 | bytes id = 1; 12 | bytes next_pubkey = 2; 13 | bool mining = 3; 14 | bytes signature = 4; 15 | SigAlgo sig_algo = 5; 16 | bytes vrf = 6; 17 | bytes proof = 7; 18 | } 19 | 20 | message SigChain { 21 | uint32 nonce = 1; 22 | uint32 data_size = 2; 23 | bytes block_hash = 3; 24 | bytes src_id = 4; 25 | bytes src_pubkey = 5; 26 | bytes dest_id = 6; 27 | bytes dest_pubkey = 7; 28 | repeated SigChainElem elems = 8; 29 | } 30 | -------------------------------------------------------------------------------- /src/common/hash.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import CryptoJS from "crypto-js"; 4 | 5 | export function cryptoHexStringParse(hexString) { 6 | return CryptoJS.enc.Hex.parse(hexString); 7 | } 8 | 9 | export function sha256(str) { 10 | return CryptoJS.SHA256(str).toString(); 11 | } 12 | 13 | export function sha256Hex(hexStr) { 14 | return sha256(cryptoHexStringParse(hexStr)); 15 | } 16 | 17 | export function doubleSha256(str) { 18 | return CryptoJS.SHA256(CryptoJS.SHA256(str)).toString(); 19 | } 20 | 21 | export function doubleSha256Hex(hexStr) { 22 | return doubleSha256(cryptoHexStringParse(hexStr)); 23 | } 24 | 25 | export function ripemd160(str) { 26 | return CryptoJS.RIPEMD160(str).toString(); 27 | } 28 | 29 | export function ripemd160Hex(hexStr) { 30 | return ripemd160(cryptoHexStringParse(hexStr)); 31 | } 32 | -------------------------------------------------------------------------------- /src/common/aes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import CryptoJS from "crypto-js"; 4 | 5 | export function encrypt(plaintext, password, iv) { 6 | return CryptoJS.AES.encrypt( 7 | CryptoJS.enc.Hex.parse(plaintext), 8 | CryptoJS.enc.Hex.parse(password), 9 | { 10 | iv: CryptoJS.enc.Hex.parse(iv), 11 | mode: CryptoJS.mode.CBC, 12 | padding: CryptoJS.pad.NoPadding, 13 | }, 14 | ).ciphertext.toString(CryptoJS.enc.Hex); 15 | } 16 | 17 | export function decrypt(ciphertext, password, iv) { 18 | return CryptoJS.AES.decrypt( 19 | CryptoJS.enc.Hex.parse(ciphertext).toString(CryptoJS.enc.Base64), 20 | CryptoJS.enc.Hex.parse(password), 21 | { 22 | iv: CryptoJS.enc.Hex.parse(iv), 23 | mode: CryptoJS.mode.CBC, 24 | padding: CryptoJS.pad.NoPadding, 25 | }, 26 | ).toString(); 27 | } 28 | -------------------------------------------------------------------------------- /src/common/pb/messages.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package messages; 4 | 5 | enum ClientMessageType { 6 | OUTBOUND_MESSAGE = 0; 7 | INBOUND_MESSAGE = 1; 8 | RECEIPT = 2; 9 | } 10 | 11 | enum CompressionType { 12 | COMPRESSION_NONE = 0; 13 | COMPRESSION_ZLIB = 1; 14 | } 15 | 16 | message ClientMessage { 17 | ClientMessageType message_type = 1; 18 | bytes message = 2; 19 | CompressionType compression_type = 3; 20 | } 21 | 22 | message OutboundMessage { 23 | string dest = 1; 24 | bytes payload = 2; 25 | repeated string dests = 3; 26 | uint32 max_holding_seconds = 4; 27 | uint32 nonce = 5; 28 | bytes block_hash = 6; 29 | repeated bytes signatures = 7; 30 | repeated bytes payloads = 8; 31 | } 32 | 33 | message InboundMessage { 34 | string src = 1; 35 | bytes payload = 2; 36 | bytes prev_signature = 3; 37 | } 38 | 39 | message Receipt { 40 | bytes prev_signature = 1; 41 | bytes signature = 2; 42 | } 43 | -------------------------------------------------------------------------------- /lib/client/consts.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.waitForChallengeTimeout = exports.defaultPublishOptions = exports.defaultOptions = exports.checkTimeoutInterval = void 0; 7 | var defaultOptions = exports.defaultOptions = { 8 | reconnectIntervalMin: 1000, 9 | reconnectIntervalMax: 64000, 10 | responseTimeout: 5000, 11 | connectTimeout: 10000, 12 | msgHoldingSeconds: 0, 13 | encrypt: true, 14 | rpcServerAddr: "https://mainnet-rpc-node-0001.nkn.org/mainnet/api/wallet", 15 | stunServerAddr: ["stun:stun.l.google.com:19302", "stun:stun.cloudflare.com:3478", "stun:stunserver.stunprotocol.org:3478"], 16 | worker: false 17 | }; 18 | var defaultPublishOptions = exports.defaultPublishOptions = { 19 | txPool: false, 20 | offset: 0, 21 | limit: 1000 22 | }; 23 | var checkTimeoutInterval = exports.checkTimeoutInterval = 250; 24 | var waitForChallengeTimeout = exports.waitForChallengeTimeout = 5000; -------------------------------------------------------------------------------- /docs/assets/fonts/source-code-pro.css: -------------------------------------------------------------------------------- 1 | @font-face{ 2 | font-family: 'Source Code Pro'; 3 | font-weight: 400; 4 | font-style: normal; 5 | font-stretch: normal; 6 | src: url('EOT/SourceCodePro-Regular.eot') format('embedded-opentype'), 7 | url('WOFF2/TTF/SourceCodePro-Regular.ttf.woff2') format('woff2'), 8 | url('WOFF/OTF/SourceCodePro-Regular.otf.woff') format('woff'), 9 | url('OTF/SourceCodePro-Regular.otf') format('opentype'), 10 | url('TTF/SourceCodePro-Regular.ttf') format('truetype'); 11 | } 12 | 13 | @font-face{ 14 | font-family: 'Source Code Pro'; 15 | font-weight: 700; 16 | font-style: normal; 17 | font-stretch: normal; 18 | src: url('EOT/SourceCodePro-Bold.eot') format('embedded-opentype'), 19 | url('WOFF2/TTF/SourceCodePro-Bold.ttf.woff2') format('woff2'), 20 | url('WOFF/OTF/SourceCodePro-Bold.otf.woff') format('woff'), 21 | url('OTF/SourceCodePro-Bold.otf') format('opentype'), 22 | url('TTF/SourceCodePro-Bold.ttf') format('truetype'); 23 | } 24 | -------------------------------------------------------------------------------- /src/multiclient/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as consts from "./consts"; 4 | 5 | export function addIdentifierPrefix(base, prefix) { 6 | if (base === "") { 7 | return "" + prefix; 8 | } 9 | if (prefix === "") { 10 | return "" + base; 11 | } 12 | return prefix + "." + base; 13 | } 14 | 15 | export function addIdentifier(addr, id) { 16 | if (id === "") { 17 | return addr; 18 | } 19 | return addIdentifierPrefix(addr, "__" + id + "__"); 20 | } 21 | 22 | export function removeIdentifier(src) { 23 | let s = src.split("."); 24 | if (consts.multiclientIdentifierRe.test(s[0])) { 25 | return { addr: s.slice(1).join("."), clientID: s[0] }; 26 | } 27 | return { addr: src, clientID: "" }; 28 | } 29 | 30 | export function addIdentifierPrefixAll(dest, clientID) { 31 | if (Array.isArray(dest)) { 32 | return dest.map((addr) => addIdentifierPrefix(addr, clientID)); 33 | } 34 | return addIdentifierPrefix(dest, clientID); 35 | } 36 | 37 | export function sessionKey(remoteAddr, sessionID) { 38 | return remoteAddr + sessionID; 39 | } 40 | -------------------------------------------------------------------------------- /lib/common/aes.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.decrypt = decrypt; 7 | exports.encrypt = encrypt; 8 | var _cryptoJs = _interopRequireDefault(require("crypto-js")); 9 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 10 | function encrypt(plaintext, password, iv) { 11 | return _cryptoJs["default"].AES.encrypt(_cryptoJs["default"].enc.Hex.parse(plaintext), _cryptoJs["default"].enc.Hex.parse(password), { 12 | iv: _cryptoJs["default"].enc.Hex.parse(iv), 13 | mode: _cryptoJs["default"].mode.CBC, 14 | padding: _cryptoJs["default"].pad.NoPadding 15 | }).ciphertext.toString(_cryptoJs["default"].enc.Hex); 16 | } 17 | function decrypt(ciphertext, password, iv) { 18 | return _cryptoJs["default"].AES.decrypt(_cryptoJs["default"].enc.Hex.parse(ciphertext).toString(_cryptoJs["default"].enc.Base64), _cryptoJs["default"].enc.Hex.parse(password), { 19 | iv: _cryptoJs["default"].enc.Hex.parse(iv), 20 | mode: _cryptoJs["default"].mode.CBC, 21 | padding: _cryptoJs["default"].pad.NoPadding 22 | }).toString(); 23 | } -------------------------------------------------------------------------------- /src/wallet/account.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as address from "./address"; 4 | import * as common from "../common"; 5 | 6 | export default class Account { 7 | key; 8 | signatureRedeem; 9 | programHash; 10 | address; 11 | contract; 12 | 13 | constructor(seed, options = {}) { 14 | this.key = new common.Key(seed, { worker: options.worker }); 15 | this.signatureRedeem = address.publicKeyToSignatureRedeem( 16 | this.key.publicKey, 17 | ); 18 | this.programHash = address.hexStringToProgramHash(this.signatureRedeem); 19 | this.address = address.programHashStringToAddress(this.programHash); 20 | this.contract = genAccountContractString( 21 | this.signatureRedeem, 22 | this.programHash, 23 | ); 24 | } 25 | 26 | getPublicKey() { 27 | return this.key.publicKey; 28 | } 29 | 30 | getSeed() { 31 | return this.key.seed; 32 | } 33 | } 34 | 35 | function genAccountContractString(signatureRedeem, programHash) { 36 | let contract = address.prefixByteCountToHexString(signatureRedeem); 37 | contract += address.prefixByteCountToHexString("00"); 38 | contract += programHash; 39 | return contract; 40 | } 41 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # jetbrains 64 | .idea/ 65 | 66 | # others 67 | .DS_Store 68 | *~ 69 | -------------------------------------------------------------------------------- /lib/common/hash.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.cryptoHexStringParse = cryptoHexStringParse; 7 | exports.doubleSha256 = doubleSha256; 8 | exports.doubleSha256Hex = doubleSha256Hex; 9 | exports.ripemd160 = ripemd160; 10 | exports.ripemd160Hex = ripemd160Hex; 11 | exports.sha256 = sha256; 12 | exports.sha256Hex = sha256Hex; 13 | var _cryptoJs = _interopRequireDefault(require("crypto-js")); 14 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 15 | function cryptoHexStringParse(hexString) { 16 | return _cryptoJs["default"].enc.Hex.parse(hexString); 17 | } 18 | function sha256(str) { 19 | return _cryptoJs["default"].SHA256(str).toString(); 20 | } 21 | function sha256Hex(hexStr) { 22 | return sha256(cryptoHexStringParse(hexStr)); 23 | } 24 | function doubleSha256(str) { 25 | return _cryptoJs["default"].SHA256(_cryptoJs["default"].SHA256(str)).toString(); 26 | } 27 | function doubleSha256Hex(hexStr) { 28 | return doubleSha256(cryptoHexStringParse(hexStr)); 29 | } 30 | function ripemd160(str) { 31 | return _cryptoJs["default"].RIPEMD160(str).toString(); 32 | } 33 | function ripemd160Hex(hexStr) { 34 | return ripemd160(cryptoHexStringParse(hexStr)); 35 | } -------------------------------------------------------------------------------- /src/worker/worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as crypto from "../common/crypto"; 4 | import * as util from "../common/util"; 5 | 6 | module.exports = function (self) { 7 | let key; 8 | self.onmessage = async function (e) { 9 | try { 10 | let result = null; 11 | switch (e.data.action) { 12 | case "setSeed": 13 | if (!key) { 14 | key = crypto.keyPair(e.data.seed); 15 | } else if (e.data.seed !== key.seed) { 16 | throw "cannot set to different seed"; 17 | } 18 | break; 19 | case "computeSharedKey": 20 | if (key) { 21 | result = await crypto.computeSharedKey( 22 | key.curvePrivateKey, 23 | e.data.otherPubkey, 24 | ); 25 | } else { 26 | throw "worker key not created"; 27 | } 28 | break; 29 | case "sign": 30 | if (key) { 31 | result = await crypto.sign(key.privateKey, e.data.message); 32 | } else { 33 | throw "worker key not created"; 34 | } 35 | break; 36 | default: 37 | throw "unknown action: " + e.data.action; 38 | } 39 | self.postMessage({ id: e.data.id, result }); 40 | } catch (err) { 41 | self.postMessage({ id: e.data.id, error: err }); 42 | } 43 | }; 44 | }; 45 | -------------------------------------------------------------------------------- /examples/pubsub.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nkn = require("../lib"); 4 | 5 | (async function () { 6 | // client 1 7 | const wallet1 = new nkn.Wallet({ password: "password1" }); 8 | const client1 = new nkn.Client({ 9 | identifier: "my-identifier", 10 | seed: wallet1.getSeed(), 11 | }); 12 | client1.onMessage(({ src, payload }) => { 13 | console.log("got data on client 1", { src, payload }); 14 | }); 15 | 16 | // client 2 17 | const wallet2 = new nkn.Wallet({ password: "password2" }); 18 | const client2 = new nkn.Client({ 19 | identifier: "my-identifier", 20 | seed: wallet2.getSeed(), 21 | }); 22 | client2.onMessage(({ src, payload }) => { 23 | console.log("got data on client 2", { src, payload }); 24 | }); 25 | 26 | await Promise.all([ 27 | new Promise((resolve) => client1.onConnect(resolve)), 28 | new Promise((resolve) => client2.onConnect(resolve)), 29 | ]); 30 | await Promise.all([ 31 | new Promise((resolve) => 32 | wallet1.subscribe("some-topic", 100, "my-identifier").then(resolve), 33 | ), 34 | new Promise((resolve) => 35 | wallet2.subscribe("some-topic", 100, "my-identifier").then(resolve), 36 | ), 37 | ]); 38 | 39 | const num = await client1.getSubscribersCount("some-topic"); 40 | const subs = await client1.getSubscribers("some-topic"); 41 | console.log({ num, subs }); 42 | 43 | // publish 44 | client1.publish("some-topic", "hello world", { txPool: true }); 45 | })(); 46 | -------------------------------------------------------------------------------- /src/common/serialize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import errors from "./errors"; 4 | 5 | export const maxUintBits = 48; 6 | export const maxUint = 2 ** maxUintBits; 7 | 8 | export function encodeUint8(value) { 9 | let buf = Buffer.alloc(1, 0); 10 | buf.writeUInt8(value); 11 | return buf.toString("hex"); 12 | } 13 | 14 | export function encodeUint16(value) { 15 | let buf = Buffer.alloc(2, 0); 16 | buf.writeUInt16LE(value); 17 | return buf.toString("hex"); 18 | } 19 | 20 | export function encodeUint32(value) { 21 | let buf = Buffer.alloc(4, 0); 22 | buf.writeUInt32LE(value); 23 | return buf.toString("hex"); 24 | } 25 | 26 | export function encodeUint64(value) { 27 | if (value > maxUint) { 28 | throw new RangeError("full 64 bit integer is not supported in JavaScript"); 29 | } 30 | let buf = Buffer.alloc(8, 0); 31 | buf.writeUIntLE(value, 0, 6); 32 | return buf.toString("hex"); 33 | } 34 | 35 | export function encodeUint(value) { 36 | if (value < 0xfd) { 37 | return encodeUint8(value); 38 | } else if (value <= 0xffff) { 39 | return "fd" + encodeUint16(value); 40 | } else if (value <= 0xffffffff) { 41 | return "fe" + encodeUint32(value); 42 | } else { 43 | return "ff" + encodeUint64(value); 44 | } 45 | } 46 | 47 | export function encodeBytes(value) { 48 | let buf = Buffer.from(value); 49 | return encodeUint(buf.length) + buf.toString("hex"); 50 | } 51 | 52 | export function encodeString(value) { 53 | let buf = Buffer.from(value, "utf8"); 54 | return encodeUint(buf.length) + buf.toString("hex"); 55 | } 56 | 57 | export function encodeBool(value) { 58 | return encodeUint8(value ? 1 : 0); 59 | } 60 | -------------------------------------------------------------------------------- /lib/common/pb/index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.transaction = exports.sigchain = exports.payloads = exports.messages = void 0; 8 | var _messages = _interopRequireWildcard(require("./messages_pb")); 9 | exports.messages = _messages; 10 | var _payloads = _interopRequireWildcard(require("./payloads_pb")); 11 | exports.payloads = _payloads; 12 | var _sigchain = _interopRequireWildcard(require("./sigchain_pb")); 13 | exports.sigchain = _sigchain; 14 | var _transaction = _interopRequireWildcard(require("./transaction_pb")); 15 | exports.transaction = _transaction; 16 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 17 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } -------------------------------------------------------------------------------- /examples/client.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nkn = require("../lib"); 4 | 5 | const useMultiClient = true; 6 | 7 | (async function () { 8 | let Client = useMultiClient ? nkn.MultiClient : nkn.Client; 9 | let alice = new Client({ identifier: "alice" }); 10 | let bob = new Client({ identifier: "bob" }); 11 | 12 | console.log("Secret seed:", alice.getSeed()); 13 | 14 | alice.onConnectFailed(() => { 15 | console.error("Alice connect failed"); 16 | }); 17 | 18 | if (useMultiClient) { 19 | for (let clientID of Object.keys(alice.clients)) { 20 | alice.clients[clientID].onConnectFailed(() => { 21 | console.error("Alice client", clientID, "connect failed"); 22 | }); 23 | } 24 | } 25 | 26 | await Promise.all([ 27 | new Promise((resolve, reject) => alice.onConnect(resolve)), 28 | new Promise((resolve, reject) => bob.onConnect(resolve)), 29 | ]); 30 | 31 | await new Promise((resolve, reject) => setTimeout(resolve, 1000)); 32 | 33 | let timeSent = Date.now(); 34 | 35 | bob.onMessage(async ({ src, payload, isEncrypted }) => { 36 | console.log( 37 | "Receive", 38 | isEncrypted ? "encrypted" : "unencrypted", 39 | "message", 40 | '"' + payload + '"', 41 | "from", 42 | src, 43 | "after", 44 | Date.now() - timeSent, 45 | "ms", 46 | ); 47 | // For byte array response: 48 | // return Uint8Array.from([1,2,3,4,5]) 49 | return "Well received!"; 50 | }); 51 | 52 | try { 53 | console.log("Send message from", alice.addr, "to", bob.addr); 54 | // For byte array data: 55 | // let reply = await alice.send(bob.addr, Uint8Array.from([1,2,3,4,5])); 56 | let reply = await alice.send(bob.addr, "Hello world!"); 57 | console.log( 58 | "Receive reply", 59 | '"' + reply + '"', 60 | "from", 61 | bob.addr, 62 | "after", 63 | Date.now() - timeSent, 64 | "ms", 65 | ); 66 | } catch (e) { 67 | console.error(e); 68 | } 69 | 70 | alice.close(); 71 | bob.close(); 72 | })(); 73 | -------------------------------------------------------------------------------- /lib/common/serialize.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.encodeBool = encodeBool; 7 | exports.encodeBytes = encodeBytes; 8 | exports.encodeString = encodeString; 9 | exports.encodeUint = encodeUint; 10 | exports.encodeUint16 = encodeUint16; 11 | exports.encodeUint32 = encodeUint32; 12 | exports.encodeUint64 = encodeUint64; 13 | exports.encodeUint8 = encodeUint8; 14 | exports.maxUintBits = exports.maxUint = void 0; 15 | var _errors = _interopRequireDefault(require("./errors")); 16 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 17 | var maxUintBits = exports.maxUintBits = 48; 18 | var maxUint = exports.maxUint = Math.pow(2, maxUintBits); 19 | function encodeUint8(value) { 20 | var buf = Buffer.alloc(1, 0); 21 | buf.writeUInt8(value); 22 | return buf.toString("hex"); 23 | } 24 | function encodeUint16(value) { 25 | var buf = Buffer.alloc(2, 0); 26 | buf.writeUInt16LE(value); 27 | return buf.toString("hex"); 28 | } 29 | function encodeUint32(value) { 30 | var buf = Buffer.alloc(4, 0); 31 | buf.writeUInt32LE(value); 32 | return buf.toString("hex"); 33 | } 34 | function encodeUint64(value) { 35 | if (value > maxUint) { 36 | throw new RangeError("full 64 bit integer is not supported in JavaScript"); 37 | } 38 | var buf = Buffer.alloc(8, 0); 39 | buf.writeUIntLE(value, 0, 6); 40 | return buf.toString("hex"); 41 | } 42 | function encodeUint(value) { 43 | if (value < 0xfd) { 44 | return encodeUint8(value); 45 | } else if (value <= 0xffff) { 46 | return "fd" + encodeUint16(value); 47 | } else if (value <= 0xffffffff) { 48 | return "fe" + encodeUint32(value); 49 | } else { 50 | return "ff" + encodeUint64(value); 51 | } 52 | } 53 | function encodeBytes(value) { 54 | var buf = Buffer.from(value); 55 | return encodeUint(buf.length) + buf.toString("hex"); 56 | } 57 | function encodeString(value) { 58 | var buf = Buffer.from(value, "utf8"); 59 | return encodeUint(buf.length) + buf.toString("hex"); 60 | } 61 | function encodeBool(value) { 62 | return encodeUint8(value ? 1 : 0); 63 | } -------------------------------------------------------------------------------- /docs/assets/github.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | github.com style (c) Vasily Polovnyov 4 | 5 | */ 6 | 7 | .hljs { 8 | display: block; 9 | overflow-x: auto; 10 | padding: 0.5em; 11 | color: #333; 12 | background: #f8f8f8; 13 | -webkit-text-size-adjust: none; 14 | } 15 | 16 | .hljs-comment, 17 | .diff .hljs-header, 18 | .hljs-javadoc { 19 | color: #998; 20 | font-style: italic; 21 | } 22 | 23 | .hljs-keyword, 24 | .css .rule .hljs-keyword, 25 | .hljs-winutils, 26 | .nginx .hljs-title, 27 | .hljs-subst, 28 | .hljs-request, 29 | .hljs-status { 30 | color: #1184CE; 31 | } 32 | 33 | .hljs-number, 34 | .hljs-hexcolor, 35 | .ruby .hljs-constant { 36 | color: #ed225d; 37 | } 38 | 39 | .hljs-string, 40 | .hljs-tag .hljs-value, 41 | .hljs-phpdoc, 42 | .hljs-dartdoc, 43 | .tex .hljs-formula { 44 | color: #ed225d; 45 | } 46 | 47 | .hljs-title, 48 | .hljs-id, 49 | .scss .hljs-preprocessor { 50 | color: #900; 51 | font-weight: bold; 52 | } 53 | 54 | .hljs-list .hljs-keyword, 55 | .hljs-subst { 56 | font-weight: normal; 57 | } 58 | 59 | .hljs-class .hljs-title, 60 | .hljs-type, 61 | .vhdl .hljs-literal, 62 | .tex .hljs-command { 63 | color: #458; 64 | font-weight: bold; 65 | } 66 | 67 | .hljs-tag, 68 | .hljs-tag .hljs-title, 69 | .hljs-rules .hljs-property, 70 | .django .hljs-tag .hljs-keyword { 71 | color: #000080; 72 | font-weight: normal; 73 | } 74 | 75 | .hljs-attribute, 76 | .hljs-variable, 77 | .lisp .hljs-body { 78 | color: #008080; 79 | } 80 | 81 | .hljs-regexp { 82 | color: #009926; 83 | } 84 | 85 | .hljs-symbol, 86 | .ruby .hljs-symbol .hljs-string, 87 | .lisp .hljs-keyword, 88 | .clojure .hljs-keyword, 89 | .scheme .hljs-keyword, 90 | .tex .hljs-special, 91 | .hljs-prompt { 92 | color: #990073; 93 | } 94 | 95 | .hljs-built_in { 96 | color: #0086b3; 97 | } 98 | 99 | .hljs-preprocessor, 100 | .hljs-pragma, 101 | .hljs-pi, 102 | .hljs-doctype, 103 | .hljs-shebang, 104 | .hljs-cdata { 105 | color: #999; 106 | font-weight: bold; 107 | } 108 | 109 | .hljs-deletion { 110 | background: #fdd; 111 | } 112 | 113 | .hljs-addition { 114 | background: #dfd; 115 | } 116 | 117 | .diff .hljs-change { 118 | background: #0086b3; 119 | } 120 | 121 | .hljs-chunk { 122 | color: #aaa; 123 | } 124 | -------------------------------------------------------------------------------- /lib/multiclient/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.addIdentifier = addIdentifier; 8 | exports.addIdentifierPrefix = addIdentifierPrefix; 9 | exports.addIdentifierPrefixAll = addIdentifierPrefixAll; 10 | exports.removeIdentifier = removeIdentifier; 11 | exports.sessionKey = sessionKey; 12 | var consts = _interopRequireWildcard(require("./consts")); 13 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 14 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 15 | function addIdentifierPrefix(base, prefix) { 16 | if (base === "") { 17 | return "" + prefix; 18 | } 19 | if (prefix === "") { 20 | return "" + base; 21 | } 22 | return prefix + "." + base; 23 | } 24 | function addIdentifier(addr, id) { 25 | if (id === "") { 26 | return addr; 27 | } 28 | return addIdentifierPrefix(addr, "__" + id + "__"); 29 | } 30 | function removeIdentifier(src) { 31 | var s = src.split("."); 32 | if (consts.multiclientIdentifierRe.test(s[0])) { 33 | return { 34 | addr: s.slice(1).join("."), 35 | clientID: s[0] 36 | }; 37 | } 38 | return { 39 | addr: src, 40 | clientID: "" 41 | }; 42 | } 43 | function addIdentifierPrefixAll(dest, clientID) { 44 | if (Array.isArray(dest)) { 45 | return dest.map(function (addr) { 46 | return addIdentifierPrefix(addr, clientID); 47 | }); 48 | } 49 | return addIdentifierPrefix(dest, clientID); 50 | } 51 | function sessionKey(remoteAddr, sessionID) { 52 | return remoteAddr + sessionID; 53 | } -------------------------------------------------------------------------------- /src/common/pb/transaction.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package transaction; 4 | 5 | message UnsignedTx { 6 | Payload payload = 1; 7 | uint64 nonce = 2; 8 | int64 fee = 3; 9 | bytes attributes = 4; 10 | } 11 | 12 | message Transaction { 13 | UnsignedTx unsigned_tx = 1; 14 | repeated Program programs = 2; 15 | } 16 | 17 | message Program { 18 | bytes code = 1; 19 | bytes parameter = 2; 20 | } 21 | 22 | enum PayloadType { 23 | COINBASE_TYPE = 0; 24 | TRANSFER_ASSET_TYPE = 1; 25 | SIG_CHAIN_TXN_TYPE = 2; 26 | REGISTER_NAME_TYPE = 3; 27 | TRANSFER_NAME_TYPE = 4; 28 | DELETE_NAME_TYPE = 5; 29 | SUBSCRIBE_TYPE = 6; 30 | UNSUBSCRIBE_TYPE = 7; 31 | GENERATE_ID_TYPE = 8; 32 | NANO_PAY_TYPE = 9; 33 | ISSUE_ASSET_TYPE = 10; 34 | } 35 | 36 | message Payload { 37 | PayloadType type = 1; 38 | bytes data = 2; 39 | } 40 | 41 | message Coinbase { 42 | bytes sender = 1; 43 | bytes recipient = 2; 44 | int64 amount = 3; 45 | } 46 | 47 | message SigChainTxn { 48 | bytes sig_chain = 1; 49 | bytes submitter = 2; 50 | } 51 | 52 | message RegisterName { 53 | bytes registrant = 1; 54 | string name = 2; 55 | int64 registration_fee = 3; 56 | } 57 | 58 | message TransferName { 59 | string name = 1; 60 | bytes registrant = 2; 61 | bytes recipient = 3; 62 | } 63 | 64 | message DeleteName { 65 | bytes registrant = 1; 66 | string name = 2; 67 | } 68 | 69 | message Subscribe { 70 | bytes subscriber = 1; 71 | string identifier = 2; 72 | string topic = 3; 73 | uint32 bucket = 4 [deprecated = true]; 74 | uint32 duration = 5; 75 | string meta = 6; 76 | } 77 | 78 | message Unsubscribe { 79 | bytes subscriber = 1; 80 | string identifier = 2; 81 | string topic = 3; 82 | } 83 | 84 | message TransferAsset { 85 | bytes sender = 1; 86 | bytes recipient = 2; 87 | int64 amount = 3; 88 | } 89 | 90 | message GenerateID { 91 | bytes public_key = 1; 92 | int64 registration_fee = 2; 93 | } 94 | 95 | message NanoPay { 96 | bytes sender = 1; 97 | bytes recipient = 2; 98 | uint64 id = 3; 99 | int64 amount = 4; 100 | uint32 txn_expiration = 5; 101 | uint32 nano_pay_expiration = 6; 102 | } 103 | 104 | message IssueAsset { 105 | bytes sender = 1; 106 | string name = 2; 107 | string symbol = 3; 108 | int64 total_supply = 4; 109 | uint32 precision = 5; 110 | } 111 | -------------------------------------------------------------------------------- /src/common/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import nacl from "tweetnacl"; 4 | 5 | import { maxUintBits } from "./serialize"; 6 | 7 | const hexRe = /^[0-9a-f]+$/i; 8 | 9 | export function hexToBytes(hex) { 10 | if (hex.length % 2 === 1) { 11 | throw new RangeError("invalid hex string length " + hex.length); 12 | } 13 | if (!hexRe.test(hex)) { 14 | throw new RangeError("invalid hex string"); 15 | } 16 | let bytes = []; 17 | for (let c = 0; c < hex.length; c += 2) { 18 | bytes.push(parseInt(hex.substr(c, 2), 16)); 19 | } 20 | return new Uint8Array(bytes); 21 | } 22 | 23 | export function bytesToHex(bytes) { 24 | return Array.from(bytes, (b) => { 25 | if (b < 0 || b > 255) { 26 | throw new RangeError("invalid byte " + b); 27 | } 28 | return ("0" + (b & 0xff).toString(16)).slice(-2); 29 | }).join(""); 30 | } 31 | 32 | export var randomBytes = nacl.randomBytes; 33 | 34 | export function setPRNG(f) { 35 | nacl.setPRNG(f); 36 | } 37 | 38 | export function randomBytesHex(len) { 39 | return bytesToHex(randomBytes(len)); 40 | } 41 | 42 | export function randomInt32() { 43 | let b = randomBytes(4); 44 | b[0] &= 127; 45 | return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; 46 | } 47 | 48 | export function randomUint64() { 49 | let hex = randomBytesHex(maxUintBits / 8); 50 | return parseInt(hex, 16); 51 | } 52 | 53 | export function mergeTypedArrays(a, b) { 54 | let c = new a.constructor(a.length + b.length); 55 | c.set(a); 56 | c.set(b, a.length); 57 | return c; 58 | } 59 | 60 | export function assignDefined(target, ...sources) { 61 | for (let source of sources) { 62 | if (source) { 63 | for (let key of Object.keys(source)) { 64 | if (source[key] !== undefined) { 65 | target[key] = source[key]; 66 | } 67 | } 68 | } 69 | } 70 | return target; 71 | } 72 | 73 | export function utf8ToBytes(s) { 74 | if (!s) { 75 | return new Uint8Array(); 76 | } 77 | return new Uint8Array(Buffer.from(s, "utf8")); 78 | } 79 | 80 | // convert all keys to lowercase recursively 81 | export function toLowerKeys(obj) { 82 | return Object.keys(obj).reduce( 83 | (merged, key) => 84 | Object.assign(merged, { 85 | [key.toLowerCase()]: 86 | typeof obj[key] === "object" ? toLowerKeys(obj[key]) : obj[key], 87 | }), 88 | {}, 89 | ); 90 | } 91 | 92 | export function sleep(duration) { 93 | return new Promise((resolve) => setTimeout(resolve, duration)); 94 | } 95 | 96 | export function isBrowser() { 97 | return ![typeof window, typeof document].includes("undefined"); 98 | } 99 | -------------------------------------------------------------------------------- /lib/common/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | Object.defineProperty(exports, "Amount", { 8 | enumerable: true, 9 | get: function get() { 10 | return _amount["default"]; 11 | } 12 | }); 13 | Object.defineProperty(exports, "Key", { 14 | enumerable: true, 15 | get: function get() { 16 | return _key["default"]; 17 | } 18 | }); 19 | exports.util = exports.serialize = exports.rpc = exports.pb = exports.key = exports.hash = exports.errors = exports.crypto = exports.aes = void 0; 20 | var _amount = _interopRequireDefault(require("./amount")); 21 | var _key = _interopRequireWildcard(require("./key")); 22 | exports.key = _key; 23 | var _aes = _interopRequireWildcard(require("./aes")); 24 | exports.aes = _aes; 25 | var _crypto = _interopRequireWildcard(require("./crypto")); 26 | exports.crypto = _crypto; 27 | var _errors = _interopRequireWildcard(require("./errors")); 28 | exports.errors = _errors; 29 | var _hash = _interopRequireWildcard(require("./hash")); 30 | exports.hash = _hash; 31 | var _pb = _interopRequireWildcard(require("./pb")); 32 | exports.pb = _pb; 33 | var _rpc = _interopRequireWildcard(require("./rpc")); 34 | exports.rpc = _rpc; 35 | var _serialize = _interopRequireWildcard(require("./serialize")); 36 | exports.serialize = _serialize; 37 | var _util = _interopRequireWildcard(require("./util")); 38 | exports.util = _util; 39 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 40 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 41 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } -------------------------------------------------------------------------------- /docs/assets/style.css: -------------------------------------------------------------------------------- 1 | .documentation { 2 | font-family: Helvetica, sans-serif; 3 | color: #666; 4 | line-height: 1.5; 5 | background: #f5f5f5; 6 | } 7 | 8 | .black { 9 | color: #666; 10 | } 11 | 12 | .bg-white { 13 | background-color: #fff; 14 | } 15 | 16 | h4 { 17 | margin: 20px 0 10px 0; 18 | } 19 | 20 | .documentation h3 { 21 | color: #000; 22 | } 23 | 24 | .border-bottom { 25 | border-color: #ddd; 26 | } 27 | 28 | a { 29 | color: #1184ce; 30 | text-decoration: none; 31 | } 32 | 33 | .documentation a[href]:hover { 34 | text-decoration: underline; 35 | } 36 | 37 | a:hover { 38 | cursor: pointer; 39 | } 40 | 41 | .py1-ul li { 42 | padding: 5px 0; 43 | } 44 | 45 | .max-height-100 { 46 | max-height: 100%; 47 | } 48 | 49 | .height-viewport-100 { 50 | height: 100vh; 51 | } 52 | 53 | section:target h3 { 54 | font-weight: 700; 55 | } 56 | 57 | .documentation td, 58 | .documentation th { 59 | padding: 0.25rem 0.25rem; 60 | } 61 | 62 | h1:hover .anchorjs-link, 63 | h2:hover .anchorjs-link, 64 | h3:hover .anchorjs-link, 65 | h4:hover .anchorjs-link { 66 | opacity: 1; 67 | } 68 | 69 | .fix-3 { 70 | width: 25%; 71 | max-width: 244px; 72 | } 73 | 74 | .fix-3 { 75 | width: 25%; 76 | max-width: 244px; 77 | } 78 | 79 | @media (min-width: 52em) { 80 | .fix-margin-3 { 81 | margin-left: 25%; 82 | } 83 | } 84 | 85 | .pre, 86 | pre, 87 | code, 88 | .code { 89 | font-family: Source Code Pro, Menlo, Consolas, Liberation Mono, monospace; 90 | font-size: 14px; 91 | } 92 | 93 | .fill-light { 94 | background: #f9f9f9; 95 | } 96 | 97 | .width2 { 98 | width: 1rem; 99 | } 100 | 101 | .input { 102 | font-family: inherit; 103 | display: block; 104 | width: 100%; 105 | height: 2rem; 106 | padding: 0.5rem; 107 | margin-bottom: 1rem; 108 | border: 1px solid #ccc; 109 | font-size: 0.875rem; 110 | border-radius: 3px; 111 | box-sizing: border-box; 112 | } 113 | 114 | table { 115 | border-collapse: collapse; 116 | } 117 | 118 | .prose table th, 119 | .prose table td { 120 | text-align: left; 121 | padding: 8px; 122 | border: 1px solid #ddd; 123 | } 124 | 125 | .prose table th:nth-child(1) { 126 | border-right: none; 127 | } 128 | .prose table th:nth-child(2) { 129 | border-left: none; 130 | } 131 | 132 | .prose table { 133 | border: 1px solid #ddd; 134 | } 135 | 136 | .prose-big { 137 | font-size: 18px; 138 | line-height: 30px; 139 | } 140 | 141 | .quiet { 142 | opacity: 0.7; 143 | } 144 | 145 | .minishadow { 146 | box-shadow: 2px 2px 10px #f3f3f3; 147 | } 148 | -------------------------------------------------------------------------------- /__tests__/client.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nkn = require('../lib'); 4 | 5 | class ClientTest { 6 | constructor(name, config) { 7 | this.name = name; 8 | this.config = config; 9 | } 10 | 11 | run() { 12 | console.log('Testing ' + this.name + '...'); 13 | 14 | test(`[${this.name}] create`, async () => { 15 | let Client = this.config.useMultiClient ? nkn.MultiClient : nkn.Client; 16 | this.alice = new Client({ 17 | identifier: 'alice', 18 | numSubClients: this.config.numSubClients, 19 | originalClient: this.config.originalClient, 20 | }); 21 | this.bob = new Client({ 22 | seed: this.alice.getSeed(), 23 | identifier: 'bob', 24 | numSubClients: this.config.numSubClients, 25 | originalClient: this.config.originalClient, 26 | }); 27 | await Promise.all([ 28 | new Promise(resolve => this.alice.onConnect(resolve)), 29 | new Promise(resolve => this.bob.onConnect(resolve)), 30 | ]); 31 | await new Promise(resolve => setTimeout(resolve, 10000)); 32 | expect(this.alice.isReady).toBe(true); 33 | expect(this.bob.isReady).toBe(true); 34 | }); 35 | 36 | test(`[${this.name}] send`, async () => { 37 | let data = this.config.binaryData ? Uint8Array.from([1,2,3,4,5]) : 'hello'; 38 | let reply = this.config.binaryData ? Uint8Array.from([6,7,8,9,0]) : 'world'; 39 | this.bob.onMessage(async ({ src, payload}) => { 40 | expect(src).toBe(this.alice.addr); 41 | expect(payload).toEqual(data); 42 | return reply; 43 | }); 44 | let receivedReply = await this.alice.send(this.bob.addr, data); 45 | expect(receivedReply).toEqual(reply); 46 | }); 47 | 48 | test(`[${this.name}] close`, async () => { 49 | await Promise.all([this.alice.close(), this.bob.close()]); 50 | expect(this.alice.isClosed).toBe(true); 51 | expect(this.bob.isClosed).toBe(true); 52 | }); 53 | } 54 | } 55 | 56 | for (let binaryData of [false, true]) { 57 | new ClientTest(`client (${binaryData ? 'binary' : 'text'})`, { 58 | useMultiClient: false, 59 | binaryData, 60 | }).run(); 61 | 62 | new ClientTest(`multiclient (${binaryData ? 'binary' : 'text'})`, { 63 | useMultiClient: true, 64 | numSubClients: 4, 65 | originalClient: false, 66 | binaryData, 67 | }).run(); 68 | 69 | new ClientTest(`multiclient with original (${binaryData ? 'binary' : 'text'})`, { 70 | useMultiClient: true, 71 | numSubClients: 4, 72 | originalClient: true, 73 | binaryData, 74 | }).run(); 75 | 76 | new ClientTest(`multiclient with only original (${binaryData ? 'binary' : 'text'})`, { 77 | useMultiClient: true, 78 | numSubClients: 0, 79 | originalClient: true, 80 | binaryData, 81 | }).run(); 82 | } 83 | -------------------------------------------------------------------------------- /src/client/webrtc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const PingData = "ping"; 4 | const PongData = "pong"; 5 | 6 | export default class Peer { 7 | config; 8 | pc; // peer connection 9 | dc; // data channel 10 | sdp; 11 | isConnected; 12 | // compatible to websocket 13 | onopen; 14 | onmessage; 15 | onclose; 16 | onerror; 17 | 18 | send(data) { 19 | this.dc.send(data); 20 | } 21 | 22 | constructor(stunServerAddr) { 23 | const config = { 24 | iceServers: [{ urls: stunServerAddr }], 25 | }; 26 | this.config = config; 27 | this.isConnected = false; 28 | } 29 | 30 | async offer(label) { 31 | return new Promise(async (resolve, reject) => { 32 | try { 33 | if (!this.pc || this.pc.signalingState === "closed") { 34 | this.pc = new RTCPeerConnection(this.config); 35 | } 36 | 37 | this.pc.oniceconnectionstatechange = () => { 38 | if (this.pc.iceConnectionState === "failed") { 39 | this.pc.restartIce(); 40 | } 41 | }; 42 | 43 | this.dc = this.pc.createDataChannel(label); 44 | 45 | this.dc.addEventListener("open", () => { 46 | this.isConnected = true; 47 | if (this.onopen) { 48 | this.onopen(); 49 | } 50 | }); 51 | 52 | this.dc.addEventListener("message", (e) => { 53 | if (e.data == PongData) { 54 | if (this.pongHandler != null) { 55 | this.pongHandler(PongData); 56 | } else { 57 | console.log("Pong handler not set"); 58 | } 59 | return; 60 | } else if (e.data == PingData) { 61 | this.dc.send(PongData); 62 | return; 63 | } 64 | if (this.onmessage) { 65 | this.onmessage(e); 66 | } 67 | }); 68 | 69 | this.dc.addEventListener("close", (event) => { 70 | this.isConnected = false; 71 | if (this.onclose) { 72 | this.onclose(); 73 | } 74 | }); 75 | 76 | this.dc.addEventListener("error", (event) => { 77 | if (this.onerror) { 78 | this.onerror(event); 79 | } 80 | }); 81 | 82 | await this.pc.createOffer(); 83 | 84 | await this.pc.setLocalDescription(); 85 | 86 | this.sdp = btoa(JSON.stringify(this.pc.localDescription)); 87 | 88 | resolve(this.sdp); 89 | } catch (err) { 90 | reject(err); 91 | } 92 | }); 93 | } 94 | 95 | setRemoteDescription(sdp) { 96 | const answer = JSON.parse(atob(sdp)); 97 | return this.pc.setRemoteDescription(answer); 98 | } 99 | 100 | close() { 101 | this.dc.close(); 102 | this.pc.close(); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "nkn-sdk", 3 | "version": "1.3.6", 4 | "description": "NKN client and wallet SDK", 5 | "main": "lib/index.js", 6 | "exports": { 7 | ".": { 8 | "import": "./lib/index.js", 9 | "require": "./lib/index.js", 10 | "types": "./index.d.ts" 11 | }, 12 | "./client/*": { 13 | "import": "./lib/client/*.js", 14 | "require": "./lib/client/*.js" 15 | }, 16 | "./common/*": { 17 | "import": "./lib/common/*.js", 18 | "require": "./lib/common/*.js" 19 | }, 20 | "./multiclient/*": { 21 | "import": "./lib/multiclient/*.js", 22 | "require": "./lib/multiclient/*.js" 23 | }, 24 | "./wallet/*": { 25 | "import": "./lib/wallet/*.js", 26 | "require": "./lib/wallet/*.js" 27 | }, 28 | "./worker/*": { 29 | "import": "./lib/worker/*.js", 30 | "require": "./lib/worker/*.js" 31 | } 32 | }, 33 | "types": "index.d.ts", 34 | "scripts": { 35 | "build": "babel src --out-dir lib && browserify lib/index.js -p esmify -s nkn --outfile dist/nkn.js && terser dist/nkn.js -c -m -o dist/nkn.min.js", 36 | "docs": "documentation build src/*/*.js -f html -o docs --sort-order alpha --shallow", 37 | "prepare": "yarn flow && yarn build && yarn docs", 38 | "test": "jest", 39 | "pb": "protoc --js_out=import_style=commonjs,binary:. src/common/pb/*.proto" 40 | }, 41 | "repository": { 42 | "type": "git", 43 | "url": "git+https://github.com/nknorg/nkn-sdk-js.git" 44 | }, 45 | "author": "NKN", 46 | "license": "Apache-2.0", 47 | "bugs": { 48 | "url": "https://github.com/nknorg/nkn-sdk-js/issues" 49 | }, 50 | "homepage": "https://github.com/nknorg/nkn-sdk-js#readme", 51 | "keywords": [ 52 | "nkn", 53 | "p2p", 54 | "peer-to-peer", 55 | "distributed", 56 | "decentralized", 57 | "blockchain", 58 | "realtime", 59 | "network", 60 | "networking", 61 | "streaming" 62 | ], 63 | "dependencies": { 64 | "@babel/polyfill": "^7.12.1", 65 | "@nkn/ncp": "^1.1.2", 66 | "axios": "^1.8.4", 67 | "base-x": "^3.0.7", 68 | "buffer": "^5.4.3", 69 | "core-js": "^3.41.0", 70 | "core-js-pure": "^3.6.4", 71 | "crypto-js": "^4.2.0", 72 | "decimal.js": "^10.2.0", 73 | "ed2curve": "^0.3.0", 74 | "google-protobuf": "^3.11.2", 75 | "heap": "^0.2.6", 76 | "isomorphic-ws": "^4.0.1", 77 | "libsodium-wrappers": "^0.7.6", 78 | "memory-cache": "^0.2.0", 79 | "pako": "^1.0.11", 80 | "scrypt-js": "^3.0.0", 81 | "tweetnacl": "^1.0.3", 82 | "webworkify": "^1.5.0", 83 | "ws": "^7.2.3" 84 | }, 85 | "devDependencies": { 86 | "@babel/cli": "^7.27.0", 87 | "@babel/core": "^7.26.10", 88 | "@babel/plugin-proposal-class-properties": "^7.16.5", 89 | "@babel/plugin-proposal-export-default-from": "^7.16.5", 90 | "@babel/plugin-proposal-export-namespace-from": "^7.16.5", 91 | "@babel/preset-env": "^7.26.9", 92 | "@babel/preset-flow": "^7.16.5", 93 | "browserify": "^17.0.1", 94 | "documentation": "^12.1.4", 95 | "esmify": "^2.1.1", 96 | "flow-bin": "^0.118.0", 97 | "jest": "^25.1.0", 98 | "terser": "^5.39.0" 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | var _exportNames = { 8 | setPRNG: true, 9 | ready: true, 10 | Client: true, 11 | MultiClient: true, 12 | Wallet: true 13 | }; 14 | Object.defineProperty(exports, "Client", { 15 | enumerable: true, 16 | get: function get() { 17 | return _client["default"]; 18 | } 19 | }); 20 | Object.defineProperty(exports, "MultiClient", { 21 | enumerable: true, 22 | get: function get() { 23 | return _multiclient["default"]; 24 | } 25 | }); 26 | Object.defineProperty(exports, "Wallet", { 27 | enumerable: true, 28 | get: function get() { 29 | return _wallet["default"]; 30 | } 31 | }); 32 | exports["default"] = void 0; 33 | Object.defineProperty(exports, "ready", { 34 | enumerable: true, 35 | get: function get() { 36 | return _libsodiumWrappers.ready; 37 | } 38 | }); 39 | exports.setPRNG = void 0; 40 | var _libsodiumWrappers = require("libsodium-wrappers"); 41 | var nkn = _interopRequireWildcard(require("./common")); 42 | Object.keys(nkn).forEach(function (key) { 43 | if (key === "default" || key === "__esModule") return; 44 | if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return; 45 | if (key in exports && exports[key] === nkn[key]) return; 46 | Object.defineProperty(exports, key, { 47 | enumerable: true, 48 | get: function get() { 49 | return nkn[key]; 50 | } 51 | }); 52 | }); 53 | var _client = _interopRequireDefault(require("./client")); 54 | var _multiclient = _interopRequireDefault(require("./multiclient")); 55 | var _wallet = _interopRequireDefault(require("./wallet")); 56 | var address = _interopRequireWildcard(require("./wallet/address")); 57 | var _crypto = require("./common/crypto"); 58 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 59 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 60 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 61 | var setPRNG = exports.setPRNG = nkn.util.setPRNG; 62 | nkn.ready = _libsodiumWrappers.ready; 63 | nkn.Client = _client["default"]; 64 | nkn.MultiClient = _multiclient["default"]; 65 | nkn.Wallet = _wallet["default"]; 66 | nkn.setPRNG = setPRNG; 67 | nkn.address = address; 68 | nkn.setDisableWASM = _crypto.setDisableWASM; 69 | var _default = exports["default"] = nkn; -------------------------------------------------------------------------------- /examples/wallet.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nkn = require("../lib"); 4 | 5 | const password = "42"; 6 | 7 | (async function () { 8 | // random new wallet 9 | let wallet = new nkn.Wallet({ password }); 10 | 11 | // wallet from seed 12 | wallet = new nkn.Wallet({ seed: wallet.getSeed(), password }); 13 | 14 | // save wallet to json 15 | let walletJson = JSON.stringify(wallet); 16 | 17 | // load wallet from json and password 18 | wallet = nkn.Wallet.fromJSON(walletJson, { password }); 19 | 20 | // or load wallet asynchronously to avoid blocking eventloop 21 | wallet = await nkn.Wallet.fromJSON(walletJson, { password, async: true }); 22 | 23 | // verify password of a wallet 24 | console.log("verify password", wallet.verifyPassword(password)); 25 | 26 | // verify password of a wallet asynchronously to avoid blocking eventloop 27 | console.log( 28 | "verify password", 29 | await wallet.verifyPassword(password, { async: true }), 30 | ); 31 | 32 | // verify whether an address is a valid NKN wallet address (static method) 33 | console.log("verify address:", nkn.Wallet.verifyAddress(wallet.address)); 34 | 35 | // get balance of this wallet 36 | console.log("balance:", await wallet.getBalance()); 37 | 38 | // get balance of an address 39 | console.log("balance:", await wallet.getBalance(wallet.address)); 40 | 41 | // get balance of an address (static method) 42 | console.log("balance:", await nkn.Wallet.getBalance(wallet.address)); 43 | 44 | // get nonce for next transaction of this wallet 45 | console.log("nonce:", await wallet.getNonce()); 46 | 47 | // get nonce of an address, does not include nonce in rpc node's transaction pool 48 | console.log( 49 | "nonce:", 50 | await wallet.getNonce(wallet.address, { txPool: false }), 51 | ); 52 | 53 | // get nonce of an address (static method) 54 | console.log( 55 | "nonce:", 56 | await nkn.Wallet.getNonce(wallet.address, { txPool: false }), 57 | ); 58 | 59 | // call below will fail because a new account has no balance 60 | try { 61 | // transfer token to some address 62 | console.log( 63 | "transfer txn hash:", 64 | await wallet.transferTo(wallet.address, 1, { 65 | fee: 0.1, 66 | attrs: "hello world", 67 | }), 68 | ); 69 | 70 | // amount and fee can also be string to prevent accuracy loss 71 | console.log( 72 | "transfer txn hash:", 73 | await wallet.transferTo(wallet.address, "1", { fee: "0.1" }), 74 | ); 75 | 76 | // subscribe to a topic for ths pubkey of the wallet for next 100 blocks 77 | console.log( 78 | "subscribe txn hash:", 79 | await wallet.subscribe("topic", 100, "identifier", "meta", { 80 | fee: "0.1", 81 | }), 82 | ); 83 | 84 | // unsubscribe from a topic 85 | console.log( 86 | "unsubscribe txn hash:", 87 | await wallet.unsubscribe("topic", "identifier", { fee: "0.1" }), 88 | ); 89 | 90 | // register name 91 | console.log("register name txn hash:", await wallet.registerName("name")); 92 | 93 | // transfer name 94 | console.log( 95 | "transfer name txn hash:", 96 | await wallet.transferName("name", wallet.getPublicKey()), 97 | ); 98 | 99 | // delete name 100 | console.log("delete name txn hash:", await wallet.deleteName("name")); 101 | } catch (e) { 102 | console.error(e); 103 | } 104 | })(); 105 | -------------------------------------------------------------------------------- /examples/session.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | const nkn = require("../lib"); 4 | 5 | const numBytes = 16 << 20; 6 | const numSubClients = 4; 7 | const writeChunkSize = 1024; 8 | 9 | (async function () { 10 | let alice = new nkn.MultiClient({ numSubClients, identifier: "alice" }); 11 | let bob = new nkn.MultiClient({ 12 | numSubClients, 13 | identifier: "bob", 14 | seed: alice.getSeed(), 15 | }); 16 | 17 | console.log("Secret seed:", alice.getSeed()); 18 | 19 | await Promise.all([ 20 | new Promise((resolve, reject) => alice.onConnect(resolve)), 21 | new Promise((resolve, reject) => bob.onConnect(resolve)), 22 | ]); 23 | 24 | await new Promise((resolve, reject) => setTimeout(resolve, 1000)); 25 | 26 | bob.onSession(async (session) => { 27 | console.log(session.localAddr, "accepted a sesison"); 28 | await read(session); 29 | }); 30 | 31 | bob.listen(); 32 | console.log("bob listening at", bob.addr); 33 | 34 | let session = await alice.dial(bob.addr); 35 | console.log(session.localAddr, "dialed a session"); 36 | 37 | await write(session, numBytes); 38 | })(); 39 | 40 | async function read(session) { 41 | let timeStart = Date.now(); 42 | let buf = new Uint8Array(0); 43 | while (buf.length < 4) { 44 | buf = nkn.util.mergeTypedArrays(buf, await session.read(4 - buf.length)); 45 | } 46 | let dv = new DataView(buf.buffer); 47 | let numBytes = dv.getUint32(0, true); 48 | for (let n = 0; n < numBytes; n += buf.length) { 49 | buf = await session.read(); 50 | for (let i = 0; i < buf.length; i++) { 51 | if (buf[i] !== byteAt(n + i)) { 52 | throw "wrong value at" + (n + i) + "byte"; 53 | } 54 | } 55 | if ( 56 | Math.floor(((n + buf.length) * 10) / numBytes) !== 57 | Math.floor((n * 10) / numBytes) 58 | ) { 59 | console.log( 60 | session.localAddr, 61 | "received", 62 | n + buf.length, 63 | "bytes", 64 | ((n + buf.length) / (1 << 20) / (Date.now() - timeStart)) * 1000, 65 | "MB/s", 66 | ); 67 | } 68 | } 69 | console.log( 70 | session.localAddr, 71 | "finished receiving", 72 | numBytes, 73 | "bytes", 74 | (numBytes / (1 << 20) / (Date.now() - timeStart)) * 1000, 75 | "MB/s", 76 | ); 77 | process.exit(); 78 | } 79 | 80 | async function write(session, numBytes) { 81 | let timeStart = Date.now(); 82 | let buffer = new ArrayBuffer(4); 83 | let dv = new DataView(buffer); 84 | dv.setUint32(0, numBytes, true); 85 | await session.write(new Uint8Array(buffer)); 86 | let buf; 87 | for (let n = 0; n < numBytes; n += buf.length) { 88 | buf = new Uint8Array(Math.min(numBytes - n, writeChunkSize)); 89 | for (let i = 0; i < buf.length; i++) { 90 | buf[i] = byteAt(n + i); 91 | } 92 | await session.write(buf); 93 | if ( 94 | Math.floor(((n + buf.length) * 10) / numBytes) !== 95 | Math.floor((n * 10) / numBytes) 96 | ) { 97 | console.log( 98 | session.localAddr, 99 | "sent", 100 | n + buf.length, 101 | "bytes", 102 | ((n + buf.length) / (1 << 20) / (Date.now() - timeStart)) * 1000, 103 | "MB/s", 104 | ); 105 | } 106 | } 107 | console.log( 108 | session.localAddr, 109 | "finished sending", 110 | numBytes, 111 | "bytes", 112 | (numBytes / (1 << 20) / (Date.now() - timeStart)) * 1000, 113 | "MB/s", 114 | ); 115 | } 116 | 117 | function byteAt(n) { 118 | return n % 256; 119 | } 120 | -------------------------------------------------------------------------------- /src/wallet/address.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as common from "../common"; 4 | 5 | export const BITCOIN_BASE58 = 6 | "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 7 | const base58 = require("base-x")(BITCOIN_BASE58); 8 | 9 | export const ADDRESS_GEN_PREFIX = "02b825"; 10 | export const ADDRESS_GEN_PREFIX_LEN = ADDRESS_GEN_PREFIX.length / 2; 11 | export const UINT160_LEN = 20; 12 | export const CHECKSUM_LEN = 4; 13 | export const ADDRESS_LEN = ADDRESS_GEN_PREFIX_LEN + UINT160_LEN + CHECKSUM_LEN; 14 | 15 | export function verifyAddress(address) { 16 | try { 17 | let addressBytes = base58.decode(address); 18 | 19 | if (addressBytes.length !== ADDRESS_LEN) { 20 | return false; 21 | } 22 | 23 | let addressPrefixBytes = addressBytes.slice(0, ADDRESS_GEN_PREFIX_LEN); 24 | let addressPrefix = common.util.bytesToHex(addressPrefixBytes); 25 | if (addressPrefix !== ADDRESS_GEN_PREFIX) { 26 | return false; 27 | } 28 | 29 | let programHash = addressStringToProgramHash(address); 30 | let addressVerifyCode = getAddressStringVerifyCode(address); 31 | let programHashVerifyCode = 32 | genAddressVerifyCodeFromProgramHash(programHash); 33 | 34 | return addressVerifyCode === programHashVerifyCode; 35 | } catch (e) { 36 | return false; 37 | } 38 | } 39 | 40 | export function publicKeyToSignatureRedeem(publicKey) { 41 | return "20" + publicKey + "ac"; 42 | } 43 | 44 | export function hexStringToProgramHash(hexStr) { 45 | return common.hash.ripemd160Hex(common.hash.sha256Hex(hexStr)); 46 | } 47 | 48 | export function programHashStringToAddress(programHash) { 49 | let addressVerifyBytes = genAddressVerifyBytesFromProgramHash(programHash); 50 | let addressBaseData = common.util.hexToBytes( 51 | ADDRESS_GEN_PREFIX + programHash, 52 | ); 53 | return base58.encode( 54 | Buffer.from( 55 | common.util.mergeTypedArrays(addressBaseData, addressVerifyBytes), 56 | ), 57 | ); 58 | } 59 | 60 | export function addressStringToProgramHash(address) { 61 | let addressBytes = base58.decode(address); 62 | let programHashBytes = addressBytes.slice( 63 | ADDRESS_GEN_PREFIX_LEN, 64 | addressBytes.length - CHECKSUM_LEN, 65 | ); 66 | return common.util.bytesToHex(programHashBytes); 67 | } 68 | 69 | export function genAddressVerifyBytesFromProgramHash(programHash) { 70 | programHash = ADDRESS_GEN_PREFIX + programHash; 71 | let verifyBytes = common.util.hexToBytes( 72 | common.hash.doubleSha256Hex(programHash), 73 | ); 74 | return verifyBytes.slice(0, CHECKSUM_LEN); 75 | } 76 | 77 | export function genAddressVerifyCodeFromProgramHash(programHash) { 78 | let verifyBytes = genAddressVerifyBytesFromProgramHash(programHash); 79 | return common.util.bytesToHex(verifyBytes); 80 | } 81 | 82 | export function getAddressStringVerifyCode(address) { 83 | let addressBytes = base58.decode(address); 84 | let verifyBytes = addressBytes.slice(-CHECKSUM_LEN); 85 | 86 | return common.util.bytesToHex(verifyBytes); 87 | } 88 | 89 | export function signatureToParameter(signatureHex) { 90 | return "40" + signatureHex; 91 | } 92 | 93 | export function prefixByteCountToHexString(hexString) { 94 | let len = hexString.length; 95 | if (0 === len) { 96 | return "00"; 97 | } 98 | 99 | if (1 === len % 2) { 100 | hexString = "0" + hexString; 101 | len += 1; 102 | } 103 | 104 | let byteCount = len / 2; 105 | 106 | byteCount = byteCount.toString(16); 107 | if (1 === byteCount.length % 2) { 108 | byteCount = "0" + byteCount; 109 | } 110 | 111 | return byteCount + hexString; 112 | } 113 | -------------------------------------------------------------------------------- /lib/common/amount.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var _decimal = require("decimal.js"); 8 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 9 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 10 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 11 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 12 | function _callSuper(t, o, e) { return o = _getPrototypeOf(o), _possibleConstructorReturn(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], _getPrototypeOf(t).constructor) : o.apply(t, e)); } 13 | function _possibleConstructorReturn(t, e) { if (e && ("object" == _typeof(e) || "function" == typeof e)) return e; if (void 0 !== e) throw new TypeError("Derived constructors may only return object or undefined"); return _assertThisInitialized(t); } 14 | function _assertThisInitialized(e) { if (void 0 === e) throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); return e; } 15 | function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); } 16 | function _getPrototypeOf(t) { return _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf.bind() : function (t) { return t.__proto__ || Object.getPrototypeOf(t); }, _getPrototypeOf(t); } 17 | function _inherits(t, e) { if ("function" != typeof e && null !== e) throw new TypeError("Super expression must either be null or a function"); t.prototype = Object.create(e && e.prototype, { constructor: { value: t, writable: !0, configurable: !0 } }), Object.defineProperty(t, "prototype", { writable: !1 }), e && _setPrototypeOf(t, e); } 18 | function _setPrototypeOf(t, e) { return _setPrototypeOf = Object.setPrototypeOf ? Object.setPrototypeOf.bind() : function (t, e) { return t.__proto__ = e, t; }, _setPrototypeOf(t, e); } 19 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 20 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 21 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 22 | _decimal.Decimal.set({ 23 | minE: -8 24 | }); 25 | 26 | /** 27 | * Amount of NKN tokens. See documentation at 28 | * [decimal.js](https://mikemcl.github.io/decimal.js/). 29 | */ 30 | var Amount = exports["default"] = /*#__PURE__*/function (_Decimal) { 31 | function Amount() { 32 | _classCallCheck(this, Amount); 33 | return _callSuper(this, Amount, arguments); 34 | } 35 | _inherits(Amount, _Decimal); 36 | return _createClass(Amount, [{ 37 | key: "value", 38 | value: function value() { 39 | return this.times(Amount.unit).floor(); 40 | } 41 | }]); 42 | }(_decimal.Decimal); 43 | _defineProperty(Amount, "unit", new _decimal.Decimal("100000000")); -------------------------------------------------------------------------------- /lib/wallet/account.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | var address = _interopRequireWildcard(require("./address")); 8 | var common = _interopRequireWildcard(require("../common")); 9 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 10 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 11 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 12 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 13 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 14 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 15 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 16 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 17 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 18 | var Account = exports["default"] = /*#__PURE__*/function () { 19 | function Account(seed) { 20 | var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; 21 | _classCallCheck(this, Account); 22 | _defineProperty(this, "key", void 0); 23 | _defineProperty(this, "signatureRedeem", void 0); 24 | _defineProperty(this, "programHash", void 0); 25 | _defineProperty(this, "address", void 0); 26 | _defineProperty(this, "contract", void 0); 27 | this.key = new common.Key(seed, { 28 | worker: options.worker 29 | }); 30 | this.signatureRedeem = address.publicKeyToSignatureRedeem(this.key.publicKey); 31 | this.programHash = address.hexStringToProgramHash(this.signatureRedeem); 32 | this.address = address.programHashStringToAddress(this.programHash); 33 | this.contract = genAccountContractString(this.signatureRedeem, this.programHash); 34 | } 35 | return _createClass(Account, [{ 36 | key: "getPublicKey", 37 | value: function getPublicKey() { 38 | return this.key.publicKey; 39 | } 40 | }, { 41 | key: "getSeed", 42 | value: function getSeed() { 43 | return this.key.seed; 44 | } 45 | }]); 46 | }(); 47 | function genAccountContractString(signatureRedeem, programHash) { 48 | var contract = address.prefixByteCountToHexString(signatureRedeem); 49 | contract += address.prefixByteCountToHexString("00"); 50 | contract += programHash; 51 | return contract; 52 | } -------------------------------------------------------------------------------- /src/common/crypto.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import ed2curve from "ed2curve"; 4 | import sodium from "libsodium-wrappers"; 5 | import nacl from "tweetnacl"; 6 | 7 | import * as util from "./util"; 8 | 9 | export const keyLength = 32; 10 | export const nonceLength = 24; 11 | export const publicKeyLength = 32; 12 | export const seedLength = 32; 13 | export const signatureLength = 64; 14 | 15 | let isReady = false; 16 | let disableWASM = false; 17 | 18 | export function setDisableWASM(disable) { 19 | disableWASM = !!disable; 20 | } 21 | 22 | export function keyPair(seed) { 23 | let seedBytes = util.hexToBytes(seed); 24 | let key; 25 | if (!disableWASM && isReady) { 26 | try { 27 | key = sodium.crypto_sign_seed_keypair(seedBytes); 28 | return { 29 | seed: seed, 30 | publicKey: key.publicKey, 31 | privateKey: key.privateKey, 32 | curvePrivateKey: ed25519SkToCurve25519(key.privateKey), 33 | }; 34 | } catch (e) { 35 | console.warn(e); 36 | } 37 | } 38 | key = nacl.sign.keyPair.fromSeed(seedBytes); 39 | return { 40 | seed: seed, 41 | publicKey: key.publicKey, 42 | privateKey: key.secretKey, 43 | curvePrivateKey: ed2curve.convertSecretKey(key.secretKey), 44 | }; 45 | } 46 | 47 | export function ed25519SkToCurve25519(sk) { 48 | if (!disableWASM && isReady) { 49 | try { 50 | return sodium.crypto_sign_ed25519_sk_to_curve25519(sk); 51 | } catch (e) { 52 | console.warn(e); 53 | } 54 | } 55 | return ed2curve.convertSecretKey(sk); 56 | } 57 | 58 | export async function ed25519PkToCurve25519(pk) { 59 | if (!disableWASM) { 60 | try { 61 | if (!isReady) { 62 | await sodium.ready; 63 | isReady = true; 64 | } 65 | return sodium.crypto_sign_ed25519_pk_to_curve25519(pk); 66 | } catch (e) { 67 | console.warn(e); 68 | } 69 | } 70 | return ed2curve.convertPublicKey(pk); 71 | } 72 | 73 | export async function computeSharedKey(myCurvePrivateKey, otherPubkey) { 74 | let otherCurvePubkey = await ed25519PkToCurve25519(Buffer.from(otherPubkey, "hex")); 75 | let sharedKey; 76 | if (!disableWASM) { 77 | try { 78 | if (!isReady) { 79 | await sodium.ready; 80 | isReady = true; 81 | } 82 | sharedKey = sodium.crypto_box_beforenm(otherCurvePubkey, myCurvePrivateKey); 83 | } catch (e) { 84 | console.warn(e); 85 | } 86 | } 87 | if (!sharedKey) { 88 | sharedKey = nacl.box.before(otherCurvePubkey, myCurvePrivateKey); 89 | } 90 | return util.bytesToHex(sharedKey); 91 | } 92 | 93 | export async function encryptSymmetric(message, nonce, key) { 94 | if (!disableWASM) { 95 | try { 96 | if (!isReady) { 97 | await sodium.ready; 98 | isReady = true; 99 | } 100 | return sodium.crypto_box_easy_afternm(message, nonce, key); 101 | } catch (e) { 102 | console.warn(e); 103 | } 104 | } 105 | return nacl.secretbox(message, nonce, key); 106 | } 107 | 108 | export async function decryptSymmetric(message, nonce, key) { 109 | if (!disableWASM) { 110 | try { 111 | if (!isReady) { 112 | await sodium.ready; 113 | isReady = true; 114 | } 115 | return sodium.crypto_box_open_easy_afternm(message, nonce, key); 116 | } catch (e) { 117 | console.warn(e); 118 | } 119 | } 120 | return nacl.secretbox.open(message, nonce, key); 121 | } 122 | 123 | export async function sign(privateKey, message) { 124 | let sig; 125 | if (!disableWASM) { 126 | try { 127 | if (!isReady) { 128 | await sodium.ready; 129 | isReady = true; 130 | } 131 | sig = sodium.crypto_sign_detached(Buffer.from(message, "hex"), privateKey); 132 | return util.bytesToHex(sig); 133 | } catch (e) { 134 | console.warn(e); 135 | } 136 | } 137 | sig = nacl.sign.detached(Buffer.from(message, "hex"), privateKey); 138 | return util.bytesToHex(sig); 139 | } 140 | -------------------------------------------------------------------------------- /lib/common/util.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports.assignDefined = assignDefined; 7 | exports.bytesToHex = bytesToHex; 8 | exports.hexToBytes = hexToBytes; 9 | exports.isBrowser = isBrowser; 10 | exports.mergeTypedArrays = mergeTypedArrays; 11 | exports.randomBytes = void 0; 12 | exports.randomBytesHex = randomBytesHex; 13 | exports.randomInt32 = randomInt32; 14 | exports.randomUint64 = randomUint64; 15 | exports.setPRNG = setPRNG; 16 | exports.sleep = sleep; 17 | exports.toLowerKeys = toLowerKeys; 18 | exports.utf8ToBytes = utf8ToBytes; 19 | var _tweetnacl = _interopRequireDefault(require("tweetnacl")); 20 | var _serialize = require("./serialize"); 21 | function _interopRequireDefault(e) { return e && e.__esModule ? e : { "default": e }; } 22 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 23 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 24 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 25 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 26 | var hexRe = /^[0-9a-f]+$/i; 27 | function hexToBytes(hex) { 28 | if (hex.length % 2 === 1) { 29 | throw new RangeError("invalid hex string length " + hex.length); 30 | } 31 | if (!hexRe.test(hex)) { 32 | throw new RangeError("invalid hex string"); 33 | } 34 | var bytes = []; 35 | for (var c = 0; c < hex.length; c += 2) { 36 | bytes.push(parseInt(hex.substr(c, 2), 16)); 37 | } 38 | return new Uint8Array(bytes); 39 | } 40 | function bytesToHex(bytes) { 41 | return Array.from(bytes, function (b) { 42 | if (b < 0 || b > 255) { 43 | throw new RangeError("invalid byte " + b); 44 | } 45 | return ("0" + (b & 0xff).toString(16)).slice(-2); 46 | }).join(""); 47 | } 48 | var randomBytes = exports.randomBytes = _tweetnacl["default"].randomBytes; 49 | function setPRNG(f) { 50 | _tweetnacl["default"].setPRNG(f); 51 | } 52 | function randomBytesHex(len) { 53 | return bytesToHex(randomBytes(len)); 54 | } 55 | function randomInt32() { 56 | var b = randomBytes(4); 57 | b[0] &= 127; 58 | return (b[0] << 24) + (b[1] << 16) + (b[2] << 8) + b[3]; 59 | } 60 | function randomUint64() { 61 | var hex = randomBytesHex(_serialize.maxUintBits / 8); 62 | return parseInt(hex, 16); 63 | } 64 | function mergeTypedArrays(a, b) { 65 | var c = new a.constructor(a.length + b.length); 66 | c.set(a); 67 | c.set(b, a.length); 68 | return c; 69 | } 70 | function assignDefined(target) { 71 | for (var _len = arguments.length, sources = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { 72 | sources[_key - 1] = arguments[_key]; 73 | } 74 | for (var _i = 0, _sources = sources; _i < _sources.length; _i++) { 75 | var source = _sources[_i]; 76 | if (source) { 77 | for (var _i2 = 0, _Object$keys = Object.keys(source); _i2 < _Object$keys.length; _i2++) { 78 | var key = _Object$keys[_i2]; 79 | if (source[key] !== undefined) { 80 | target[key] = source[key]; 81 | } 82 | } 83 | } 84 | } 85 | return target; 86 | } 87 | function utf8ToBytes(s) { 88 | if (!s) { 89 | return new Uint8Array(); 90 | } 91 | return new Uint8Array(Buffer.from(s, "utf8")); 92 | } 93 | 94 | // convert all keys to lowercase recursively 95 | function toLowerKeys(obj) { 96 | return Object.keys(obj).reduce(function (merged, key) { 97 | return Object.assign(merged, _defineProperty({}, key.toLowerCase(), _typeof(obj[key]) === "object" ? toLowerKeys(obj[key]) : obj[key])); 98 | }, {}); 99 | } 100 | function sleep(duration) { 101 | return new Promise(function (resolve) { 102 | return setTimeout(resolve, duration); 103 | }); 104 | } 105 | function isBrowser() { 106 | return ![typeof window === "undefined" ? "undefined" : _typeof(window), typeof document === "undefined" ? "undefined" : _typeof(document)].includes("undefined"); 107 | } -------------------------------------------------------------------------------- /src/common/key.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import work from "webworkify"; 4 | 5 | import * as crypto from "./crypto"; 6 | import * as errors from "./errors"; 7 | import * as util from "./util"; 8 | 9 | export default class Key { 10 | seed; 11 | publicKey; 12 | privateKey; 13 | curvePrivateKey; 14 | sharedKeyCache; 15 | useWorker; 16 | worker; 17 | workerMsgID; 18 | workerMsgCache; 19 | 20 | constructor(seed, options = {}) { 21 | if (!seed) { 22 | seed = util.randomBytesHex(crypto.seedLength); 23 | } 24 | 25 | let key = crypto.keyPair(seed); 26 | this.seed = seed; 27 | this.publicKey = util.bytesToHex(key.publicKey); 28 | this.privateKey = key.privateKey; 29 | this.curvePrivateKey = key.curvePrivateKey; 30 | this.sharedKeyCache = new Map(); 31 | this.useWorker = this._shouldUseWorker(options.worker); 32 | this.worker = null; 33 | this.workerMsgID = 0; 34 | this.workerMsgCache = new Map(); 35 | 36 | if (this.useWorker) { 37 | (async () => { 38 | try { 39 | if (typeof options.worker === "function") { 40 | this.worker = await options.worker(); 41 | } else { 42 | try { 43 | this.worker = work(require("../worker/worker.js")); 44 | } catch (e) { 45 | try { 46 | let Worker = require("../worker/webpack.worker.js"); 47 | this.worker = new Worker(); 48 | } catch (e) { 49 | throw "neither browserify nor webpack worker-loader is detected"; 50 | } 51 | } 52 | } 53 | this.worker.onmessage = (e) => { 54 | if (e.data.id !== undefined && this.workerMsgCache.has(e.data.id)) { 55 | let msgPromise = this.workerMsgCache.get(e.data.id); 56 | if (e.data.error) { 57 | msgPromise.reject(e.data.error); 58 | } else { 59 | msgPromise.resolve(e.data.result); 60 | } 61 | this.workerMsgCache.delete(e.data.id); 62 | } 63 | }; 64 | await this._sendToWorker({ action: "setSeed", seed: this.seed }); 65 | } catch (e) { 66 | console.warn("Launch web worker failed:", e); 67 | this.useWorker = false; 68 | } 69 | })(); 70 | } 71 | } 72 | 73 | _shouldUseWorker(useWorker) { 74 | if (!useWorker) { 75 | return false; 76 | } 77 | if (typeof window === "undefined") { 78 | return false; 79 | } 80 | if (!window.Worker) { 81 | return false; 82 | } 83 | return true; 84 | } 85 | 86 | _sendToWorker(data) { 87 | return new Promise((resolve, reject) => { 88 | let id = this.workerMsgID; 89 | this.workerMsgID++; 90 | this.workerMsgCache.set(id, { resolve, reject }); 91 | this.worker.postMessage(Object.assign({ id }, data)); 92 | }); 93 | } 94 | 95 | async computeSharedKey(otherPubkey) { 96 | if (this.useWorker) { 97 | try { 98 | return await this._sendToWorker({ 99 | action: "computeSharedKey", 100 | otherPubkey, 101 | }); 102 | } catch (e) { 103 | console.warn( 104 | "worker computeSharedKey failed, fallback to main thread:", 105 | e, 106 | ); 107 | } 108 | } 109 | return await crypto.computeSharedKey(this.curvePrivateKey, otherPubkey); 110 | } 111 | 112 | async getOrComputeSharedKey(otherPubkey) { 113 | let sharedKey = this.sharedKeyCache.get(otherPubkey); 114 | if (!sharedKey) { 115 | sharedKey = await this.computeSharedKey(otherPubkey); 116 | this.sharedKeyCache.set(otherPubkey, sharedKey); 117 | } 118 | return sharedKey; 119 | } 120 | 121 | async encrypt(message, destPubkey, options = {}) { 122 | let sharedKey = await this.getOrComputeSharedKey(destPubkey); 123 | sharedKey = Buffer.from(sharedKey, "hex"); 124 | let nonce = options.nonce || util.randomBytes(crypto.nonceLength); 125 | return { 126 | message: await crypto.encryptSymmetric(message, nonce, sharedKey), 127 | nonce: nonce, 128 | }; 129 | } 130 | 131 | async decrypt(message, nonce, srcPubkey, options = {}) { 132 | let sharedKey = await this.getOrComputeSharedKey(srcPubkey); 133 | sharedKey = Buffer.from(sharedKey, "hex"); 134 | return await crypto.decryptSymmetric(message, nonce, sharedKey); 135 | } 136 | 137 | async sign(message) { 138 | if (this.useWorker) { 139 | try { 140 | return await this._sendToWorker({ action: "sign", message }); 141 | } catch (e) { 142 | console.warn("worker sign failed, fallback to main thread:", e); 143 | } 144 | } 145 | return await crypto.sign(this.privateKey, message); 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /examples/webrtc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | WebRTC example 8 | 9 | 10 | 11 |

WebRTC example

12 |

13 | This example demonstrates how to use WebRTC to send messages between 14 | two clients, Alice and Bob. 15 |

16 |

17 | If connect to local NKN node, please uncomment the line of 18 | rpcServerAddr in the code. 19 |

20 | 21 | 27 |
28 | 29 | 30 | 125 | 126 | -------------------------------------------------------------------------------- /docs/assets/fonts/LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark of Adobe Systems Incorporated in the United States and/or other countries. 2 | 3 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 4 | 5 | This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL 6 | 7 | 8 | ----------------------------------------------------------- 9 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 10 | ----------------------------------------------------------- 11 | 12 | PREAMBLE 13 | The goals of the Open Font License (OFL) are to stimulate worldwide 14 | development of collaborative font projects, to support the font creation 15 | efforts of academic and linguistic communities, and to provide a free and 16 | open framework in which fonts may be shared and improved in partnership 17 | with others. 18 | 19 | The OFL allows the licensed fonts to be used, studied, modified and 20 | redistributed freely as long as they are not sold by themselves. The 21 | fonts, including any derivative works, can be bundled, embedded, 22 | redistributed and/or sold with any software provided that any reserved 23 | names are not used by derivative works. The fonts and derivatives, 24 | however, cannot be released under any other type of license. The 25 | requirement for fonts to remain under this license does not apply 26 | to any document created using the fonts or their derivatives. 27 | 28 | DEFINITIONS 29 | "Font Software" refers to the set of files released by the Copyright 30 | Holder(s) under this license and clearly marked as such. This may 31 | include source files, build scripts and documentation. 32 | 33 | "Reserved Font Name" refers to any names specified as such after the 34 | copyright statement(s). 35 | 36 | "Original Version" refers to the collection of Font Software components as 37 | distributed by the Copyright Holder(s). 38 | 39 | "Modified Version" refers to any derivative made by adding to, deleting, 40 | or substituting -- in part or in whole -- any of the components of the 41 | Original Version, by changing formats or by porting the Font Software to a 42 | new environment. 43 | 44 | "Author" refers to any designer, engineer, programmer, technical 45 | writer or other person who contributed to the Font Software. 46 | 47 | PERMISSION & CONDITIONS 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 50 | redistribute, and sell modified and unmodified copies of the Font 51 | Software, subject to the following conditions: 52 | 53 | 1) Neither the Font Software nor any of its individual components, 54 | in Original or Modified Versions, may be sold by itself. 55 | 56 | 2) Original or Modified Versions of the Font Software may be bundled, 57 | redistributed and/or sold with any software, provided that each copy 58 | contains the above copyright notice and this license. These can be 59 | included either as stand-alone text files, human-readable headers or 60 | in the appropriate machine-readable metadata fields within text or 61 | binary files as long as those fields can be easily viewed by the user. 62 | 63 | 3) No Modified Version of the Font Software may use the Reserved Font 64 | Name(s) unless explicit written permission is granted by the corresponding 65 | Copyright Holder. This restriction only applies to the primary font name as 66 | presented to the users. 67 | 68 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 69 | Software shall not be used to promote, endorse or advertise any 70 | Modified Version, except to acknowledge the contribution(s) of the 71 | Copyright Holder(s) and the Author(s) or with their explicit written 72 | permission. 73 | 74 | 5) The Font Software, modified or unmodified, in part or in whole, 75 | must be distributed entirely under this license, and must not be 76 | distributed under any other license. The requirement for fonts to 77 | remain under this license does not apply to any document created 78 | using the Font Software. 79 | 80 | TERMINATION 81 | This license becomes null and void if any of the above conditions are 82 | not met. 83 | 84 | DISCLAIMER 85 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 86 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 87 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 88 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 89 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 90 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 91 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 92 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 93 | OTHER DEALINGS IN THE FONT SOFTWARE. 94 | -------------------------------------------------------------------------------- /lib/wallet/address.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | Object.defineProperty(exports, "__esModule", { 5 | value: true 6 | }); 7 | exports.UINT160_LEN = exports.CHECKSUM_LEN = exports.BITCOIN_BASE58 = exports.ADDRESS_LEN = exports.ADDRESS_GEN_PREFIX_LEN = exports.ADDRESS_GEN_PREFIX = void 0; 8 | exports.addressStringToProgramHash = addressStringToProgramHash; 9 | exports.genAddressVerifyBytesFromProgramHash = genAddressVerifyBytesFromProgramHash; 10 | exports.genAddressVerifyCodeFromProgramHash = genAddressVerifyCodeFromProgramHash; 11 | exports.getAddressStringVerifyCode = getAddressStringVerifyCode; 12 | exports.hexStringToProgramHash = hexStringToProgramHash; 13 | exports.prefixByteCountToHexString = prefixByteCountToHexString; 14 | exports.programHashStringToAddress = programHashStringToAddress; 15 | exports.publicKeyToSignatureRedeem = publicKeyToSignatureRedeem; 16 | exports.signatureToParameter = signatureToParameter; 17 | exports.verifyAddress = verifyAddress; 18 | var common = _interopRequireWildcard(require("../common")); 19 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 20 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 21 | var BITCOIN_BASE58 = exports.BITCOIN_BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 22 | var base58 = require("base-x")(BITCOIN_BASE58); 23 | var ADDRESS_GEN_PREFIX = exports.ADDRESS_GEN_PREFIX = "02b825"; 24 | var ADDRESS_GEN_PREFIX_LEN = exports.ADDRESS_GEN_PREFIX_LEN = ADDRESS_GEN_PREFIX.length / 2; 25 | var UINT160_LEN = exports.UINT160_LEN = 20; 26 | var CHECKSUM_LEN = exports.CHECKSUM_LEN = 4; 27 | var ADDRESS_LEN = exports.ADDRESS_LEN = ADDRESS_GEN_PREFIX_LEN + UINT160_LEN + CHECKSUM_LEN; 28 | function verifyAddress(address) { 29 | try { 30 | var addressBytes = base58.decode(address); 31 | if (addressBytes.length !== ADDRESS_LEN) { 32 | return false; 33 | } 34 | var addressPrefixBytes = addressBytes.slice(0, ADDRESS_GEN_PREFIX_LEN); 35 | var addressPrefix = common.util.bytesToHex(addressPrefixBytes); 36 | if (addressPrefix !== ADDRESS_GEN_PREFIX) { 37 | return false; 38 | } 39 | var programHash = addressStringToProgramHash(address); 40 | var addressVerifyCode = getAddressStringVerifyCode(address); 41 | var programHashVerifyCode = genAddressVerifyCodeFromProgramHash(programHash); 42 | return addressVerifyCode === programHashVerifyCode; 43 | } catch (e) { 44 | return false; 45 | } 46 | } 47 | function publicKeyToSignatureRedeem(publicKey) { 48 | return "20" + publicKey + "ac"; 49 | } 50 | function hexStringToProgramHash(hexStr) { 51 | return common.hash.ripemd160Hex(common.hash.sha256Hex(hexStr)); 52 | } 53 | function programHashStringToAddress(programHash) { 54 | var addressVerifyBytes = genAddressVerifyBytesFromProgramHash(programHash); 55 | var addressBaseData = common.util.hexToBytes(ADDRESS_GEN_PREFIX + programHash); 56 | return base58.encode(Buffer.from(common.util.mergeTypedArrays(addressBaseData, addressVerifyBytes))); 57 | } 58 | function addressStringToProgramHash(address) { 59 | var addressBytes = base58.decode(address); 60 | var programHashBytes = addressBytes.slice(ADDRESS_GEN_PREFIX_LEN, addressBytes.length - CHECKSUM_LEN); 61 | return common.util.bytesToHex(programHashBytes); 62 | } 63 | function genAddressVerifyBytesFromProgramHash(programHash) { 64 | programHash = ADDRESS_GEN_PREFIX + programHash; 65 | var verifyBytes = common.util.hexToBytes(common.hash.doubleSha256Hex(programHash)); 66 | return verifyBytes.slice(0, CHECKSUM_LEN); 67 | } 68 | function genAddressVerifyCodeFromProgramHash(programHash) { 69 | var verifyBytes = genAddressVerifyBytesFromProgramHash(programHash); 70 | return common.util.bytesToHex(verifyBytes); 71 | } 72 | function getAddressStringVerifyCode(address) { 73 | var addressBytes = base58.decode(address); 74 | var verifyBytes = addressBytes.slice(-CHECKSUM_LEN); 75 | return common.util.bytesToHex(verifyBytes); 76 | } 77 | function signatureToParameter(signatureHex) { 78 | return "40" + signatureHex; 79 | } 80 | function prefixByteCountToHexString(hexString) { 81 | var len = hexString.length; 82 | if (0 === len) { 83 | return "00"; 84 | } 85 | if (1 === len % 2) { 86 | hexString = "0" + hexString; 87 | len += 1; 88 | } 89 | var byteCount = len / 2; 90 | byteCount = byteCount.toString(16); 91 | if (1 === byteCount.length % 2) { 92 | byteCount = "0" + byteCount; 93 | } 94 | return byteCount + hexString; 95 | } -------------------------------------------------------------------------------- /docs/assets/site.js: -------------------------------------------------------------------------------- 1 | /* global anchors */ 2 | 3 | // add anchor links to headers 4 | anchors.options.placement = 'left'; 5 | anchors.add('h3'); 6 | 7 | // Filter UI 8 | var tocElements = document.getElementById('toc').getElementsByTagName('li'); 9 | 10 | document.getElementById('filter-input').addEventListener('keyup', function(e) { 11 | var i, element, children; 12 | 13 | // enter key 14 | if (e.keyCode === 13) { 15 | // go to the first displayed item in the toc 16 | for (i = 0; i < tocElements.length; i++) { 17 | element = tocElements[i]; 18 | if (!element.classList.contains('display-none')) { 19 | location.replace(element.firstChild.href); 20 | return e.preventDefault(); 21 | } 22 | } 23 | } 24 | 25 | var match = function() { 26 | return true; 27 | }; 28 | 29 | var value = this.value.toLowerCase(); 30 | 31 | if (!value.match(/^\s*$/)) { 32 | match = function(element) { 33 | var html = element.firstChild.innerHTML; 34 | return html && html.toLowerCase().indexOf(value) !== -1; 35 | }; 36 | } 37 | 38 | for (i = 0; i < tocElements.length; i++) { 39 | element = tocElements[i]; 40 | children = Array.from(element.getElementsByTagName('li')); 41 | if (match(element) || children.some(match)) { 42 | element.classList.remove('display-none'); 43 | } else { 44 | element.classList.add('display-none'); 45 | } 46 | } 47 | }); 48 | 49 | var items = document.getElementsByClassName('toggle-sibling'); 50 | for (var j = 0; j < items.length; j++) { 51 | items[j].addEventListener('click', toggleSibling); 52 | } 53 | 54 | function toggleSibling() { 55 | var stepSibling = this.parentNode.getElementsByClassName('toggle-target')[0]; 56 | var icon = this.getElementsByClassName('icon')[0]; 57 | var klass = 'display-none'; 58 | if (stepSibling.classList.contains(klass)) { 59 | stepSibling.classList.remove(klass); 60 | icon.innerHTML = '▾'; 61 | } else { 62 | stepSibling.classList.add(klass); 63 | icon.innerHTML = '▸'; 64 | } 65 | } 66 | 67 | function showHashTarget(targetId) { 68 | if (targetId) { 69 | var hashTarget = document.getElementById(targetId); 70 | // new target is hidden 71 | if ( 72 | hashTarget && 73 | hashTarget.offsetHeight === 0 && 74 | hashTarget.parentNode.parentNode.classList.contains('display-none') 75 | ) { 76 | hashTarget.parentNode.parentNode.classList.remove('display-none'); 77 | } 78 | } 79 | } 80 | 81 | function scrollIntoView(targetId) { 82 | // Only scroll to element if we don't have a stored scroll position. 83 | if (targetId && !history.state) { 84 | var hashTarget = document.getElementById(targetId); 85 | if (hashTarget) { 86 | hashTarget.scrollIntoView(); 87 | } 88 | } 89 | } 90 | 91 | function gotoCurrentTarget() { 92 | showHashTarget(location.hash.substring(1)); 93 | scrollIntoView(location.hash.substring(1)); 94 | } 95 | 96 | window.addEventListener('hashchange', gotoCurrentTarget); 97 | gotoCurrentTarget(); 98 | 99 | var toclinks = document.getElementsByClassName('pre-open'); 100 | for (var k = 0; k < toclinks.length; k++) { 101 | toclinks[k].addEventListener('mousedown', preOpen, false); 102 | } 103 | 104 | function preOpen() { 105 | showHashTarget(this.hash.substring(1)); 106 | } 107 | 108 | var split_left = document.querySelector('#split-left'); 109 | var split_right = document.querySelector('#split-right'); 110 | var split_parent = split_left.parentNode; 111 | var cw_with_sb = split_left.clientWidth; 112 | split_left.style.overflow = 'hidden'; 113 | var cw_without_sb = split_left.clientWidth; 114 | split_left.style.overflow = ''; 115 | 116 | Split(['#split-left', '#split-right'], { 117 | elementStyle: function(dimension, size, gutterSize) { 118 | return { 119 | 'flex-basis': 'calc(' + size + '% - ' + gutterSize + 'px)' 120 | }; 121 | }, 122 | gutterStyle: function(dimension, gutterSize) { 123 | return { 124 | 'flex-basis': gutterSize + 'px' 125 | }; 126 | }, 127 | gutterSize: 20, 128 | sizes: [33, 67] 129 | }); 130 | 131 | // Chrome doesn't remember scroll position properly so do it ourselves. 132 | // Also works on Firefox and Edge. 133 | 134 | function updateState() { 135 | history.replaceState( 136 | { 137 | left_top: split_left.scrollTop, 138 | right_top: split_right.scrollTop 139 | }, 140 | document.title 141 | ); 142 | } 143 | 144 | function loadState(ev) { 145 | if (ev) { 146 | // Edge doesn't replace change history.state on popstate. 147 | history.replaceState(ev.state, document.title); 148 | } 149 | if (history.state) { 150 | split_left.scrollTop = history.state.left_top; 151 | split_right.scrollTop = history.state.right_top; 152 | } 153 | } 154 | 155 | window.addEventListener('load', function() { 156 | // Restore after Firefox scrolls to hash. 157 | setTimeout(function() { 158 | loadState(); 159 | // Update with initial scroll position. 160 | updateState(); 161 | // Update scroll positions only after we've loaded because Firefox 162 | // emits an initial scroll event with 0. 163 | split_left.addEventListener('scroll', updateState); 164 | split_right.addEventListener('scroll', updateState); 165 | }, 1); 166 | }); 167 | 168 | window.addEventListener('popstate', loadState); 169 | -------------------------------------------------------------------------------- /__tests__/wallet.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nkn = require('../lib'); 4 | 5 | const password = '42'; 6 | 7 | test('from seed', () => { 8 | let wallet = new nkn.Wallet({ password: '42' }); 9 | let walletFromSeed = new nkn.Wallet({ seed: wallet.getSeed(), password: '42' }); 10 | expect(walletFromSeed.address).toBe(wallet.address); 11 | }); 12 | 13 | test('from/to json v1', () => { 14 | let wallet = new nkn.Wallet({ password: '42', version: 1 }); 15 | let walletFromJson = nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '42' }); 16 | expect(walletFromJson.address).toBe(wallet.address); 17 | expect(() => { 18 | nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '233' }); 19 | }).toThrow(); 20 | }); 21 | 22 | test('from/to json v2', () => { 23 | let wallet = new nkn.Wallet({ password: '42', version: 2 }); 24 | let walletFromJson = nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '42' }); 25 | expect(walletFromJson.address).toBe(wallet.address); 26 | expect(() => { 27 | nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '233' }); 28 | }).toThrow(); 29 | }); 30 | 31 | test('from json async v1', async () => { 32 | let wallet = new nkn.Wallet({ password: '42', version: 1 }); 33 | let walletFromJson = await nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '42', async: true }); 34 | expect(walletFromJson.address).toBe(wallet.address); 35 | expect(nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '233', async: true })).rejects.toEqual(new nkn.errors.WrongPasswordError()) 36 | }); 37 | 38 | test('from json async v2', async () => { 39 | let wallet = new nkn.Wallet({ password: '42', verison: 2 }); 40 | let walletFromJson = await nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '42', async: true }); 41 | expect(walletFromJson.address).toBe(wallet.address); 42 | expect(nkn.Wallet.fromJSON(JSON.stringify(wallet), { password: '233', async: true })).rejects.toEqual(new nkn.errors.WrongPasswordError()) 43 | }); 44 | 45 | test('verify password', () => { 46 | let wallet = new nkn.Wallet({ password: '42' }); 47 | expect(wallet.verifyPassword('42')).toBe(true); 48 | expect(wallet.verifyPassword('233')).toBe(false); 49 | }); 50 | 51 | test('verify password async', async () => { 52 | let wallet = new nkn.Wallet({ password: '42' }); 53 | expect(await wallet.verifyPassword('42', { async: true })).toBe(true); 54 | expect(await wallet.verifyPassword('233', { async: true })).toBe(false); 55 | }); 56 | 57 | test('verify address', () => { 58 | let wallet = new nkn.Wallet({ password: '42' }); 59 | expect(nkn.Wallet.verifyAddress(wallet.address)).toBe(true); 60 | expect(nkn.Wallet.verifyAddress(wallet.address.slice(1))).toBe(false); 61 | expect(nkn.Wallet.verifyAddress(wallet.address.slice(0, -1))).toBe(false); 62 | }); 63 | 64 | test('get balance', async () => { 65 | let wallet = new nkn.Wallet({ password: '42' }); 66 | expect((await wallet.getBalance()).toString()).toBe('0'); 67 | expect((await wallet.getBalance(wallet.address)).toString()).toBe('0'); 68 | expect((await nkn.Wallet.getBalance(wallet.address)).toString()).toBe('0'); 69 | }); 70 | 71 | test('get nonce', async () => { 72 | let wallet = new nkn.Wallet({ password: '42' }); 73 | expect(await wallet.getNonce()).toBe(0); 74 | expect(await wallet.getNonce(wallet.address)).toBe(0); 75 | expect(await nkn.Wallet.getNonce(wallet.address)).toBe(0); 76 | }); 77 | 78 | test('transfer', async () => { 79 | let wallet = new nkn.Wallet({ password: '42' }); 80 | try { 81 | await wallet.transferTo(wallet.address, '1', { fee: '0.1', attrs: 'hello world' }); 82 | } catch (e) { 83 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 84 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 85 | } 86 | }); 87 | 88 | test('subscribe', async () => { 89 | let wallet = new nkn.Wallet({ password: '42' }); 90 | try { 91 | await wallet.subscribe('topic', 100, 'identifier', 'meta', { fee: '0.1' }); 92 | } catch (e) { 93 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 94 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 95 | } 96 | }); 97 | 98 | test('unsubscribe', async () => { 99 | let wallet = new nkn.Wallet({ password: '42' }); 100 | try { 101 | await wallet.unsubscribe('topic', 'identifier'); 102 | } catch (e) { 103 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 104 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 105 | } 106 | }); 107 | 108 | test('register name', async () => { 109 | let wallet = new nkn.Wallet({ password: '42' }); 110 | try { 111 | await wallet.registerName(wallet.address); 112 | } catch (e) { 113 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 114 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 115 | } 116 | }); 117 | 118 | test('transfer name', async () => { 119 | let wallet = new nkn.Wallet({ password: '42' }); 120 | try { 121 | await wallet.transferName(wallet.address, new nkn.Wallet({ password: '42' }).getPublicKey()); 122 | } catch (e) { 123 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 124 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 125 | } 126 | }); 127 | 128 | test('delete name', async () => { 129 | let wallet = new nkn.Wallet({ password: '42' }); 130 | try { 131 | await wallet.deleteName(wallet.address); 132 | } catch (e) { 133 | expect(e).toBeInstanceOf(nkn.errors.ServerError); 134 | expect(e.code).toBe(nkn.errors.rpcRespErrCodes.appendTxnPool); 135 | } 136 | }); 137 | -------------------------------------------------------------------------------- /__tests__/session.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const nkn = require('../lib'); 4 | 5 | class SessionTest { 6 | constructor(name, config) { 7 | this.name = name; 8 | this.config = config; 9 | } 10 | 11 | byteAt(n) { 12 | return n % 256; 13 | } 14 | 15 | async read(session) { 16 | let timeStart = Date.now(); 17 | let buf = new Uint8Array(0); 18 | while (buf.length < 4) { 19 | buf = nkn.util.mergeTypedArrays(buf, await session.read(4 - buf.length)); 20 | } 21 | let dv = new DataView(buf.buffer); 22 | let numBytes = dv.getUint32(0, true); 23 | for (let n = 0; n < numBytes; n += buf.length) { 24 | buf = await session.read(); 25 | for (let i = 0; i < buf.length; i++) { 26 | if (buf[i] !== this.byteAt(n + i)) { 27 | throw 'wrong value at byte ' + (n + i); 28 | } 29 | } 30 | if (this.config.verbose && Math.floor((n + buf.length) * 10 / numBytes) !== Math.floor(n * 10 / numBytes)) { 31 | console.log(session.localAddr, 'received', n + buf.length, 'bytes', (n + buf.length) / (1<<20) / (Date.now() - timeStart) * 1000, 'MB/s'); 32 | } 33 | } 34 | console.log(session.localAddr, 'finished receiving', numBytes, 'bytes', numBytes / (1<<20) / (Date.now() - timeStart) * 1000, 'MB/s'); 35 | } 36 | 37 | async write(session, numBytes) { 38 | let timeStart = Date.now(); 39 | let buffer = new ArrayBuffer(4); 40 | let dv = new DataView(buffer); 41 | dv.setUint32(0, numBytes, true); 42 | await session.write(new Uint8Array(buffer)); 43 | let buf; 44 | for (let n = 0; n < numBytes; n += buf.length) { 45 | buf = new Uint8Array(Math.min(numBytes - n, this.config.writeChunkSize)); 46 | for (let i = 0; i < buf.length; i++) { 47 | buf[i] = this.byteAt(n + i); 48 | } 49 | await session.write(buf); 50 | if (this.config.verbose && Math.floor((n + buf.length) * 10 / numBytes) !== Math.floor(n * 10 / numBytes)) { 51 | console.log(session.localAddr, 'sent', n + buf.length, 'bytes', (n + buf.length) / (1<<20) / (Date.now() - timeStart) * 1000, 'MB/s'); 52 | } 53 | } 54 | console.log(session.localAddr, 'finished sending', numBytes, 'bytes', numBytes / (1<<20) / (Date.now() - timeStart) * 1000, 'MB/s'); 55 | } 56 | 57 | run() { 58 | console.log('Testing ' + this.name + '...'); 59 | 60 | test(`[${this.name}] create`, async () => { 61 | this.alice = new nkn.MultiClient({ 62 | identifier: 'alice', 63 | numSubClients: this.config.numClients - (this.config.originalClient ? 1 : 0), 64 | originalClient: this.config.originalClient, 65 | sessionConfig: this.config.sessionConfig, 66 | }); 67 | this.bob = new nkn.MultiClient({ 68 | seed: this.alice.getSeed(), 69 | identifier: 'bob', 70 | numSubClients: this.config.numClients - (this.config.originalClient ? 1 : 0), 71 | originalClient: this.config.originalClient, 72 | sessionConfig: this.config.sessionConfig, 73 | }); 74 | await Promise.all([ 75 | new Promise(resolve => this.alice.onConnect(resolve)), 76 | new Promise(resolve => this.bob.onConnect(resolve)), 77 | ]); 78 | await new Promise(resolve => setTimeout(resolve, 10000)); 79 | expect(this.alice.isReady).toBe(true); 80 | expect(this.bob.isReady).toBe(true); 81 | }); 82 | 83 | test(`[${this.name}] dial`, async () => { 84 | this.bob.listen(); 85 | [this.aliceSession, this.bobSession] = await Promise.all([ 86 | this.alice.dial(this.bob.addr), 87 | new Promise(resolve => this.bob.onSession(resolve)), 88 | ]); 89 | expect(this.aliceSession.remoteAddr).toBe(this.bobSession.localAddr); 90 | expect(this.bobSession.remoteAddr).toBe(this.aliceSession.localAddr); 91 | }); 92 | 93 | test(`[${this.name}] send`, async () => { 94 | let promises = [ 95 | this.write(this.aliceSession, this.config.numBytes), 96 | this.read(this.bobSession), 97 | this.write(this.bobSession, this.config.numBytes), 98 | this.read(this.aliceSession), 99 | ]; 100 | let r = await Promise.all(promises); 101 | expect(r.length).toBe(promises.length); 102 | }); 103 | 104 | test(`[${this.name}] close`, async () => { 105 | await Promise.all([this.alice.close(), this.bob.close()]); 106 | expect(this.aliceSession.isClosed).toBe(true); 107 | expect(this.bobSession.isClosed).toBe(true); 108 | expect(this.alice.isClosed).toBe(true); 109 | expect(this.bob.isClosed).toBe(true); 110 | }); 111 | } 112 | } 113 | 114 | new SessionTest('default', { 115 | numClients: 4, 116 | numBytes: 1 << 14, 117 | writeChunkSize: 1024, 118 | }).run(); 119 | 120 | // new SessionTest('single client', { 121 | // numClients: 1, 122 | // numBytes: 1 << 14, 123 | // writeChunkSize: 1024, 124 | // }).run(); 125 | // 126 | // new SessionTest('original client', { 127 | // numClients: 2, 128 | // originalClient: true, 129 | // numBytes: 1 << 14, 130 | // writeChunkSize: 1024, 131 | // }).run(); 132 | // 133 | // new SessionTest('minimal session window', { 134 | // numClients: 4, 135 | // numBytes: 1 << 4, 136 | // writeChunkSize: 1, 137 | // sessionConfig: { sessionWindowSize: 1 }, 138 | // }).run(); 139 | // 140 | // new SessionTest('big write chunk size', { 141 | // numClients: 4, 142 | // numBytes: 1 << 14, 143 | // writeChunkSize: 2048, 144 | // }).run(); 145 | // 146 | // new SessionTest('non stream', { 147 | // numClients: 4, 148 | // numBytes: 1 << 24, 149 | // writeChunkSize: 1000, 150 | // sessionConfig: { nonStream: true }, 151 | // }).run(); 152 | -------------------------------------------------------------------------------- /src/wallet/transaction.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import * as address from "./address"; 4 | import * as common from "../common"; 5 | 6 | export const nameRegistrationFee = "10"; 7 | 8 | export function newTransferPayload(sender, recipient, amount) { 9 | let transfer = new common.pb.transaction.TransferAsset(); 10 | transfer.setSender(Buffer.from(sender, "hex")); 11 | transfer.setRecipient(Buffer.from(recipient, "hex")); 12 | transfer.setAmount(new common.Amount(amount).value()); 13 | 14 | let pld = new common.pb.transaction.Payload(); 15 | pld.setType(common.pb.transaction.PayloadType.TRANSFER_ASSET_TYPE); 16 | pld.setData(transfer.serializeBinary()); 17 | 18 | return pld; 19 | } 20 | 21 | export function newRegisterNamePayload( 22 | registrant, 23 | name, 24 | registrationFee = nameRegistrationFee, 25 | ) { 26 | let registerName = new common.pb.transaction.RegisterName(); 27 | registerName.setRegistrant(Buffer.from(registrant, "hex")); 28 | registerName.setName(name); 29 | registerName.setRegistrationFee(new common.Amount(registrationFee).value()); 30 | 31 | let pld = new common.pb.transaction.Payload(); 32 | pld.setType(common.pb.transaction.PayloadType.REGISTER_NAME_TYPE); 33 | pld.setData(registerName.serializeBinary()); 34 | 35 | return pld; 36 | } 37 | 38 | export function newTransferNamePayload(name, registrant, recipient) { 39 | let transferName = new common.pb.transaction.TransferName(); 40 | transferName.setName(name); 41 | transferName.setRegistrant(Buffer.from(registrant, "hex")); 42 | transferName.setRecipient(Buffer.from(recipient, "hex")); 43 | 44 | let pld = new common.pb.transaction.Payload(); 45 | pld.setType(common.pb.transaction.PayloadType.TRANSFER_NAME_TYPE); 46 | pld.setData(transferName.serializeBinary()); 47 | 48 | return pld; 49 | } 50 | 51 | export function newDeleteNamePayload(registrant, name) { 52 | let deleteName = new common.pb.transaction.DeleteName(); 53 | deleteName.setRegistrant(Buffer.from(registrant, "hex")); 54 | deleteName.setName(name); 55 | 56 | let pld = new common.pb.transaction.Payload(); 57 | pld.setType(common.pb.transaction.PayloadType.DELETE_NAME_TYPE); 58 | pld.setData(deleteName.serializeBinary()); 59 | 60 | return pld; 61 | } 62 | 63 | export function newSubscribePayload( 64 | subscriber, 65 | identifier, 66 | topic, 67 | duration, 68 | meta, 69 | ) { 70 | let subscribe = new common.pb.transaction.Subscribe(); 71 | subscribe.setSubscriber(Buffer.from(subscriber, "hex")); 72 | subscribe.setIdentifier(identifier); 73 | subscribe.setTopic(topic); 74 | subscribe.setDuration(duration); 75 | subscribe.setMeta(meta); 76 | 77 | let pld = new common.pb.transaction.Payload(); 78 | pld.setType(common.pb.transaction.PayloadType.SUBSCRIBE_TYPE); 79 | pld.setData(subscribe.serializeBinary()); 80 | 81 | return pld; 82 | } 83 | 84 | export function newUnsubscribePayload(subscriber, identifier, topic) { 85 | let unsubscribe = new common.pb.transaction.Unsubscribe(); 86 | unsubscribe.setSubscriber(Buffer.from(subscriber, "hex")); 87 | unsubscribe.setIdentifier(identifier); 88 | unsubscribe.setTopic(topic); 89 | 90 | let pld = new common.pb.transaction.Payload(); 91 | pld.setType(common.pb.transaction.PayloadType.UNSUBSCRIBE_TYPE); 92 | pld.setData(unsubscribe.serializeBinary()); 93 | 94 | return pld; 95 | } 96 | 97 | export function newNanoPayPayload( 98 | sender, 99 | recipient, 100 | id, 101 | amount, 102 | txnExpiration, 103 | nanoPayExpiration, 104 | ) { 105 | let nanoPay = new common.pb.transaction.NanoPay(); 106 | nanoPay.setSender(Buffer.from(sender, "hex")); 107 | nanoPay.setRecipient(Buffer.from(recipient, "hex")); 108 | nanoPay.setId(id); 109 | nanoPay.setAmount(new common.Amount(amount).value()); 110 | nanoPay.setTxnExpiration(txnExpiration); 111 | nanoPay.setNanoPayExpiration(nanoPayExpiration); 112 | 113 | let pld = new common.pb.transaction.Payload(); 114 | pld.setType(common.pb.transaction.PayloadType.NANO_PAY_TYPE); 115 | pld.setData(nanoPay.serializeBinary()); 116 | 117 | return pld; 118 | } 119 | 120 | export function serializePayload(payload) { 121 | let hex = ""; 122 | hex += common.serialize.encodeUint32(payload.getType()); 123 | hex += common.serialize.encodeBytes(payload.getData()); 124 | return hex; 125 | } 126 | 127 | export async function newTransaction( 128 | account, 129 | pld, 130 | nonce, 131 | fee = "0", 132 | attrs = "", 133 | ) { 134 | let unsignedTx = new common.pb.transaction.UnsignedTx(); 135 | unsignedTx.setPayload(pld); 136 | unsignedTx.setNonce(nonce); 137 | unsignedTx.setFee(new common.Amount(fee).value()); 138 | unsignedTx.setAttributes(Buffer.from(attrs, "hex")); 139 | 140 | let txn = new common.pb.transaction.Transaction(); 141 | txn.setUnsignedTx(unsignedTx); 142 | await signTx(account, txn); 143 | 144 | return txn; 145 | } 146 | 147 | export function serializeUnsignedTx(unsignedTx) { 148 | let hex = ""; 149 | hex += serializePayload(unsignedTx.getPayload()); 150 | hex += common.serialize.encodeUint64(unsignedTx.getNonce()); 151 | hex += common.serialize.encodeUint64(unsignedTx.getFee()); 152 | hex += common.serialize.encodeBytes(unsignedTx.getAttributes()); 153 | return hex; 154 | } 155 | 156 | export async function signTx(account, txn) { 157 | let unsignedTx = txn.getUnsignedTx(); 158 | let hex = serializeUnsignedTx(unsignedTx); 159 | let digest = common.hash.sha256Hex(hex); 160 | let signature = await account.key.sign(digest); 161 | 162 | txn.hash = common.hash.doubleSha256Hex(hex); 163 | 164 | let prgm = new common.pb.transaction.Program(); 165 | prgm.setCode(Buffer.from(account.signatureRedeem, "hex")); 166 | prgm.setParameter( 167 | Buffer.from(address.signatureToParameter(signature), "hex"), 168 | ); 169 | 170 | txn.setProgramsList([prgm]); 171 | } 172 | -------------------------------------------------------------------------------- /src/common/errors.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | export const rpcRespErrCodes = { 4 | success: 0, 5 | wrongNode: 48001, 6 | appendTxnPool: 45021, 7 | invalidMethod: 42001, 8 | }; 9 | 10 | export class AddrNotAllowedError extends Error { 11 | constructor(message = "address not allowed", ...params) { 12 | super(message, ...params); 13 | if (Error.captureStackTrace) { 14 | Error.captureStackTrace(this, AddrNotAllowedError); 15 | } 16 | this.name = "AddrNotAllowedError"; 17 | } 18 | } 19 | 20 | export class ClientNotReadyError extends Error { 21 | constructor(message = "client not ready", ...params) { 22 | super(message, ...params); 23 | if (Error.captureStackTrace) { 24 | Error.captureStackTrace(this, ClientNotReadyError); 25 | } 26 | this.name = "ClientNotReadyError"; 27 | } 28 | } 29 | 30 | export class DataSizeTooLargeError extends Error { 31 | constructor(message = "data size too large", ...params) { 32 | super(message, ...params); 33 | if (Error.captureStackTrace) { 34 | Error.captureStackTrace(this, DataSizeTooLargeError); 35 | } 36 | this.name = "DataSizeTooLargeError"; 37 | } 38 | } 39 | 40 | export class DecryptionError extends Error { 41 | constructor(message = "decrypt message error", ...params) { 42 | super(message, ...params); 43 | if (Error.captureStackTrace) { 44 | Error.captureStackTrace(this, DecryptionError); 45 | } 46 | this.name = "DecryptionError"; 47 | } 48 | } 49 | 50 | export class UnknownError extends Error { 51 | constructor(message = "unknown error", ...params) { 52 | super(message, ...params); 53 | if (Error.captureStackTrace) { 54 | Error.captureStackTrace(this, UnknownError); 55 | } 56 | this.name = "UnknownError"; 57 | } 58 | } 59 | 60 | export class NotEnoughBalanceError extends Error { 61 | constructor(message = "not enough balance", ...params) { 62 | super(message, ...params); 63 | if (Error.captureStackTrace) { 64 | Error.captureStackTrace(this, NotEnoughBalanceError); 65 | } 66 | this.name = "NotEnoughBalanceError"; 67 | } 68 | } 69 | 70 | export class WrongPasswordError extends Error { 71 | constructor(message = "wrong password", ...params) { 72 | super(message, ...params); 73 | if (Error.captureStackTrace) { 74 | Error.captureStackTrace(this, WrongPasswordError); 75 | } 76 | this.name = "WrongPasswordError"; 77 | } 78 | } 79 | 80 | export class InvalidAddressError extends Error { 81 | constructor(message = "invalid wallet address", ...params) { 82 | super(message, ...params); 83 | if (Error.captureStackTrace) { 84 | Error.captureStackTrace(this, InvalidAddressError); 85 | } 86 | this.name = "InvalidAddressError"; 87 | } 88 | } 89 | 90 | export class InvalidWalletFormatError extends Error { 91 | constructor(message = "invalid wallet format", ...params) { 92 | super(message, ...params); 93 | if (Error.captureStackTrace) { 94 | Error.captureStackTrace(this, InvalidWalletFormatError); 95 | } 96 | this.name = "InvalidWalletFormatError"; 97 | } 98 | } 99 | 100 | export class InvalidWalletVersionError extends Error { 101 | constructor(message = "invalid wallet version", ...params) { 102 | super(message, ...params); 103 | if (Error.captureStackTrace) { 104 | Error.captureStackTrace(this, InvalidWalletVersionError); 105 | } 106 | this.name = "InvalidWalletVersionError"; 107 | } 108 | } 109 | 110 | export class InvalidArgumentError extends Error { 111 | constructor(message = "invalid argument", ...params) { 112 | super(message, ...params); 113 | if (Error.captureStackTrace) { 114 | Error.captureStackTrace(this, InvalidArgumentError); 115 | } 116 | this.name = "InvalidArgumentError"; 117 | } 118 | } 119 | 120 | export class InvalidResponseError extends Error { 121 | constructor(message = "invalid response from RPC server", ...params) { 122 | super(message, ...params); 123 | if (Error.captureStackTrace) { 124 | Error.captureStackTrace(this, InvalidResponseError); 125 | } 126 | this.name = "InvalidResponseError"; 127 | } 128 | } 129 | 130 | export class ServerError extends Error { 131 | constructor(error = "error from RPC server", ...params) { 132 | let message; 133 | if (typeof error === "object") { 134 | message = error.message + ": " + error.data; 135 | } else { 136 | message = error; 137 | } 138 | super(message, ...params); 139 | if (Error.captureStackTrace) { 140 | Error.captureStackTrace(this, ServerError); 141 | } 142 | this.name = "ServerError"; 143 | if (error.code) { 144 | this.code = -error.code; 145 | } 146 | } 147 | } 148 | 149 | export class InvalidDestinationError extends Error { 150 | constructor(message = "invalid destination", ...params) { 151 | super(message, ...params); 152 | if (Error.captureStackTrace) { 153 | Error.captureStackTrace(this, InvalidDestinationError); 154 | } 155 | this.name = "InvalidDestinationError"; 156 | } 157 | } 158 | 159 | export class RpcTimeoutError extends Error { 160 | constructor(message = "rpc timeout", ...params) { 161 | super(message, ...params); 162 | if (Error.captureStackTrace) { 163 | Error.captureStackTrace(this, RpcTimeoutError); 164 | } 165 | this.name = "RpcTimeoutError"; 166 | } 167 | } 168 | 169 | export class RpcError extends Error { 170 | constructor(message = "rpc error", ...params) { 171 | super(message, ...params); 172 | if (Error.captureStackTrace) { 173 | Error.captureStackTrace(this, RpcError); 174 | } 175 | this.name = "RpcError"; 176 | } 177 | } 178 | 179 | export class ChallengeTimeoutError extends Error { 180 | constructor(message = "challenge timeout", ...params) { 181 | super(message, ...params); 182 | if (Error.captureStackTrace) { 183 | Error.captureStackTrace(this, ChallengeTimeoutError); 184 | } 185 | this.name = "ChallengeTimeoutError"; 186 | } 187 | } 188 | 189 | export class ConnectToNodeTimeoutError extends Error { 190 | constructor(message = "connect to node timeout", ...params) { 191 | super(message, ...params); 192 | if (Error.captureStackTrace) { 193 | Error.captureStackTrace(this, ConnectToNodeTimeoutError); 194 | } 195 | this.name = "ConnectToNodeTimeoutError"; 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /src/client/message.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import pako from "pako"; 4 | 5 | import * as common from "../common"; 6 | 7 | export const messageIdSize = 8; // in bytes 8 | export const maxClientMessageSize = 4000000; // in bytes. NKN node is using 4*1024*1024 as limit, we give some additional space for serialization overhead. 9 | 10 | export function newPayload(type, replyToId, data, messageId) { 11 | let payload = new common.pb.payloads.Payload(); 12 | payload.setType(type); 13 | if (replyToId) { 14 | payload.setReplyToId(replyToId); 15 | } else if (messageId) { 16 | payload.setMessageId(messageId); 17 | } else { 18 | payload.setMessageId(common.util.randomBytes(messageIdSize)); 19 | } 20 | payload.setData(data); 21 | return payload; 22 | } 23 | 24 | export function newBinaryPayload(data, replyToId, messageId) { 25 | return newPayload( 26 | common.pb.payloads.PayloadType.BINARY, 27 | replyToId, 28 | data, 29 | messageId, 30 | ); 31 | } 32 | 33 | export function newTextPayload(text, replyToId, messageId) { 34 | let data = new common.pb.payloads.TextData(); 35 | data.setText(text); 36 | return newPayload( 37 | common.pb.payloads.PayloadType.TEXT, 38 | replyToId, 39 | data.serializeBinary(), 40 | messageId, 41 | ); 42 | } 43 | 44 | export function newAckPayload(replyToId, messageId) { 45 | return newPayload( 46 | common.pb.payloads.PayloadType.ACK, 47 | replyToId, 48 | null, 49 | messageId, 50 | ); 51 | } 52 | 53 | export function newSessionPayload(data, sessionID) { 54 | return newPayload( 55 | common.pb.payloads.PayloadType.SESSION, 56 | null, 57 | data, 58 | sessionID, 59 | ); 60 | } 61 | 62 | export function newMessage(payload, encrypted, nonce, encryptedKey) { 63 | let msg = new common.pb.payloads.Message(); 64 | msg.setPayload(payload); 65 | msg.setEncrypted(encrypted); 66 | if (nonce) { 67 | msg.setNonce(nonce); 68 | } 69 | if (encryptedKey) { 70 | msg.setEncryptedKey(encryptedKey); 71 | } 72 | return msg; 73 | } 74 | 75 | export function newClientMessage(messageType, message, compressionType) { 76 | let msg = new common.pb.messages.ClientMessage(); 77 | msg.setMessageType(messageType); 78 | msg.setCompressionType(compressionType); 79 | switch (compressionType) { 80 | case common.pb.messages.CompressionType.COMPRESSION_NONE: 81 | break; 82 | case common.pb.messages.CompressionType.COMPRESSION_ZLIB: 83 | message = pako.deflate(message); 84 | break; 85 | default: 86 | throw new common.errors.InvalidArgumentError( 87 | "unknown compression type " + compressionType, 88 | ); 89 | } 90 | msg.setMessage(message); 91 | return msg; 92 | } 93 | 94 | export async function newOutboundMessage( 95 | client, 96 | dest, 97 | payload, 98 | maxHoldingSeconds, 99 | ) { 100 | if (!Array.isArray(dest)) { 101 | dest = [dest]; 102 | } 103 | if (dest.length === 0) { 104 | throw new common.errors.InvalidArgumentError("no destination"); 105 | } 106 | 107 | if (!Array.isArray(payload)) { 108 | payload = [payload]; 109 | } 110 | if (payload.length === 0) { 111 | throw new common.errors.InvalidArgumentError("no payloads"); 112 | } 113 | 114 | if (payload.length > 1 && payload.length !== dest.length) { 115 | throw new common.errors.InvalidArgumentError( 116 | "invalid payload array length", 117 | ); 118 | } 119 | 120 | let sigChainElem = new common.pb.sigchain.SigChainElem(); 121 | sigChainElem.setNextPubkey(Buffer.from(client.node.pubkey, "hex")); 122 | let sigChainElemSerialized = serializeSigChainElem(sigChainElem); 123 | 124 | let sigChain = new common.pb.sigchain.SigChain(); 125 | sigChain.setNonce(common.util.randomInt32()); 126 | if (client.sigChainBlockHash) { 127 | sigChain.setBlockHash(Buffer.from(client.sigChainBlockHash, "hex")); 128 | } 129 | sigChain.setSrcId(Buffer.from(addrToID(client.addr), "hex")); 130 | sigChain.setSrcPubkey(Buffer.from(client.key.publicKey, "hex")); 131 | 132 | let signatures = []; 133 | let hex, digest, signature; 134 | for (let i = 0; i < dest.length; i++) { 135 | sigChain.setDestId(Buffer.from(addrToID(dest[i]), "hex")); 136 | sigChain.setDestPubkey(Buffer.from(addrToPubkey(dest[i]), "hex")); 137 | if (payload.length > 1) { 138 | sigChain.setDataSize(payload[i].length); 139 | } else { 140 | sigChain.setDataSize(payload[0].length); 141 | } 142 | hex = serializeSigChainMetadata(sigChain); 143 | digest = common.hash.sha256Hex(hex); 144 | digest = common.hash.sha256Hex(digest + sigChainElemSerialized); 145 | signature = await client.key.sign(digest); 146 | signatures.push(Buffer.from(signature, "hex")); 147 | } 148 | 149 | let msg = new common.pb.messages.OutboundMessage(); 150 | msg.setDestsList(dest); 151 | msg.setPayloadsList(payload); 152 | msg.setMaxHoldingSeconds(maxHoldingSeconds); 153 | msg.setNonce(sigChain.getNonce()); 154 | msg.setBlockHash(sigChain.getBlockHash()); 155 | msg.setSignaturesList(signatures); 156 | 157 | let compressionType; 158 | if (payload.length > 1) { 159 | compressionType = common.pb.messages.CompressionType.COMPRESSION_ZLIB; 160 | } else { 161 | compressionType = common.pb.messages.CompressionType.COMPRESSION_NONE; 162 | } 163 | 164 | return newClientMessage( 165 | common.pb.messages.ClientMessageType.OUTBOUND_MESSAGE, 166 | msg.serializeBinary(), 167 | compressionType, 168 | ); 169 | } 170 | 171 | export async function newReceipt(client, prevSignature) { 172 | let sigChainElem = new common.pb.sigchain.SigChainElem(); 173 | let sigChainElemSerialized = serializeSigChainElem(sigChainElem); 174 | let digest = common.hash.sha256Hex(prevSignature); 175 | digest = common.hash.sha256Hex(digest + sigChainElemSerialized); 176 | let signature = await client.key.sign(digest); 177 | let msg = new common.pb.messages.Receipt(); 178 | msg.setPrevSignature(Buffer.from(prevSignature, "hex")); 179 | msg.setSignature(Buffer.from(signature, "hex")); 180 | return newClientMessage( 181 | common.pb.messages.ClientMessageType.RECEIPT, 182 | msg.serializeBinary(), 183 | common.pb.messages.CompressionType.COMPRESSION_NONE, 184 | ); 185 | } 186 | 187 | export function serializeSigChainMetadata(sigChain) { 188 | let hex = ""; 189 | hex += common.serialize.encodeUint32(sigChain.getNonce()); 190 | hex += common.serialize.encodeUint32(sigChain.getDataSize()); 191 | hex += common.serialize.encodeBytes(sigChain.getBlockHash()); 192 | hex += common.serialize.encodeBytes(sigChain.getSrcId()); 193 | hex += common.serialize.encodeBytes(sigChain.getSrcPubkey()); 194 | hex += common.serialize.encodeBytes(sigChain.getDestId()); 195 | hex += common.serialize.encodeBytes(sigChain.getDestPubkey()); 196 | return hex; 197 | } 198 | 199 | export function serializeSigChainElem(sigChainElem) { 200 | let hex = ""; 201 | hex += common.serialize.encodeBytes(sigChainElem.getId()); 202 | hex += common.serialize.encodeBytes(sigChainElem.getNextPubkey()); 203 | hex += common.serialize.encodeBool(sigChainElem.getMining()); 204 | return hex; 205 | } 206 | 207 | export function addrToID(addr) { 208 | return common.hash.sha256(addr); 209 | } 210 | 211 | export function addrToPubkey(addr) { 212 | let s = addr.split("."); 213 | return s[s.length - 1]; 214 | } 215 | -------------------------------------------------------------------------------- /src/common/rpc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | import axios from "axios"; 4 | 5 | import Amount from "./amount"; 6 | import * as address from "../wallet/address"; 7 | import * as errors from "./errors"; 8 | import * as transaction from "../wallet/transaction"; 9 | import * as util from "./util"; 10 | 11 | const rpcTimeout = 10000; 12 | 13 | const methods = { 14 | getWsAddr: { method: "getwsaddr" }, 15 | getWssAddr: { method: "getwssaddr" }, 16 | getSubscribers: { 17 | method: "getsubscribers", 18 | defaultParams: { offset: 0, limit: 1000, meta: false, txPool: false }, 19 | }, 20 | getSubscribersCount: { method: "getsubscriberscount" }, 21 | getSubscription: { method: "getsubscription" }, 22 | getBalanceByAddr: { method: "getbalancebyaddr" }, 23 | getNonceByAddr: { method: "getnoncebyaddr" }, 24 | getRegistrant: { method: "getregistrant" }, 25 | getLatestBlockHash: { method: "getlatestblockhash" }, 26 | sendRawTransaction: { method: "sendrawtransaction" }, 27 | getNodeState: { method: "getnodestate" }, 28 | getPeerAddr: { method: "getpeeraddr" }, 29 | }; 30 | 31 | var rpc = {}; 32 | for (let method in methods) { 33 | if (methods.hasOwnProperty(method)) { 34 | rpc[method] = (addr, params) => { 35 | params = util.assignDefined({}, methods[method].defaultParams, params); 36 | return rpcCall(addr, methods[method].method, params); 37 | }; 38 | } 39 | } 40 | 41 | export async function rpcCall(addr, method, params = {}) { 42 | const source = axios.CancelToken.source(); 43 | let response = null; 44 | 45 | setTimeout(() => { 46 | if (response === null) { 47 | source.cancel("rpc timeout"); 48 | } 49 | }, rpcTimeout); 50 | 51 | try { 52 | response = await axios({ 53 | url: addr, 54 | method: "POST", 55 | timeout: rpcTimeout, 56 | cancelToken: source.token, 57 | data: { 58 | id: "nkn-sdk-js", 59 | jsonrpc: "2.0", 60 | method: method, 61 | params: params, 62 | }, 63 | }); 64 | } catch (e) { 65 | if (axios.isCancel(e)) { 66 | throw new errors.RpcTimeoutError(e.message); 67 | } else { 68 | throw new errors.RpcError(e.message); 69 | } 70 | } 71 | 72 | let data = response.data; 73 | 74 | if (data.error) { 75 | throw new errors.ServerError(data.error); 76 | } 77 | 78 | if (data.result !== undefined) { 79 | return data.result; 80 | } 81 | 82 | throw new errors.InvalidResponseError( 83 | "rpc response contains no result or error field", 84 | ); 85 | } 86 | 87 | export async function getWsAddr(address, options = {}) { 88 | return rpc.getWsAddr(options.rpcServerAddr, { address }); 89 | } 90 | 91 | export async function getWssAddr(address, options = {}) { 92 | return rpc.getWssAddr(options.rpcServerAddr, { address }); 93 | } 94 | 95 | export async function getLatestBlock(options = {}) { 96 | return rpc.getLatestBlockHash(options.rpcServerAddr); 97 | } 98 | 99 | export async function getRegistrant(name, options = {}) { 100 | return rpc.getRegistrant(options.rpcServerAddr, { name }); 101 | } 102 | 103 | export async function getSubscribers(topic, options = {}) { 104 | return rpc.getSubscribers(options.rpcServerAddr, { 105 | topic, 106 | offset: options.offset, 107 | limit: options.limit, 108 | meta: options.meta, 109 | txPool: options.txPool, 110 | }); 111 | } 112 | 113 | export async function getSubscribersCount(topic, options = {}) { 114 | return rpc.getSubscribersCount(options.rpcServerAddr, { topic }); 115 | } 116 | 117 | export async function getSubscription(topic, subscriber, options = {}) { 118 | return rpc.getSubscription(options.rpcServerAddr, { topic, subscriber }); 119 | } 120 | 121 | export async function getBalance(address, options = {}) { 122 | if (!address) { 123 | throw new errors.InvalidArgumentError("address is empty"); 124 | } 125 | let data = await rpc.getBalanceByAddr(options.rpcServerAddr, { address }); 126 | if (!data.amount) { 127 | throw new errors.InvalidResponseError("amount is empty"); 128 | } 129 | return new Amount(data.amount); 130 | } 131 | 132 | export async function getNonce(address, options = {}) { 133 | if (!address) { 134 | throw new errors.InvalidArgumentError("address is empty"); 135 | } 136 | options = util.assignDefined({ txPool: true }, options); 137 | let data = await rpc.getNonceByAddr(options.rpcServerAddr, { address }); 138 | if (typeof data.nonce !== "number") { 139 | throw new errors.InvalidResponseError("nonce is not a number"); 140 | } 141 | let nonce = data.nonce; 142 | if (options.txPool && data.nonceInTxPool && data.nonceInTxPool > nonce) { 143 | nonce = data.nonceInTxPool; 144 | } 145 | return nonce; 146 | } 147 | 148 | export async function sendTransaction(txn, options = {}) { 149 | return rpc.sendRawTransaction(options.rpcServerAddr, { 150 | tx: util.bytesToHex(txn.serializeBinary()), 151 | }); 152 | } 153 | 154 | export async function transferTo(toAddress, amount, options = {}) { 155 | if (!address.verifyAddress(toAddress)) { 156 | throw new errors.InvalidAddressError("invalid recipient address"); 157 | } 158 | let nonce = options.nonce; 159 | if (nonce === null || nonce === undefined) { 160 | nonce = await this.getNonce(); 161 | } 162 | let signatureRedeem = address.publicKeyToSignatureRedeem(this.getPublicKey()); 163 | let programHash = address.hexStringToProgramHash(signatureRedeem); 164 | let pld = transaction.newTransferPayload( 165 | programHash, 166 | address.addressStringToProgramHash(toAddress), 167 | amount, 168 | ); 169 | let txn = await this.createTransaction(pld, nonce, options); 170 | return options.buildOnly ? txn : await this.sendTransaction(txn); 171 | } 172 | 173 | export async function registerName(name, options = {}) { 174 | let nonce = options.nonce; 175 | if (nonce === null || nonce === undefined) { 176 | nonce = await this.getNonce(); 177 | } 178 | let pld = transaction.newRegisterNamePayload(this.getPublicKey(), name); 179 | let txn = await this.createTransaction(pld, nonce, options); 180 | return options.buildOnly ? txn : await this.sendTransaction(txn); 181 | } 182 | 183 | export async function transferName(name, recipient, options = {}) { 184 | let nonce = options.nonce; 185 | if (nonce === null || nonce === undefined) { 186 | nonce = await this.getNonce(); 187 | } 188 | let pld = transaction.newTransferNamePayload( 189 | name, 190 | this.getPublicKey(), 191 | recipient, 192 | ); 193 | let txn = await this.createTransaction(pld, nonce, options); 194 | return options.buildOnly ? txn : await this.sendTransaction(txn); 195 | } 196 | 197 | export async function deleteName(name, options = {}) { 198 | let nonce = options.nonce; 199 | if (nonce === null || nonce === undefined) { 200 | nonce = await this.getNonce(); 201 | } 202 | let pld = transaction.newDeleteNamePayload(this.getPublicKey(), name); 203 | let txn = await this.createTransaction(pld, nonce, options); 204 | return options.buildOnly ? txn : await this.sendTransaction(txn); 205 | } 206 | 207 | export async function subscribe( 208 | topic, 209 | duration, 210 | identifier, 211 | meta, 212 | options = {}, 213 | ) { 214 | let nonce = options.nonce; 215 | if (nonce === null || nonce === undefined) { 216 | nonce = await this.getNonce(); 217 | } 218 | let pld = transaction.newSubscribePayload( 219 | this.getPublicKey(), 220 | identifier, 221 | topic, 222 | duration, 223 | meta, 224 | ); 225 | let txn = await this.createTransaction(pld, nonce, options); 226 | return options.buildOnly ? txn : await this.sendTransaction(txn); 227 | } 228 | 229 | export async function unsubscribe(topic, identifier, options = {}) { 230 | let nonce = options.nonce; 231 | if (nonce === null || nonce === undefined) { 232 | nonce = await this.getNonce(); 233 | } 234 | let pld = transaction.newUnsubscribePayload( 235 | this.getPublicKey(), 236 | identifier, 237 | topic, 238 | ); 239 | let txn = await this.createTransaction(pld, nonce, options); 240 | return options.buildOnly ? txn : await this.sendTransaction(txn); 241 | } 242 | 243 | export async function getNodeState(options = {}) { 244 | return rpc.getNodeState(options.rpcServerAddr); 245 | } 246 | 247 | export async function getPeerAddr(address, options = {}) { 248 | return rpc.getPeerAddr(options.rpcServerAddr, { 249 | address, 250 | offer: options.offer, 251 | }); 252 | } 253 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /lib/worker/worker.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 4 | var crypto = _interopRequireWildcard(require("../common/crypto")); 5 | var util = _interopRequireWildcard(require("../common/util")); 6 | function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); } 7 | function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof(e) && "function" != typeof e) return { "default": e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n["default"] = e, t && t.set(e, n), n; } 8 | function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } 9 | function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } 10 | function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } 11 | module.exports = function (self) { 12 | var key; 13 | self.onmessage = /*#__PURE__*/function () { 14 | var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(e) { 15 | var result; 16 | return _regeneratorRuntime().wrap(function _callee$(_context) { 17 | while (1) switch (_context.prev = _context.next) { 18 | case 0: 19 | _context.prev = 0; 20 | result = null; 21 | _context.t0 = e.data.action; 22 | _context.next = _context.t0 === "setSeed" ? 5 : _context.t0 === "computeSharedKey" ? 12 : _context.t0 === "sign" ? 20 : 28; 23 | break; 24 | case 5: 25 | if (key) { 26 | _context.next = 9; 27 | break; 28 | } 29 | key = crypto.keyPair(e.data.seed); 30 | _context.next = 11; 31 | break; 32 | case 9: 33 | if (!(e.data.seed !== key.seed)) { 34 | _context.next = 11; 35 | break; 36 | } 37 | throw "cannot set to different seed"; 38 | case 11: 39 | return _context.abrupt("break", 29); 40 | case 12: 41 | if (!key) { 42 | _context.next = 18; 43 | break; 44 | } 45 | _context.next = 15; 46 | return crypto.computeSharedKey(key.curvePrivateKey, e.data.otherPubkey); 47 | case 15: 48 | result = _context.sent; 49 | _context.next = 19; 50 | break; 51 | case 18: 52 | throw "worker key not created"; 53 | case 19: 54 | return _context.abrupt("break", 29); 55 | case 20: 56 | if (!key) { 57 | _context.next = 26; 58 | break; 59 | } 60 | _context.next = 23; 61 | return crypto.sign(key.privateKey, e.data.message); 62 | case 23: 63 | result = _context.sent; 64 | _context.next = 27; 65 | break; 66 | case 26: 67 | throw "worker key not created"; 68 | case 27: 69 | return _context.abrupt("break", 29); 70 | case 28: 71 | throw "unknown action: " + e.data.action; 72 | case 29: 73 | self.postMessage({ 74 | id: e.data.id, 75 | result: result 76 | }); 77 | _context.next = 35; 78 | break; 79 | case 32: 80 | _context.prev = 32; 81 | _context.t1 = _context["catch"](0); 82 | self.postMessage({ 83 | id: e.data.id, 84 | error: _context.t1 85 | }); 86 | case 35: 87 | case "end": 88 | return _context.stop(); 89 | } 90 | }, _callee, null, [[0, 32]]); 91 | })); 92 | return function (_x) { 93 | return _ref.apply(this, arguments); 94 | }; 95 | }(); 96 | }; -------------------------------------------------------------------------------- /docs/assets/bass.css: -------------------------------------------------------------------------------- 1 | /*! Basscss | http://basscss.com | MIT License */ 2 | 3 | .h1{ font-size: 2rem } 4 | .h2{ font-size: 1.5rem } 5 | .h3{ font-size: 1.25rem } 6 | .h4{ font-size: 1rem } 7 | .h5{ font-size: .875rem } 8 | .h6{ font-size: .75rem } 9 | 10 | .font-family-inherit{ font-family:inherit } 11 | .font-size-inherit{ font-size:inherit } 12 | .text-decoration-none{ text-decoration:none } 13 | 14 | .bold{ font-weight: bold; font-weight: bold } 15 | .regular{ font-weight:normal } 16 | .italic{ font-style:italic } 17 | .caps{ text-transform:uppercase; letter-spacing: .2em; } 18 | 19 | .left-align{ text-align:left } 20 | .center{ text-align:center } 21 | .right-align{ text-align:right } 22 | .justify{ text-align:justify } 23 | 24 | .nowrap{ white-space:nowrap } 25 | .break-word{ word-wrap:break-word } 26 | 27 | .line-height-1{ line-height: 1 } 28 | .line-height-2{ line-height: 1.125 } 29 | .line-height-3{ line-height: 1.25 } 30 | .line-height-4{ line-height: 1.5 } 31 | 32 | .list-style-none{ list-style:none } 33 | .underline{ text-decoration:underline } 34 | 35 | .truncate{ 36 | max-width:100%; 37 | overflow:hidden; 38 | text-overflow:ellipsis; 39 | white-space:nowrap; 40 | } 41 | 42 | .list-reset{ 43 | list-style:none; 44 | padding-left:0; 45 | } 46 | 47 | .inline{ display:inline } 48 | .block{ display:block } 49 | .inline-block{ display:inline-block } 50 | .table{ display:table } 51 | .table-cell{ display:table-cell } 52 | 53 | .overflow-hidden{ overflow:hidden } 54 | .overflow-scroll{ overflow:scroll } 55 | .overflow-auto{ overflow:auto } 56 | 57 | .clearfix:before, 58 | .clearfix:after{ 59 | content:" "; 60 | display:table 61 | } 62 | .clearfix:after{ clear:both } 63 | 64 | .left{ float:left } 65 | .right{ float:right } 66 | 67 | .fit{ max-width:100% } 68 | 69 | .max-width-1{ max-width: 24rem } 70 | .max-width-2{ max-width: 32rem } 71 | .max-width-3{ max-width: 48rem } 72 | .max-width-4{ max-width: 64rem } 73 | 74 | .border-box{ box-sizing:border-box } 75 | 76 | .align-baseline{ vertical-align:baseline } 77 | .align-top{ vertical-align:top } 78 | .align-middle{ vertical-align:middle } 79 | .align-bottom{ vertical-align:bottom } 80 | 81 | .m0{ margin:0 } 82 | .mt0{ margin-top:0 } 83 | .mr0{ margin-right:0 } 84 | .mb0{ margin-bottom:0 } 85 | .ml0{ margin-left:0 } 86 | .mx0{ margin-left:0; margin-right:0 } 87 | .my0{ margin-top:0; margin-bottom:0 } 88 | 89 | .m1{ margin: .5rem } 90 | .mt1{ margin-top: .5rem } 91 | .mr1{ margin-right: .5rem } 92 | .mb1{ margin-bottom: .5rem } 93 | .ml1{ margin-left: .5rem } 94 | .mx1{ margin-left: .5rem; margin-right: .5rem } 95 | .my1{ margin-top: .5rem; margin-bottom: .5rem } 96 | 97 | .m2{ margin: 1rem } 98 | .mt2{ margin-top: 1rem } 99 | .mr2{ margin-right: 1rem } 100 | .mb2{ margin-bottom: 1rem } 101 | .ml2{ margin-left: 1rem } 102 | .mx2{ margin-left: 1rem; margin-right: 1rem } 103 | .my2{ margin-top: 1rem; margin-bottom: 1rem } 104 | 105 | .m3{ margin: 2rem } 106 | .mt3{ margin-top: 2rem } 107 | .mr3{ margin-right: 2rem } 108 | .mb3{ margin-bottom: 2rem } 109 | .ml3{ margin-left: 2rem } 110 | .mx3{ margin-left: 2rem; margin-right: 2rem } 111 | .my3{ margin-top: 2rem; margin-bottom: 2rem } 112 | 113 | .m4{ margin: 4rem } 114 | .mt4{ margin-top: 4rem } 115 | .mr4{ margin-right: 4rem } 116 | .mb4{ margin-bottom: 4rem } 117 | .ml4{ margin-left: 4rem } 118 | .mx4{ margin-left: 4rem; margin-right: 4rem } 119 | .my4{ margin-top: 4rem; margin-bottom: 4rem } 120 | 121 | .mxn1{ margin-left: -.5rem; margin-right: -.5rem; } 122 | .mxn2{ margin-left: -1rem; margin-right: -1rem; } 123 | .mxn3{ margin-left: -2rem; margin-right: -2rem; } 124 | .mxn4{ margin-left: -4rem; margin-right: -4rem; } 125 | 126 | .ml-auto{ margin-left:auto } 127 | .mr-auto{ margin-right:auto } 128 | .mx-auto{ margin-left:auto; margin-right:auto; } 129 | 130 | .p0{ padding:0 } 131 | .pt0{ padding-top:0 } 132 | .pr0{ padding-right:0 } 133 | .pb0{ padding-bottom:0 } 134 | .pl0{ padding-left:0 } 135 | .px0{ padding-left:0; padding-right:0 } 136 | .py0{ padding-top:0; padding-bottom:0 } 137 | 138 | .p1{ padding: .5rem } 139 | .pt1{ padding-top: .5rem } 140 | .pr1{ padding-right: .5rem } 141 | .pb1{ padding-bottom: .5rem } 142 | .pl1{ padding-left: .5rem } 143 | .py1{ padding-top: .5rem; padding-bottom: .5rem } 144 | .px1{ padding-left: .5rem; padding-right: .5rem } 145 | 146 | .p2{ padding: 1rem } 147 | .pt2{ padding-top: 1rem } 148 | .pr2{ padding-right: 1rem } 149 | .pb2{ padding-bottom: 1rem } 150 | .pl2{ padding-left: 1rem } 151 | .py2{ padding-top: 1rem; padding-bottom: 1rem } 152 | .px2{ padding-left: 1rem; padding-right: 1rem } 153 | 154 | .p3{ padding: 2rem } 155 | .pt3{ padding-top: 2rem } 156 | .pr3{ padding-right: 2rem } 157 | .pb3{ padding-bottom: 2rem } 158 | .pl3{ padding-left: 2rem } 159 | .py3{ padding-top: 2rem; padding-bottom: 2rem } 160 | .px3{ padding-left: 2rem; padding-right: 2rem } 161 | 162 | .p4{ padding: 4rem } 163 | .pt4{ padding-top: 4rem } 164 | .pr4{ padding-right: 4rem } 165 | .pb4{ padding-bottom: 4rem } 166 | .pl4{ padding-left: 4rem } 167 | .py4{ padding-top: 4rem; padding-bottom: 4rem } 168 | .px4{ padding-left: 4rem; padding-right: 4rem } 169 | 170 | .col{ 171 | float:left; 172 | box-sizing:border-box; 173 | } 174 | 175 | .col-right{ 176 | float:right; 177 | box-sizing:border-box; 178 | } 179 | 180 | .col-1{ 181 | width:8.33333%; 182 | } 183 | 184 | .col-2{ 185 | width:16.66667%; 186 | } 187 | 188 | .col-3{ 189 | width:25%; 190 | } 191 | 192 | .col-4{ 193 | width:33.33333%; 194 | } 195 | 196 | .col-5{ 197 | width:41.66667%; 198 | } 199 | 200 | .col-6{ 201 | width:50%; 202 | } 203 | 204 | .col-7{ 205 | width:58.33333%; 206 | } 207 | 208 | .col-8{ 209 | width:66.66667%; 210 | } 211 | 212 | .col-9{ 213 | width:75%; 214 | } 215 | 216 | .col-10{ 217 | width:83.33333%; 218 | } 219 | 220 | .col-11{ 221 | width:91.66667%; 222 | } 223 | 224 | .col-12{ 225 | width:100%; 226 | } 227 | @media (min-width: 40em){ 228 | 229 | .sm-col{ 230 | float:left; 231 | box-sizing:border-box; 232 | } 233 | 234 | .sm-col-right{ 235 | float:right; 236 | box-sizing:border-box; 237 | } 238 | 239 | .sm-col-1{ 240 | width:8.33333%; 241 | } 242 | 243 | .sm-col-2{ 244 | width:16.66667%; 245 | } 246 | 247 | .sm-col-3{ 248 | width:25%; 249 | } 250 | 251 | .sm-col-4{ 252 | width:33.33333%; 253 | } 254 | 255 | .sm-col-5{ 256 | width:41.66667%; 257 | } 258 | 259 | .sm-col-6{ 260 | width:50%; 261 | } 262 | 263 | .sm-col-7{ 264 | width:58.33333%; 265 | } 266 | 267 | .sm-col-8{ 268 | width:66.66667%; 269 | } 270 | 271 | .sm-col-9{ 272 | width:75%; 273 | } 274 | 275 | .sm-col-10{ 276 | width:83.33333%; 277 | } 278 | 279 | .sm-col-11{ 280 | width:91.66667%; 281 | } 282 | 283 | .sm-col-12{ 284 | width:100%; 285 | } 286 | 287 | } 288 | @media (min-width: 52em){ 289 | 290 | .md-col{ 291 | float:left; 292 | box-sizing:border-box; 293 | } 294 | 295 | .md-col-right{ 296 | float:right; 297 | box-sizing:border-box; 298 | } 299 | 300 | .md-col-1{ 301 | width:8.33333%; 302 | } 303 | 304 | .md-col-2{ 305 | width:16.66667%; 306 | } 307 | 308 | .md-col-3{ 309 | width:25%; 310 | } 311 | 312 | .md-col-4{ 313 | width:33.33333%; 314 | } 315 | 316 | .md-col-5{ 317 | width:41.66667%; 318 | } 319 | 320 | .md-col-6{ 321 | width:50%; 322 | } 323 | 324 | .md-col-7{ 325 | width:58.33333%; 326 | } 327 | 328 | .md-col-8{ 329 | width:66.66667%; 330 | } 331 | 332 | .md-col-9{ 333 | width:75%; 334 | } 335 | 336 | .md-col-10{ 337 | width:83.33333%; 338 | } 339 | 340 | .md-col-11{ 341 | width:91.66667%; 342 | } 343 | 344 | .md-col-12{ 345 | width:100%; 346 | } 347 | 348 | } 349 | @media (min-width: 64em){ 350 | 351 | .lg-col{ 352 | float:left; 353 | box-sizing:border-box; 354 | } 355 | 356 | .lg-col-right{ 357 | float:right; 358 | box-sizing:border-box; 359 | } 360 | 361 | .lg-col-1{ 362 | width:8.33333%; 363 | } 364 | 365 | .lg-col-2{ 366 | width:16.66667%; 367 | } 368 | 369 | .lg-col-3{ 370 | width:25%; 371 | } 372 | 373 | .lg-col-4{ 374 | width:33.33333%; 375 | } 376 | 377 | .lg-col-5{ 378 | width:41.66667%; 379 | } 380 | 381 | .lg-col-6{ 382 | width:50%; 383 | } 384 | 385 | .lg-col-7{ 386 | width:58.33333%; 387 | } 388 | 389 | .lg-col-8{ 390 | width:66.66667%; 391 | } 392 | 393 | .lg-col-9{ 394 | width:75%; 395 | } 396 | 397 | .lg-col-10{ 398 | width:83.33333%; 399 | } 400 | 401 | .lg-col-11{ 402 | width:91.66667%; 403 | } 404 | 405 | .lg-col-12{ 406 | width:100%; 407 | } 408 | 409 | } 410 | .flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 411 | 412 | @media (min-width: 40em){ 413 | .sm-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 414 | } 415 | 416 | @media (min-width: 52em){ 417 | .md-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 418 | } 419 | 420 | @media (min-width: 64em){ 421 | .lg-flex{ display:-webkit-box; display:-webkit-flex; display:-ms-flexbox; display:flex } 422 | } 423 | 424 | .flex-column{ -webkit-box-orient:vertical; -webkit-box-direction:normal; -webkit-flex-direction:column; -ms-flex-direction:column; flex-direction:column } 425 | .flex-wrap{ -webkit-flex-wrap:wrap; -ms-flex-wrap:wrap; flex-wrap:wrap } 426 | 427 | .items-start{ -webkit-box-align:start; -webkit-align-items:flex-start; -ms-flex-align:start; -ms-grid-row-align:flex-start; align-items:flex-start } 428 | .items-end{ -webkit-box-align:end; -webkit-align-items:flex-end; -ms-flex-align:end; -ms-grid-row-align:flex-end; align-items:flex-end } 429 | .items-center{ -webkit-box-align:center; -webkit-align-items:center; -ms-flex-align:center; -ms-grid-row-align:center; align-items:center } 430 | .items-baseline{ -webkit-box-align:baseline; -webkit-align-items:baseline; -ms-flex-align:baseline; -ms-grid-row-align:baseline; align-items:baseline } 431 | .items-stretch{ -webkit-box-align:stretch; -webkit-align-items:stretch; -ms-flex-align:stretch; -ms-grid-row-align:stretch; align-items:stretch } 432 | 433 | .self-start{ -webkit-align-self:flex-start; -ms-flex-item-align:start; align-self:flex-start } 434 | .self-end{ -webkit-align-self:flex-end; -ms-flex-item-align:end; align-self:flex-end } 435 | .self-center{ -webkit-align-self:center; -ms-flex-item-align:center; align-self:center } 436 | .self-baseline{ -webkit-align-self:baseline; -ms-flex-item-align:baseline; align-self:baseline } 437 | .self-stretch{ -webkit-align-self:stretch; -ms-flex-item-align:stretch; align-self:stretch } 438 | 439 | .justify-start{ -webkit-box-pack:start; -webkit-justify-content:flex-start; -ms-flex-pack:start; justify-content:flex-start } 440 | .justify-end{ -webkit-box-pack:end; -webkit-justify-content:flex-end; -ms-flex-pack:end; justify-content:flex-end } 441 | .justify-center{ -webkit-box-pack:center; -webkit-justify-content:center; -ms-flex-pack:center; justify-content:center } 442 | .justify-between{ -webkit-box-pack:justify; -webkit-justify-content:space-between; -ms-flex-pack:justify; justify-content:space-between } 443 | .justify-around{ -webkit-justify-content:space-around; -ms-flex-pack:distribute; justify-content:space-around } 444 | 445 | .content-start{ -webkit-align-content:flex-start; -ms-flex-line-pack:start; align-content:flex-start } 446 | .content-end{ -webkit-align-content:flex-end; -ms-flex-line-pack:end; align-content:flex-end } 447 | .content-center{ -webkit-align-content:center; -ms-flex-line-pack:center; align-content:center } 448 | .content-between{ -webkit-align-content:space-between; -ms-flex-line-pack:justify; align-content:space-between } 449 | .content-around{ -webkit-align-content:space-around; -ms-flex-line-pack:distribute; align-content:space-around } 450 | .content-stretch{ -webkit-align-content:stretch; -ms-flex-line-pack:stretch; align-content:stretch } 451 | .flex-auto{ 452 | -webkit-box-flex:1; 453 | -webkit-flex:1 1 auto; 454 | -ms-flex:1 1 auto; 455 | flex:1 1 auto; 456 | min-width:0; 457 | min-height:0; 458 | } 459 | .flex-none{ -webkit-box-flex:0; -webkit-flex:none; -ms-flex:none; flex:none } 460 | .fs0{ flex-shrink: 0 } 461 | 462 | .order-0{ -webkit-box-ordinal-group:1; -webkit-order:0; -ms-flex-order:0; order:0 } 463 | .order-1{ -webkit-box-ordinal-group:2; -webkit-order:1; -ms-flex-order:1; order:1 } 464 | .order-2{ -webkit-box-ordinal-group:3; -webkit-order:2; -ms-flex-order:2; order:2 } 465 | .order-3{ -webkit-box-ordinal-group:4; -webkit-order:3; -ms-flex-order:3; order:3 } 466 | .order-last{ -webkit-box-ordinal-group:100000; -webkit-order:99999; -ms-flex-order:99999; order:99999 } 467 | 468 | .relative{ position:relative } 469 | .absolute{ position:absolute } 470 | .fixed{ position:fixed } 471 | 472 | .top-0{ top:0 } 473 | .right-0{ right:0 } 474 | .bottom-0{ bottom:0 } 475 | .left-0{ left:0 } 476 | 477 | .z1{ z-index: 1 } 478 | .z2{ z-index: 2 } 479 | .z3{ z-index: 3 } 480 | .z4{ z-index: 4 } 481 | 482 | .border{ 483 | border-style:solid; 484 | border-width: 1px; 485 | } 486 | 487 | .border-top{ 488 | border-top-style:solid; 489 | border-top-width: 1px; 490 | } 491 | 492 | .border-right{ 493 | border-right-style:solid; 494 | border-right-width: 1px; 495 | } 496 | 497 | .border-bottom{ 498 | border-bottom-style:solid; 499 | border-bottom-width: 1px; 500 | } 501 | 502 | .border-left{ 503 | border-left-style:solid; 504 | border-left-width: 1px; 505 | } 506 | 507 | .border-none{ border:0 } 508 | 509 | .rounded{ border-radius: 3px } 510 | .circle{ border-radius:50% } 511 | 512 | .rounded-top{ border-radius: 3px 3px 0 0 } 513 | .rounded-right{ border-radius: 0 3px 3px 0 } 514 | .rounded-bottom{ border-radius: 0 0 3px 3px } 515 | .rounded-left{ border-radius: 3px 0 0 3px } 516 | 517 | .not-rounded{ border-radius:0 } 518 | 519 | .hide{ 520 | position:absolute !important; 521 | height:1px; 522 | width:1px; 523 | overflow:hidden; 524 | clip:rect(1px, 1px, 1px, 1px); 525 | } 526 | 527 | @media (max-width: 40em){ 528 | .xs-hide{ display:none !important } 529 | } 530 | 531 | @media (min-width: 40em) and (max-width: 52em){ 532 | .sm-hide{ display:none !important } 533 | } 534 | 535 | @media (min-width: 52em) and (max-width: 64em){ 536 | .md-hide{ display:none !important } 537 | } 538 | 539 | @media (min-width: 64em){ 540 | .lg-hide{ display:none !important } 541 | } 542 | 543 | .display-none{ display:none !important } 544 | 545 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nkn-sdk-js 2 | 3 | [![GitHub license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/nknorg/nkn-sdk-js/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/nkn-sdk.svg?style=flat)](https://www.npmjs.com/package/nkn-sdk) [![CircleCI Status](https://circleci.com/gh/nknorg/nkn-sdk-js.svg?style=shield&circle-token=:circle-token)](https://circleci.com/gh/nknorg/nkn-sdk-js) [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](#contributing) 4 | 5 | ![nkn](logo.png) 6 | 7 | JavaScript implementation of NKN client and wallet SDK. The SDK consists of a 8 | few components: 9 | 10 | - [NKN Client](#client): Send and receive data for free between any NKN clients 11 | regardless their network condition without setting up a server or relying on 12 | any third party services. Data are end to end encrypted by default. Typically 13 | you might want to use [multiclient](#multiclient) instead of using client 14 | directly. 15 | 16 | - [NKN MultiClient](#multiclient): Send and receive data using multiple NKN 17 | clients concurrently to improve reliability and latency. In addition, it 18 | supports session mode, a reliable streaming protocol similar to TCP based 19 | on [ncp](https://github.com/nknorg/ncp-js). 20 | 21 | - [NKN Wallet](#wallet): Wallet SDK for [NKN 22 | blockchain](https://github.com/nknorg/nkn). It can be used to create wallet, 23 | transfer token to NKN wallet address, register name, subscribe to topic, 24 | etc. 25 | 26 | Advantages of using NKN client/multiclient for data transmission: 27 | 28 | - Network agnostic: Neither sender nor receiver needs to have public IP address 29 | or port forwarding. NKN clients only establish outbound (websocket) 30 | connections, so Internet access is all they need. This is ideal for client 31 | side peer to peer communication. 32 | 33 | - Top level security: All data are end to end authenticated and encrypted. No 34 | one else in the world except sender and receiver can see or modify the content 35 | of the data. The same public key is used for both routing and encryption, 36 | eliminating the possibility of man in the middle attack. 37 | 38 | - Decent performance: By aggregating multiple overlay paths concurrently, 39 | multiclient can get ~100ms end to end latency and 10+mbps end to end session 40 | throughput between international devices. 41 | 42 | - Everything is free, open source and decentralized. (If you are curious, node 43 | relay traffic for clients for free to earn mining rewards in NKN blockchain.) 44 | 45 | Documentation: 46 | [https://docs.nkn.org/nkn-sdk-js](https://docs.nkn.org/nkn-sdk-js). 47 | 48 | ## Install 49 | 50 | For npm: 51 | 52 | ```shell 53 | npm install nkn-sdk 54 | ``` 55 | 56 | And then in your code: 57 | 58 | ```javascript 59 | const nkn = require('nkn-sdk'); 60 | ``` 61 | 62 | or using ES6 import: 63 | 64 | ```javascript 65 | import nkn from 'nkn-sdk'; 66 | ``` 67 | 68 | For browser, use `dist/nkn.js` or `dist/nkn.min.js`. 69 | 70 | For environment where cryptographically secure random number generator is not 71 | natively implemented (e.g. React Native), see [Random bytes 72 | generation](#random-bytes-generation). 73 | 74 | ## Client 75 | 76 | NKN client provides the basic functions of sending and receiving data between 77 | NKN clients or topics regardless their network condition without setting up a 78 | server or relying on any third party services. Typically you might want to use 79 | [multiclient](#multiclient) instead of using client directly. 80 | 81 | Create a client with a generated key pair: 82 | 83 | ```javascript 84 | let client = new nkn.Client(); 85 | ``` 86 | 87 | Or with an identifier (used to distinguish different clients sharing the same 88 | key pair): 89 | 90 | ```javascript 91 | let client = new nkn.Client({ 92 | identifier: 'any-string', 93 | }); 94 | ``` 95 | 96 | Get client secret seed and public key: 97 | 98 | ```javascript 99 | console.log(client.getSeed(), client.getPublicKey()); 100 | ``` 101 | 102 | Create a client using an existing secret seed: 103 | 104 | ```javascript 105 | let client = new nkn.Client({ 106 | seed: '2bc5501d131696429264eb7286c44a29dd44dd66834d9471bd8b0eb875a1edb0', 107 | }); 108 | ``` 109 | 110 | Secret key should be kept **SECRET**! Never put it in version control system 111 | like here. 112 | 113 | By default the client will use bootstrap RPC server (for getting node address) 114 | provided by nkn.org. Any NKN full node can serve as a bootstrap RPC server. You 115 | can create a client using customized bootstrap RPC server: 116 | 117 | ```javascript 118 | let client = new nkn.Client({ 119 | rpcServerAddr: 'https://ip:port', 120 | }); 121 | ``` 122 | 123 | Get client NKN address, which is used to receive data from other clients: 124 | 125 | ```javascript 126 | console.log(client.addr); 127 | ``` 128 | 129 | Listen for connection established: 130 | 131 | ```javascript 132 | client.onConnect(() => { 133 | console.log('Client ready.'); 134 | }); 135 | ``` 136 | 137 | Send text message to other clients: 138 | 139 | ```javascript 140 | client.send( 141 | 'another-client-address', 142 | 'hello world!', 143 | ); 144 | ``` 145 | 146 | You can also send byte array directly: 147 | 148 | ```javascript 149 | client.send( 150 | 'another-client-address', 151 | Uint8Array.from([1,2,3,4,5]), 152 | ); 153 | ``` 154 | 155 | The destination address can also be a name registered using [wallet](#wallet). 156 | 157 | Publish text message to all subscribers of a topic (subscribe is done through 158 | [wallet](#wallet)): 159 | 160 | ```javascript 161 | client.publish( 162 | 'topic', 163 | 'hello world!', 164 | ); 165 | ``` 166 | 167 | Receive data from other clients: 168 | 169 | ```javascript 170 | client.onMessage(({ src, payload }) => { 171 | console.log('Receive message', payload, 'from', src); 172 | }); 173 | ``` 174 | 175 | If a valid data (string or Uint8Array) is returned at the end of the handler, 176 | the data will be sent back to sender as reply: 177 | 178 | ```javascript 179 | client.onMessage(({ src, payload }) => { 180 | return 'Well received!'; 181 | }); 182 | ``` 183 | 184 | Handler can also be an async function, and reply can be byte array as well: 185 | 186 | ```javascript 187 | client.onMessage(async ({ src, payload }) => { 188 | return Uint8Array.from([1,2,3,4,5]); 189 | }); 190 | ``` 191 | 192 | Note that if multiple message handlers are added, the result returned by the 193 | first handler (in the order of being added) will be sent as reply. 194 | 195 | The `send` method will return a Promise that will be resolved when sender 196 | receives a reply, or rejected if not receiving reply or acknowledgement within 197 | timeout period. Similar to message, reply can be either string or byte array: 198 | 199 | ```javascript 200 | client.send( 201 | 'another-client-address', 202 | 'hello world!', 203 | ).then((reply) => { 204 | // The reply here can be either string or Uint8Array 205 | console.log('Receive reply:', reply); 206 | }).catch((e) => { 207 | // This will most likely to be timeout 208 | console.log('Catch:', e); 209 | }); 210 | ``` 211 | 212 | Client receiving data will automatically send an acknowledgement back to sender 213 | if message handler returns `null` or `undefined` so that sender will be able to 214 | know if the packet has been delivered. On the sender's side, it's almost the 215 | same as receiving a reply, except that the Promise is resolved with `null`: 216 | 217 | ```javascript 218 | client.send( 219 | 'another-client-address', 220 | 'hello world!', 221 | ).then(() => { 222 | console.log('Receive ACK'); 223 | }).catch((e) => { 224 | // This will most likely to be timeout 225 | console.log('Catch:', e); 226 | }); 227 | ``` 228 | 229 | If handler returns `false`, no reply or ACK will be sent. 230 | 231 | Check [examples/client.js](examples/client.js) for complete examples and 232 | [https://docs.nkn.org/nkn-sdk-js](https://docs.nkn.org/nkn-sdk-js) for full 233 | documentation. 234 | 235 | ## MultiClient 236 | 237 | MultiClient creates multiple NKN client instances by adding identifier prefix 238 | (`__0__.`, `__1__.`, `__2__.`, ...) to a NKN address and send/receive packets 239 | concurrently. This will greatly increase reliability and reduce latency at the 240 | cost of more bandwidth usage (proportional to the number of clients). 241 | 242 | MultiClient basically has the same API as [client](#client), with a few 243 | additional initial configurations and session mode: 244 | 245 | ```javascript 246 | let multiclient = new nkn.MultiClient({ 247 | numSubClients: 4, 248 | originalClient: false, 249 | }); 250 | ``` 251 | 252 | where `originalClient` controls whether a client with original identifier 253 | (without adding any additional identifier prefix) will be created, and 254 | `numSubClients` controls how many sub-clients to create by adding prefix 255 | `__0__.`, `__1__.`, `__2__.`, etc. Using `originalClient: true` and 256 | `numSubClients: 0` is equivalent to using a standard NKN Client without any 257 | modification to the identifier. Note that if you use `originalClient: true` and 258 | `numSubClients` is greater than 0, your identifier should not starts with 259 | `__X__` where `X` is any number, otherwise you may end up with identifier 260 | collision. 261 | 262 | Any additional options will be passed to NKN client. 263 | 264 | MultiClient instance shares most of the public API as regular NKN client, see 265 | [client](#client) for usage and examples. If you need low-level property or API, 266 | you can use `multiclient.defaultClient` to get the default client and 267 | `multiclient.clients` to get all clients. 268 | 269 | Check [examples/client.js](examples/client.js) for complete examples and 270 | [https://docs.nkn.org/nkn-sdk-js](https://docs.nkn.org/nkn-sdk-js) for full 271 | documentation. 272 | 273 | ### Session 274 | 275 | In addition to the default packet mode, multiclient also supports session mode, 276 | a reliable streaming protocol similar to TCP based on 277 | [ncp](https://github.com/nknorg/ncp-js). 278 | 279 | Listens for incoming sessions (without `listen()` no sessions will be accepted): 280 | 281 | ```javascript 282 | multiclient.listen(); 283 | ``` 284 | 285 | Dial a session: 286 | 287 | ```javascript 288 | multiclient.dial('another-client-address').then((session) => { 289 | console.log(session.localAddr, 'dialed a session to', session.remoteAddr); 290 | }); 291 | ``` 292 | 293 | Accepts for incoming sessions: 294 | 295 | ```javascript 296 | multiclient.onSession((session) => { 297 | console.log(session.localAddr, 'accepted a session from', session.remoteAddr); 298 | }); 299 | ``` 300 | 301 | Write to session: 302 | 303 | ```javascript 304 | session.write(Uint8Array.from([1,2,3,4,5])).then(() => { 305 | console.log('write success'); 306 | }); 307 | ``` 308 | 309 | Read from session: 310 | 311 | ```javascript 312 | session.read().then((data) => { 313 | console.log('read', data); 314 | }); 315 | ``` 316 | 317 | `session.read` also accepts a `maxSize` parameter, e.g. `session.read(maxSize)`. 318 | If `maxSize > 0`, at most `maxSize` bytes will be returned. If `maxSize == 0` or 319 | not set, the first batch of received data will be returned. If `maxSize < 0`, 320 | all received data will be concatenated and returned together. 321 | 322 | Session can be converted to WebStream using `session.getReadableStream()` and 323 | `session.getWritableStream(closeSessionOnEnd = false)`. Note that WebStream is 324 | not fully supported by all browser, so you might need to polyfill it globally or 325 | setting `session.ReadableStream` and `session.WritableStream` constructors. 326 | 327 | Check [examples/session.js](examples/session.js) for complete example or try 328 | demo file transfer web app at [https://nftp.nkn.org](https://nftp.nkn.org) and 329 | its source code at 330 | [https://github.com/nknorg/nftp-js](https://github.com/nknorg/nftp-js). 331 | 332 | ## Wallet 333 | 334 | NKN Wallet SDK. 335 | 336 | Create a new wallet with a generated key pair: 337 | 338 | ```javascript 339 | let wallet = new nkn.Wallet({ password: 'password' }); 340 | ``` 341 | 342 | Create wallet from a secret seed: 343 | 344 | ```javascript 345 | let wallet = new nkn.Wallet({ 346 | seed: wallet.getSeed(), 347 | password: 'new-wallet-password', 348 | }); 349 | ``` 350 | 351 | Export wallet to JSON string: 352 | 353 | ```javascript 354 | let walletJson = wallet.toJSON(); 355 | ``` 356 | 357 | Load wallet from JSON and password: 358 | 359 | ```javascript 360 | let wallet = nkn.Wallet.fromJSON(walletJson, { password: 'password' }); 361 | ``` 362 | 363 | By default the wallet will use RPC server provided by nkn.org. Any NKN full node 364 | can serve as a RPC server. You can create a wallet using customized RPC server: 365 | 366 | ```javascript 367 | let wallet = new nkn.Wallet({ 368 | password: 'password', 369 | rpcServerAddr: 'https://ip:port', 370 | }); 371 | ``` 372 | 373 | Verify whether an address is a valid NKN wallet address: 374 | 375 | ```javascript 376 | console.log(nkn.Wallet.verifyAddress(wallet.address)); 377 | ``` 378 | 379 | Verify password of the wallet: 380 | 381 | ```javascript 382 | console.log(wallet.verifyPassword('password')); 383 | ``` 384 | 385 | Get balance of this wallet: 386 | 387 | ```javascript 388 | wallet.getBalance().then((value) => { 389 | console.log('Balance for this wallet is:', value.toString()); 390 | }); 391 | ``` 392 | 393 | Transfer token to another wallet address: 394 | 395 | ```javascript 396 | wallet.transferTo(wallet.address, 1, { fee: 0.1, attrs: 'hello world' }).then((txnHash) => { 397 | console.log('Transfer transaction hash:', txnHash); 398 | }); 399 | ``` 400 | 401 | Subscribe to a topic for this wallet for next 100 blocks (around 20 seconds per 402 | block), client using the same key pair (seed) as this wallet and same identifier 403 | as passed to `subscribe` will be able to receive messages from this topic: 404 | 405 | ```javascript 406 | wallet.subscribe('topic', 100, 'identifier', 'metadata', { fee: '0.1' }).then((txnHash) => { 407 | console.log('Subscribe transaction hash:', txnHash); 408 | }); 409 | ``` 410 | 411 | Check [examples/wallet.js](examples/wallet.js) for complete examples and 412 | [https://docs.nkn.org/nkn-sdk-js](https://docs.nkn.org/nkn-sdk-js) for full 413 | documentation. 414 | 415 | ## Random bytes generation 416 | 417 | By default, this library uses the same random bytes generator as 418 | [tweetnacl-js](https://github.com/dchest/tweetnacl-js). 419 | 420 | If a platform you are targeting doesn't implement secure random number 421 | generator, but you somehow have a cryptographically-strong source of entropy 422 | (not `Math.random`!), and you know what you are doing, you can plug it like 423 | this: 424 | 425 | ```javascript 426 | nkn.setPRNG(function(x, n) { 427 | // ... copy n random bytes into x ... 428 | }); 429 | ``` 430 | 431 | An example using node.js native crypto library: 432 | 433 | ```javascript 434 | crypto = require('crypto'); 435 | nkn.setPRNG(function(x, n) { 436 | var i, v = crypto.randomBytes(n); 437 | for (i = 0; i < n; i++) x[i] = v[i]; 438 | // clean up v 439 | }); 440 | ``` 441 | 442 | Note that `setPRNG` *completely replaces* internal random byte generator 443 | with the one provided. 444 | 445 | ## Disable WASM 446 | 447 | You can disable WASM and use pure JavaScript implementation by: 448 | 449 | ```javascript 450 | nkn.setDisableWASM(true); 451 | ``` 452 | 453 | ## Contributing 454 | 455 | **Can I submit a bug, suggestion or feature request?** 456 | 457 | Yes. Please open an issue for that. 458 | 459 | **Can I contribute patches?** 460 | 461 | Yes, we appreciate your help! To make contributions, please fork the repo, push 462 | your changes to the forked repo with signed-off commits, and open a pull request 463 | here. 464 | 465 | Please sign off your commit. This means adding a line "Signed-off-by: Name 466 | " at the end of each commit, indicating that you wrote the code and have 467 | the right to pass it on as an open source patch. This can be done automatically 468 | by adding -s when committing: 469 | 470 | ```shell 471 | git commit -s 472 | ``` 473 | 474 | ## Community 475 | 476 | - [Forum](https://forum.nkn.org/) 477 | - [Discord](https://discord.gg/c7mTynX) 478 | - [Telegram](https://t.me/nknorg) 479 | - [Reddit](https://www.reddit.com/r/nknblockchain/) 480 | - [Twitter](https://twitter.com/NKN_ORG) 481 | -------------------------------------------------------------------------------- /lib/client/webrtc.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | Object.defineProperty(exports, "__esModule", { 4 | value: true 5 | }); 6 | exports["default"] = void 0; 7 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); } 8 | function _regeneratorRuntime() { "use strict"; /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/facebook/regenerator/blob/main/LICENSE */ _regeneratorRuntime = function _regeneratorRuntime() { return e; }; var t, e = {}, r = Object.prototype, n = r.hasOwnProperty, o = Object.defineProperty || function (t, e, r) { t[e] = r.value; }, i = "function" == typeof Symbol ? Symbol : {}, a = i.iterator || "@@iterator", c = i.asyncIterator || "@@asyncIterator", u = i.toStringTag || "@@toStringTag"; function define(t, e, r) { return Object.defineProperty(t, e, { value: r, enumerable: !0, configurable: !0, writable: !0 }), t[e]; } try { define({}, ""); } catch (t) { define = function define(t, e, r) { return t[e] = r; }; } function wrap(t, e, r, n) { var i = e && e.prototype instanceof Generator ? e : Generator, a = Object.create(i.prototype), c = new Context(n || []); return o(a, "_invoke", { value: makeInvokeMethod(t, r, c) }), a; } function tryCatch(t, e, r) { try { return { type: "normal", arg: t.call(e, r) }; } catch (t) { return { type: "throw", arg: t }; } } e.wrap = wrap; var h = "suspendedStart", l = "suspendedYield", f = "executing", s = "completed", y = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} var p = {}; define(p, a, function () { return this; }); var d = Object.getPrototypeOf, v = d && d(d(values([]))); v && v !== r && n.call(v, a) && (p = v); var g = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(p); function defineIteratorMethods(t) { ["next", "throw", "return"].forEach(function (e) { define(t, e, function (t) { return this._invoke(e, t); }); }); } function AsyncIterator(t, e) { function invoke(r, o, i, a) { var c = tryCatch(t[r], t, o); if ("throw" !== c.type) { var u = c.arg, h = u.value; return h && "object" == _typeof(h) && n.call(h, "__await") ? e.resolve(h.__await).then(function (t) { invoke("next", t, i, a); }, function (t) { invoke("throw", t, i, a); }) : e.resolve(h).then(function (t) { u.value = t, i(u); }, function (t) { return invoke("throw", t, i, a); }); } a(c.arg); } var r; o(this, "_invoke", { value: function value(t, n) { function callInvokeWithMethodAndArg() { return new e(function (e, r) { invoke(t, n, e, r); }); } return r = r ? r.then(callInvokeWithMethodAndArg, callInvokeWithMethodAndArg) : callInvokeWithMethodAndArg(); } }); } function makeInvokeMethod(e, r, n) { var o = h; return function (i, a) { if (o === f) throw Error("Generator is already running"); if (o === s) { if ("throw" === i) throw a; return { value: t, done: !0 }; } for (n.method = i, n.arg = a;;) { var c = n.delegate; if (c) { var u = maybeInvokeDelegate(c, n); if (u) { if (u === y) continue; return u; } } if ("next" === n.method) n.sent = n._sent = n.arg;else if ("throw" === n.method) { if (o === h) throw o = s, n.arg; n.dispatchException(n.arg); } else "return" === n.method && n.abrupt("return", n.arg); o = f; var p = tryCatch(e, r, n); if ("normal" === p.type) { if (o = n.done ? s : l, p.arg === y) continue; return { value: p.arg, done: n.done }; } "throw" === p.type && (o = s, n.method = "throw", n.arg = p.arg); } }; } function maybeInvokeDelegate(e, r) { var n = r.method, o = e.iterator[n]; if (o === t) return r.delegate = null, "throw" === n && e.iterator["return"] && (r.method = "return", r.arg = t, maybeInvokeDelegate(e, r), "throw" === r.method) || "return" !== n && (r.method = "throw", r.arg = new TypeError("The iterator does not provide a '" + n + "' method")), y; var i = tryCatch(o, e.iterator, r.arg); if ("throw" === i.type) return r.method = "throw", r.arg = i.arg, r.delegate = null, y; var a = i.arg; return a ? a.done ? (r[e.resultName] = a.value, r.next = e.nextLoc, "return" !== r.method && (r.method = "next", r.arg = t), r.delegate = null, y) : a : (r.method = "throw", r.arg = new TypeError("iterator result is not an object"), r.delegate = null, y); } function pushTryEntry(t) { var e = { tryLoc: t[0] }; 1 in t && (e.catchLoc = t[1]), 2 in t && (e.finallyLoc = t[2], e.afterLoc = t[3]), this.tryEntries.push(e); } function resetTryEntry(t) { var e = t.completion || {}; e.type = "normal", delete e.arg, t.completion = e; } function Context(t) { this.tryEntries = [{ tryLoc: "root" }], t.forEach(pushTryEntry, this), this.reset(!0); } function values(e) { if (e || "" === e) { var r = e[a]; if (r) return r.call(e); if ("function" == typeof e.next) return e; if (!isNaN(e.length)) { var o = -1, i = function next() { for (; ++o < e.length;) if (n.call(e, o)) return next.value = e[o], next.done = !1, next; return next.value = t, next.done = !0, next; }; return i.next = i; } } throw new TypeError(_typeof(e) + " is not iterable"); } return GeneratorFunction.prototype = GeneratorFunctionPrototype, o(g, "constructor", { value: GeneratorFunctionPrototype, configurable: !0 }), o(GeneratorFunctionPrototype, "constructor", { value: GeneratorFunction, configurable: !0 }), GeneratorFunction.displayName = define(GeneratorFunctionPrototype, u, "GeneratorFunction"), e.isGeneratorFunction = function (t) { var e = "function" == typeof t && t.constructor; return !!e && (e === GeneratorFunction || "GeneratorFunction" === (e.displayName || e.name)); }, e.mark = function (t) { return Object.setPrototypeOf ? Object.setPrototypeOf(t, GeneratorFunctionPrototype) : (t.__proto__ = GeneratorFunctionPrototype, define(t, u, "GeneratorFunction")), t.prototype = Object.create(g), t; }, e.awrap = function (t) { return { __await: t }; }, defineIteratorMethods(AsyncIterator.prototype), define(AsyncIterator.prototype, c, function () { return this; }), e.AsyncIterator = AsyncIterator, e.async = function (t, r, n, o, i) { void 0 === i && (i = Promise); var a = new AsyncIterator(wrap(t, r, n, o), i); return e.isGeneratorFunction(r) ? a : a.next().then(function (t) { return t.done ? t.value : a.next(); }); }, defineIteratorMethods(g), define(g, u, "Generator"), define(g, a, function () { return this; }), define(g, "toString", function () { return "[object Generator]"; }), e.keys = function (t) { var e = Object(t), r = []; for (var n in e) r.push(n); return r.reverse(), function next() { for (; r.length;) { var t = r.pop(); if (t in e) return next.value = t, next.done = !1, next; } return next.done = !0, next; }; }, e.values = values, Context.prototype = { constructor: Context, reset: function reset(e) { if (this.prev = 0, this.next = 0, this.sent = this._sent = t, this.done = !1, this.delegate = null, this.method = "next", this.arg = t, this.tryEntries.forEach(resetTryEntry), !e) for (var r in this) "t" === r.charAt(0) && n.call(this, r) && !isNaN(+r.slice(1)) && (this[r] = t); }, stop: function stop() { this.done = !0; var t = this.tryEntries[0].completion; if ("throw" === t.type) throw t.arg; return this.rval; }, dispatchException: function dispatchException(e) { if (this.done) throw e; var r = this; function handle(n, o) { return a.type = "throw", a.arg = e, r.next = n, o && (r.method = "next", r.arg = t), !!o; } for (var o = this.tryEntries.length - 1; o >= 0; --o) { var i = this.tryEntries[o], a = i.completion; if ("root" === i.tryLoc) return handle("end"); if (i.tryLoc <= this.prev) { var c = n.call(i, "catchLoc"), u = n.call(i, "finallyLoc"); if (c && u) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } else if (c) { if (this.prev < i.catchLoc) return handle(i.catchLoc, !0); } else { if (!u) throw Error("try statement without catch or finally"); if (this.prev < i.finallyLoc) return handle(i.finallyLoc); } } } }, abrupt: function abrupt(t, e) { for (var r = this.tryEntries.length - 1; r >= 0; --r) { var o = this.tryEntries[r]; if (o.tryLoc <= this.prev && n.call(o, "finallyLoc") && this.prev < o.finallyLoc) { var i = o; break; } } i && ("break" === t || "continue" === t) && i.tryLoc <= e && e <= i.finallyLoc && (i = null); var a = i ? i.completion : {}; return a.type = t, a.arg = e, i ? (this.method = "next", this.next = i.finallyLoc, y) : this.complete(a); }, complete: function complete(t, e) { if ("throw" === t.type) throw t.arg; return "break" === t.type || "continue" === t.type ? this.next = t.arg : "return" === t.type ? (this.rval = this.arg = t.arg, this.method = "return", this.next = "end") : "normal" === t.type && e && (this.next = e), y; }, finish: function finish(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.finallyLoc === t) return this.complete(r.completion, r.afterLoc), resetTryEntry(r), y; } }, "catch": function _catch(t) { for (var e = this.tryEntries.length - 1; e >= 0; --e) { var r = this.tryEntries[e]; if (r.tryLoc === t) { var n = r.completion; if ("throw" === n.type) { var o = n.arg; resetTryEntry(r); } return o; } } throw Error("illegal catch attempt"); }, delegateYield: function delegateYield(e, r, n) { return this.delegate = { iterator: values(e), resultName: r, nextLoc: n }, "next" === this.method && (this.arg = t), y; } }, e; } 9 | function asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); } 10 | function _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, "next", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, "throw", n); } _next(void 0); }); }; } 11 | function _classCallCheck(a, n) { if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); } 12 | function _defineProperties(e, r) { for (var t = 0; t < r.length; t++) { var o = r[t]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); } } 13 | function _createClass(e, r, t) { return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { writable: !1 }), e; } 14 | function _defineProperty(e, r, t) { return (r = _toPropertyKey(r)) in e ? Object.defineProperty(e, r, { value: t, enumerable: !0, configurable: !0, writable: !0 }) : e[r] = t, e; } 15 | function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == _typeof(i) ? i : i + ""; } 16 | function _toPrimitive(t, r) { if ("object" != _typeof(t) || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != _typeof(i)) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } 17 | var PingData = "ping"; 18 | var PongData = "pong"; 19 | var Peer = exports["default"] = /*#__PURE__*/function () { 20 | function Peer(stunServerAddr) { 21 | _classCallCheck(this, Peer); 22 | _defineProperty(this, "config", void 0); 23 | _defineProperty(this, "pc", void 0); 24 | // peer connection 25 | _defineProperty(this, "dc", void 0); 26 | // data channel 27 | _defineProperty(this, "sdp", void 0); 28 | _defineProperty(this, "isConnected", void 0); 29 | // compatible to websocket 30 | _defineProperty(this, "onopen", void 0); 31 | _defineProperty(this, "onmessage", void 0); 32 | _defineProperty(this, "onclose", void 0); 33 | _defineProperty(this, "onerror", void 0); 34 | var config = { 35 | iceServers: [{ 36 | urls: stunServerAddr 37 | }] 38 | }; 39 | this.config = config; 40 | this.isConnected = false; 41 | } 42 | return _createClass(Peer, [{ 43 | key: "send", 44 | value: function send(data) { 45 | this.dc.send(data); 46 | } 47 | }, { 48 | key: "offer", 49 | value: function () { 50 | var _offer = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee2(label) { 51 | var _this = this; 52 | return _regeneratorRuntime().wrap(function _callee2$(_context2) { 53 | while (1) switch (_context2.prev = _context2.next) { 54 | case 0: 55 | return _context2.abrupt("return", new Promise(/*#__PURE__*/function () { 56 | var _ref = _asyncToGenerator(/*#__PURE__*/_regeneratorRuntime().mark(function _callee(resolve, reject) { 57 | return _regeneratorRuntime().wrap(function _callee$(_context) { 58 | while (1) switch (_context.prev = _context.next) { 59 | case 0: 60 | _context.prev = 0; 61 | if (!_this.pc || _this.pc.signalingState === "closed") { 62 | _this.pc = new RTCPeerConnection(_this.config); 63 | } 64 | _this.pc.oniceconnectionstatechange = function () { 65 | if (_this.pc.iceConnectionState === "failed") { 66 | _this.pc.restartIce(); 67 | } 68 | }; 69 | _this.dc = _this.pc.createDataChannel(label); 70 | _this.dc.addEventListener("open", function () { 71 | _this.isConnected = true; 72 | if (_this.onopen) { 73 | _this.onopen(); 74 | } 75 | }); 76 | _this.dc.addEventListener("message", function (e) { 77 | if (e.data == PongData) { 78 | if (_this.pongHandler != null) { 79 | _this.pongHandler(PongData); 80 | } else { 81 | console.log("Pong handler not set"); 82 | } 83 | return; 84 | } else if (e.data == PingData) { 85 | _this.dc.send(PongData); 86 | return; 87 | } 88 | if (_this.onmessage) { 89 | _this.onmessage(e); 90 | } 91 | }); 92 | _this.dc.addEventListener("close", function (event) { 93 | _this.isConnected = false; 94 | if (_this.onclose) { 95 | _this.onclose(); 96 | } 97 | }); 98 | _this.dc.addEventListener("error", function (event) { 99 | if (_this.onerror) { 100 | _this.onerror(event); 101 | } 102 | }); 103 | _context.next = 10; 104 | return _this.pc.createOffer(); 105 | case 10: 106 | _context.next = 12; 107 | return _this.pc.setLocalDescription(); 108 | case 12: 109 | _this.sdp = btoa(JSON.stringify(_this.pc.localDescription)); 110 | resolve(_this.sdp); 111 | _context.next = 19; 112 | break; 113 | case 16: 114 | _context.prev = 16; 115 | _context.t0 = _context["catch"](0); 116 | reject(_context.t0); 117 | case 19: 118 | case "end": 119 | return _context.stop(); 120 | } 121 | }, _callee, null, [[0, 16]]); 122 | })); 123 | return function (_x2, _x3) { 124 | return _ref.apply(this, arguments); 125 | }; 126 | }())); 127 | case 1: 128 | case "end": 129 | return _context2.stop(); 130 | } 131 | }, _callee2); 132 | })); 133 | function offer(_x) { 134 | return _offer.apply(this, arguments); 135 | } 136 | return offer; 137 | }() 138 | }, { 139 | key: "setRemoteDescription", 140 | value: function setRemoteDescription(sdp) { 141 | var answer = JSON.parse(atob(sdp)); 142 | return this.pc.setRemoteDescription(answer); 143 | } 144 | }, { 145 | key: "close", 146 | value: function close() { 147 | this.dc.close(); 148 | this.pc.close(); 149 | } 150 | }]); 151 | }(); --------------------------------------------------------------------------------