UNPKG

swtc-lib

Version:

websocket access for jingtum blockchain

830 lines 31.7 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.Remote = void 0; const account_1 = require("./account"); const events_1 = require("events"); const orderbook_1 = require("./orderbook"); const request_1 = require("./request"); const server_1 = require("./server"); const transaction_1 = require("@swtc/transaction"); const lru_cache_1 = __importDefault(require("lru-cache")); const sha1_1 = __importDefault(require("sha1")); const Wallet = transaction_1.Transaction.Wallet; const utils = transaction_1.Transaction.utils; function getRelationType(type) { switch (type) { case "trust": return 0; case "authorize": return 1; case "freeze": return 3; default: return null; } } class Remote extends events_1.EventEmitter { constructor(options = { local_sign: true }) { super(); this.AbiCoder = null; this.Tum3 = null; this._url = `ws://${Remote.XLIB.default_ws || "ws.swtclib.ca:5020"}`; this._url_failover = `ws://${Remote.XLIB.default_ws_failover || "ws-failover.swtclib.ca:5020"}`; this._solidity = false; this._timeout = 20 * 1000; this._failover = false; this.AlethEvent = function (options) { const request = new request_1.Request(this, "aleth_eventlog", data => data); if (typeof options !== "object") { request.message.obj = new Error("invalid options type"); return request; } const des = options.destination; const abi = options.abi; if (!utils.isValidAddress(des)) { request.message.des = new Error("invalid destination"); return request; } if (!abi) { request.message.abi = new Error("not found abi"); return request; } if (!Array.isArray(abi)) { request.message.params = new Error("invalid abi: type error."); return request; } this.abi = abi; request.message.Destination = des; return request; }; const _opts = options || {}; if (_opts.hasOwnProperty("timeout")) { const timeout = Number(_opts.timeout); this._timeout = timeout > 5 * 1000 ? timeout : 20 * 1000; } this._local_sign = true; if (_opts.solidity) { this._solidity = true; try { this.AbiCoder = require("tum3-eth-abi").AbiCoder; this.Tum3 = require("swtc-tum3"); } catch (error) { throw Error("install tum3-eth-abi and swtc-tum3 to enable solidity support"); } } if (!_opts.hasOwnProperty("server")) { this._failover = true; } else { if (typeof _opts.server !== "string") { this.type = new TypeError("server config not supplied"); return this; } this._url = _opts.server; } if (_opts.hasOwnProperty("server_failover")) { if (typeof _opts.server_failover !== "string") { this.type = new TypeError("server_failover config not supplied"); return this; } this._url_failover = _opts.server_failover; } if (_opts.failover) { this._failover = true; } this._server = new server_1.Server(this, this._url); this._status = { ledger_index: 0 }; this._requests = {}; this._token = options.token || Wallet.token || "swt"; this._issuer = options.issuer || Wallet.config.issuer || "jGa9J9TkqtBcUoHe2zqhVFFbgUVED6o9or"; this._cache = new lru_cache_1.default({ max: 100, maxAge: 1000 * 60 * 5 }); this._paths = new lru_cache_1.default({ max: 100, maxAge: 1000 * 60 * 5 }); this.on("newListener", type => { if (!this._server.isConnected()) return; if (type === "removeListener") return; if (type === "transactions") { this.subscribe("transactions").submit(); } if (type === "ledger_closed") { this.subscribe("ledger").submit(); } }); this.on("removeListener", type => { if (!this._server.isConnected()) return; if (type === "transactions") { this.unsubscribe("transactions").submit(); } if (type === "ledger_closed") { this.unsubscribe("ledger").submit(); } }); } config() { return { _local_sign: this._local_sign, _failover: this._failover, _url: this._url, _url_failover: this._url_failover, _url_active: this._server._url, _token: this._token, _issuer: this._issuer, _solidity: this._solidity, _timeout: this._timeout }; } makeCurrency(currency = this._token, issuer = this._issuer) { return Wallet.makeCurrency(currency, issuer); } makeAmount(value = 1, currency = this._token, issuer = this._issuer) { return Wallet.makeAmount(value, currency, issuer); } connect(callback) { if (!this._server) return callback("server not ready"); this._server.connect(callback); } connectPromise() { return new Promise((resolve, reject) => { if (!this._server) return reject(new Error("server not ready")); this._server .connectPromise() .then(result => resolve(result)) .catch(error => { if (!this._failover) { reject(error); } else { if (this._server._url === this._url) { this._server = new server_1.Server(this, this._url_failover); } else { this._server = new server_1.Server(this, this._url); } this._server .connectPromise() .then(result_failover => resolve(result_failover)) .catch(error_failover => reject(error_failover)); } }); }); } disconnect() { if (!this._server) return; this._server.disconnect(); } isConnected() { return this._server.isConnected(); } _handleMessage(e) { let data; let try_again = false; try { data = JSON.parse(e.data); if (typeof data !== "object") return; switch (data.type) { case "ledgerClosed": this._handleLedgerClosed(data); break; case "serverStatus": this._handleServerStatus(data); break; case "response": this._handleResponse(data); break; case "transaction": this._handleTransaction(data); break; case "path_find": this._handlePathFind(data); break; } } catch (error) { try_again = true; } if (try_again) { try { data = JSON.parse(e); if (typeof data !== "object") return; switch (data.type) { case "ledgerClosed": this._handleLedgerClosed(data); break; case "serverStatus": this._handleServerStatus(data); break; case "response": this._handleResponse(data); break; case "transaction": this._handleTransaction(data); break; case "path_find": this._handlePathFind(data); break; } } catch (error) { try_again = false; } } } _submit(command, data, filter, callback) { const req_id = this._server.sendMessage(command, data); this._requests[req_id] = { command, data, filter, callback }; } requestServerInfo() { return new request_1.Request(this, "server_info", data => { return { complete_ledgers: data.info.complete_ledgers, ledger: data.info.validated_ledger.hash, public_key: data.info.pubkey_node, state: data.info.server_state, peers: data.info.peers, version: "skywelld-" + data.info.build_version }; }); } requestPeers() { return new request_1.Request(this, "peers", data => { return data; }); } requestLedgerClosed() { return new request_1.Request(this, "ledger_closed", data => { return { ledger_hash: data.ledger_hash, ledger_index: data.ledger_index }; }); } requestLedger(options) { const cmd = "ledger"; let filter = true; const request = new request_1.Request(this, cmd, data => { const ledger = data.ledger || data.closed.ledger; if (!filter) { return ledger; } return { accepted: ledger.accepted, ledger_hash: ledger.hash, ledger_index: ledger.ledger_index, parent_hash: ledger.parent_hash, close_time: ledger.close_time_human, total_coins: ledger.total_coins }; }); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } if (options.ledger_index && !/^[1-9]\d{0,9}$/.test(String(options.ledger_index))) { request.message.ledger_index = new Error("invalid ledger_index"); return request; } if (options.ledger_index) { request.message.ledger_index = Number(options.ledger_index); } if (utils.isValidHash(options.ledger_hash)) { request.message.ledger_hash = options.ledger_hash; } if ("full" in options && typeof options.full === "boolean") { request.message.full = options.full; filter = false; } if ("expand" in options && typeof options.expand === "boolean") { request.message.expand = options.expand; filter = false; } if ("transactions" in options && typeof options.transactions === "boolean") { request.message.transactions = options.transactions; filter = false; } if ("accounts" in options && typeof options.accounts === "boolean") { request.message.accounts = options.accounts; filter = false; } return request; } requestAccounts(options) { const request = new request_1.Request(this, "account_count"); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } if (options.ledger_index && !/^[1-9]\d{0,9}$/.test(String(options.ledger_index))) { request.message.ledger_index = new Error("invalid ledger_index"); return request; } if (options.ledger_index) { request.message.ledger_index = Number(options.ledger_index); } if (utils.isValidHash(options.ledger_hash)) { request.message.ledger_hash = options.ledger_hash; } if (options.marker) { request.message.marker = options.marker; } return request; } requestTx(options) { const request = new request_1.Request(this, "tx"); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } const hash = options.hash; if (!utils.isValidHash(hash)) { request.message.hash = new Error("invalid tx hash"); return request; } request.message.transaction = hash; return request; } requestAccountInfo(options) { const request = new request_1.Request(this); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } return this.__requestAccount("account_info", options, request); } requestAccountTums(options) { const request = new request_1.Request(this); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } return this.__requestAccount("account_currencies", options, request); } requestAccountRelations(options) { const request = new request_1.Request(this); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } if (!~transaction_1.Transaction.RelationTypes.indexOf(options.type)) { request.message.relation_type = new Error("invalid realtion type"); return request; } switch (options.type) { case "trust": return this.__requestAccount("account_lines", options, request); case "authorize": case "freeze": return this.__requestAccount("account_relation", options, request); } request.message.msg = new Error("relation should not go here"); return request; } requestAccountOffers(options) { const request = new request_1.Request(this); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } return this.__requestAccount("account_offers", options, request); } requestAccountTx(options) { const request = new request_1.Request(this, "account_tx", data => { const results = []; for (const data_transaction of data.transactions) { results.push(utils.processTx(data_transaction, options.account)); } data.transactions = results; return data; }); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } if (!utils.isValidAddress(options.account)) { request.message.account = new Error("account parameter is invalid"); return request; } request.message.account = options.account; if (options.ledger_min && Number(options.ledger_min)) { request.message.ledger_index_min = Number(options.ledger_min); } else { request.message.ledger_index_min = 0; } if (options.ledger_max && Number(options.ledger_max)) { request.message.ledger_index_max = Number(options.ledger_max); } else { request.message.ledger_index_max = -1; } if (options.limit && Number(options.limit)) { request.message.limit = Number(options.limit); } if (options.offset && Number(options.offset)) { request.message.offset = Number(options.offset); } if (typeof options.marker === "object" && !Number.isNaN(Number(options.marker.ledger)) && !Number.isNaN(Number(options.marker.seq))) { request.message.marker = options.marker; } if (options.forward && typeof options.forward === "boolean") { request.message.forward = options.forward; } return request; } requestOrderBook(options) { const request = new request_1.Request(this, "book_offers"); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } const taker_gets = options.taker_gets || options.pays; if (!utils.isValidAmount0(taker_gets)) { request.message.taker_gets = new Error("invalid taker gets amount"); return request; } const taker_pays = options.taker_pays || options.gets; if (!utils.isValidAmount0(taker_pays)) { request.message.taker_pays = new Error("invalid taker pays amount"); return request; } options.limit = Number(options.limit); if (!options.limit) { options.limit = 300; } request.message.taker_gets = taker_gets; request.message.taker_pays = taker_pays; request.message.taker = options.taker ? options.taker : Wallet.getAccountOne(); request.message.limit = options.limit; return request; } requestBrokerage(options) { const request = new request_1.Request(this, "Fee_Info"); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } const account = options.account; if (!utils.isValidAddress(account)) { request.message.account = new Error("account parameter is invalid"); return request; } request.message.account = account; request.message.ledger_index = "validated"; return request; } requestSignerList(options) { const request = new request_1.Request(this, "account_objects"); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } const account = options.account; if (!utils.isValidAddress(account)) { request.message.account = new Error("account parameter is invalid"); return request; } request.message.account = account; return request; } requestPathFind(options) { const request = new request_1.Request(this, "path_find", data => { const request2 = new request_1.Request(this, "path_find"); request2.message.subcommand = "close"; request2.submit(); const _result = []; for (const item of data.alternatives) { const key = sha1_1.default(JSON.stringify(item)); this._paths.set(key, { path: JSON.stringify(item.paths_computed), choice: item.source_amount }); _result.push({ choice: utils.parseAmount(item.source_amount), key }); } return _result; }); if (options === null || typeof options !== "object") { request.message.type = new Error("invalid options type"); return request; } const account = options.account; const dest = options.destination; const amount = options.amount; if (!utils.isValidAddress(account)) { request.message.source_account = new Error("invalid source account"); return request; } if (!utils.isValidAddress(dest)) { request.message.destination_account = new Error("invalid destination account"); return request; } if (!utils.isValidAmount(amount)) { request.message.destination_amount = new Error("invalid amount"); return request; } request.message.subcommand = "create"; request.message.source_account = account; request.message.destination_account = dest; request.message.destination_amount = utils.ToAmount(amount); return request; } buildPaymentTx(options) { return transaction_1.Transaction.buildPaymentTx(options, this); } initContract(options) { return transaction_1.Transaction.initContractTx(options, this); } invokeContract(options) { return transaction_1.Transaction.invokeContractTx(options, this); } initContractTx(options) { return transaction_1.Transaction.initContractTx(options, this); } invokeContractTx(options) { return transaction_1.Transaction.invokeContractTx(options, this); } buildContractInitTx(options) { return transaction_1.Transaction.initContractTx(options, this); } buildContractInvokeTx(options) { return transaction_1.Transaction.invokeContractTx(options, this); } buildContractDeployTx(options) { return transaction_1.Transaction.deployContractTx(options, this); } buildContractCallTx(options) { return transaction_1.Transaction.callContractTx(options, this); } deployContractTx(options) { return transaction_1.Transaction.deployContractTx(options, this); } callContractTx(options) { return transaction_1.Transaction.callContractTx(options, this); } buildSignTx(options) { return transaction_1.Transaction.buildSignTx(options, this); } buildBrokerageTx(options) { return transaction_1.Transaction.buildBrokerageTx(options, this); } buildRelationTx(options) { return transaction_1.Transaction.buildRelationTx(options, this); } buildAccountSetTx(options) { return transaction_1.Transaction.buildAccountSetTx(options, this); } buildOfferCreateTx(options) { return transaction_1.Transaction.buildOfferCreateTx(options, this); } buildOfferCancelTx(options) { return transaction_1.Transaction.buildOfferCancelTx(options, this); } buildSignerListTx(options) { return transaction_1.Transaction.buildSignerListTx(options, this); } buildSignFirstTx(options) { return transaction_1.Transaction.buildSignFirstTx(options); } buildSignOtherTx(options) { return transaction_1.Transaction.buildSignOtherTx(options, this); } buildMultisignedTx(tx_json) { return transaction_1.Transaction.buildMultisignedTx(tx_json, this); } buildTx(tx_json) { return transaction_1.Transaction.buildTx(tx_json, this); } subscribe(streams) { const request = new request_1.Request(this, "subscribe"); if (streams) { request.message.streams = Array.isArray(streams) ? streams : [streams]; } return request; } unsubscribe(streams) { const request = new request_1.Request(this, "unsubscribe"); if (streams) { request.message.streams = Array.isArray(streams) ? streams : [streams]; } return request; } createAccountStub() { return new account_1.Account(this); } createOrderBookStub() { return new orderbook_1.OrderBook(this); } _handleLedgerClosed(data) { if (data.ledger_index > this._status.ledger_index) { this._status.ledger_index = data.ledger_index; this._status.ledger_time = data.ledger_time; this._status.reserve_base = data.reserve_base; this._status.reserve_inc = data.reserve_inc; this._status.fee_base = data.fee_base; this._status.fee_ref = data.fee_ref; this.emit("ledger_closed", data); } } _handleServerStatus(data) { this._updateServerStatus(data); this.emit("server_status", data); } _updateServerStatus(data) { this._status.load_base = data.load_base; this._status.load_factor = data.load_factor; if (data.pubkey_node) { this._status.pubkey_node = data.pubkey_node; } this._status.server_status = data.server_status; const online = ~server_1.Server.onlineStates.indexOf(data.server_status); this._server._setState(online ? "online" : "offline"); } _handleResponse(data) { const req_id = data.id; if (typeof req_id !== "number" || req_id < 0 || req_id > this._requests.length) { return; } const request = this._requests[req_id]; if (request.data && request.data.abi) { data.abi = request.data.abi; } delete this._requests[req_id]; delete data.id; if (data.result && data.status === "success" && data.result.server_status) { this._updateServerStatus(data.result); } if (this._solidity) { if (data.status === "success") { const result = request.filter(data.result); if (result.ContractState && result.tx_json.TransactionType === "AlethContract" && result.tx_json.Method === 1) { const method = utils.hexToString(result.tx_json.MethodSignature); result.func = method.substring(0, method.indexOf("(")); result.func_parms = method .substring(method.indexOf("(") + 1, method.indexOf(")")) .split(","); if (result.func_parms.length === 1 && result.func_parms[0] === "") { result.func_parms = []; } if (result.engine_result === "tesSUCCESS") { const abi = new this.AbiCoder(); const types = utils.getTypes(data.abi, result.func); result.ContractState = abi.decodeParameters(types, result.ContractState); types.forEach((type, i) => { if (type === "address") { const adr = result.ContractState[i].slice(2); const buf = new Buffer(20); buf.write(adr, 0, "hex"); result.ContractState[i] = Wallet.KeyPair.addressCodec.encodeAddress(buf); } }); } } if (result.AlethLog) { const logValue = []; const item = { address: "", data: {} }; const logs = result.AlethLog; logs.forEach(log => { const _log = JSON.parse(log.item); const _adr = _log.address.slice(2); const buf = new Buffer(20); buf.write(_adr, 0, "hex"); item.address = Wallet.KeyPair.addressCodec.encodeAddress(buf); const abi = new this.AbiCoder(); data.abi .filter(json => { return json.type === "event"; }) .map(json => { const types = json.inputs.map(input => { return input.type; }); const foo = json.name + "(" + types.join(",") + ")"; if (abi.encodeEventSignature(foo) === _log.topics[0]) { const data2 = abi.decodeLog(json.inputs, _log.data, _log.topics); json.inputs.forEach((input, i) => { if (input.type === "address") { const _adr2 = data2[i].slice(2); const buf2 = new Buffer(20); buf2.write(_adr2, 0, "hex"); item.data[i] = Wallet.KeyPair.addressCodec.encodeAddress(buf2); } else { item.data[i] = data2[i]; } }); } }); logValue.push(item); }); result.AlethLog = logValue; } request && request.callback(null, result); } else if (data.status === "error") { request && request.callback(data.error_message || data.error_exception); } } else { if (data.status === "success") { const result = request.filter(data.result); request && request.callback(null, result); } else if (data.status === "error") { request && request.callback(data.error_message || data.error_exception); } } } _handleTransaction(data) { const tx = data.transaction.hash; if (this._cache.get(tx)) return; this._cache.set(tx, 1); this.emit("transactions", data); } _handlePathFind(data) { this.emit("path_find", data); } __requestAccount(type, options, request) { request._command = type; const account = options.account; const ledger = options.ledger; const peer = options.peer; let limit = options.limit; const marker = options.marker; request.message.relation_type = getRelationType(options.type); if (account) { if (!utils.isValidAddress(account)) { request.message.account = new Error("invalid account"); return request; } else { request.message.account = account; } } request.selectLedger(ledger); if (peer && utils.isValidAddress(peer)) { request.message.peer = peer; } if (Number(limit)) { limit = Number(limit); if (limit < 0) limit = 0; if (limit > 1e9) limit = 1e9; request.message.limit = limit; } if (marker) { request.message.marker = marker; } return request; } } exports.Remote = Remote; Remote.Wallet = Wallet; Remote.Account = account_1.Account; Remote.OrderBook = orderbook_1.OrderBook; Remote.Transaction = transaction_1.Transaction; Remote.utils = utils; Remote.XLIB = Wallet.config.XLIB || {}; //# sourceMappingURL=remote.js.map