UNPKG

seele-sdk-javascript

Version:
383 lines (362 loc) 13.1 kB
"use strict"; const SEELE_SUBSCRIPTION_TOPICS = {"newBlockHeaders":"newBlockHeaders", "transaction":"transaction", "debt" : "debt", "event":"event"} ; const seele = require("./rpc.js"); const utils = require("./key"); const EventEmitter = require("eventemitter3") const web3abi = require("web3-eth-abi") /**@class */ class Subscription { constructor(options) { this.topicList = SEELE_SUBSCRIPTION_TOPICS; this.id = new Date().getTime(); this.host = options.host; this.topics = {}; } sub(topicName,topicOptions){ console.log("call sub",topicName); if ( SEELE_SUBSCRIPTION_TOPICS[topicName] == undefined) { console.log("invalid topic name") return } let eventEmitter = new EventEmitter(); if (topicName == SEELE_SUBSCRIPTION_TOPICS.newBlockHeaders){ if (this.topics[topicName]){ clearInterval(this.topics[topicName]) } let subId = listenBlockHeaders(this.host,topicOptions,eventEmitter) this.topics[topicName] = subId }else if (topicName == SEELE_SUBSCRIPTION_TOPICS.event){ if (this.topics[topicName]){ clearInterval(this.topics[topicName]) } let subId = listenLogs(this.host,topicOptions,eventEmitter) if (typeof(subId)=="string"){ console.log("sub topic failed"); }else{ this.topics[topicName] = subId } }else if (topicName ==SEELE_SUBSCRIPTION_TOPICS.transaction){ if (this.topics[topicName]){ clearInterval(this.topics[topicName]) } let subId = listenTransaction(this.host,topicOptions,eventEmitter) if (typeof(subId)=="string"){ console.log("sub topic failed"); }else{ this.topics[topicName] = subId } }else if (topicName ==SEELE_SUBSCRIPTION_TOPICS.debt){ if (this.topics[topicName]){ clearInterval(this.topics[topicName]) } let subId = listenDebt(this.host,topicOptions,eventEmitter) if (typeof(subId)=="string"){ console.log("sub topic failed"); }else{ this.topics[topicName] = subId } } return eventEmitter; } unsub(topicName){ if (this.topics[topicName]){ clearInterval(this.topics[topicName]); delete this.topics[topicName]; return true; }else if (SEELE_SUBSCRIPTION_TOPICS[topicName]==undefined) { console.log("topic name invalid:"+topicName); return false; }else{ console.log("topic not subscribed:"+topicName); return false; } } } let listenBlockHeaders = function(host,options,eventEmitter){ //options.startHeight: start listen from this block height let startHeight = -1; if (options != undefined && options.startHeight != undefined) { startHeight = options.startHeight; } let blockHeight = startHeight return setInterval(function () { let seeleJSONRPC = new seele(host); try { let result = seeleJSONRPC.sendSync("getBlock", '', blockHeight, false) if (result.header!=undefined && result.header.Height!=undefined){ eventEmitter.emit('data',result.header); blockHeight = result.header.Height + 1; }else{ eventEmitter.emit('error',"no header info") } }catch (e) { eventEmitter.emit('error',e.toString()) } },15*1000) } let validateLogsTopicParams = function (host,options){ if (options == undefined) { return new Error("option is undefined"); } let contractAddress = options.contractAddress; if (contractAddress == undefined) { return new Error("option contractAddress is undefined"); } let abiJSON = options.abiJSON; if (abiJSON == undefined) { return new Error("option abiJSON is undefined"); } let eventName = options.eventName; if (eventName == undefined) { return new Error("option eventName is undefined"); } let startHeight = -1; let seeleJSONRPC = new seele(host); if (options.startHeight != undefined) { startHeight = options.startHeight; }else{ //get current block height as startHeight let retryTimes = 0; do{ try{ let result = seeleJSONRPC.sendSync("getBlock","",-1,false); if (result.header!=undefined && result.header.Height!=undefined){ options.startHeight = result.header.Height; startHeight = options.startHeight } }catch (e) { retryTimes++; if (retryTimes>=10){ return e; } } }while(startHeight==-1); } return null; } let listenLogs = function (host,options,eventEmitter) { let err = validateLogsTopicParams(host,options); if (err != null && err != ""){ return err.toString(); } let blockHeight = options.startHeight; let seeleJSONRPC = new seele(host); return setInterval(function () { try { let result = seeleJSONRPC.sendSync("getLogs", blockHeight, options.contractAddress, options.abiJSON, options.eventName) if (result != undefined && result.length > 0){ console.log(result); eventEmitter.emit('data',result); } blockHeight = blockHeight + 1; }catch (e) { eventEmitter.emit('error',e.toString()) } },1*1000) } let validateTransactionTopicParams = function (host,options){ if (options == undefined) { return new Error("option is undefined"); } let from = options.from; let to = options.to; let account = options.account; if (from != undefined && !utils.valid(from)){ return new Error("option from address is invalid"); } if (to != undefined && !utils.valid(to)){ return new Error("option to address is invalid"); } if (account != undefined && !utils.valid(account)){ return new Error("option to address is invalid"); } let txhash = options.txhash; if (txhash == undefined && from == undefined && to ==undefined && account == undefined){ return new Error("subscription transaction should define at least one option:from, to, or txhash, account"); } let startHeight = -1; let seeleJSONRPC = new seele(host); if (options.startHeight != undefined) { startHeight = options.startHeight; }else{ //get current block height as startHeight let retryTimes = 0; do{ try{ let result = seeleJSONRPC.sendSync("getBlock",host,"",-1,false); if (result.header!=undefined && result.header.Height!=undefined){ options.startHeight = result.header.Height; startHeight = options.startHeight } }catch (e) { retryTimes++; if (retryTimes>=10){ return e; } } }while(startHeight==-1); } return null; } let listenTransaction = function(host, options, eventEmitter) { let err = validateTransactionTopicParams(host,options); if (err != null && err != ""){ return err.toString(); } let blockHeight = options.startHeight; let seeleJSONRPC = new seele(host); return setInterval(function () { try { console.log("blockHeight:"+blockHeight); let result = seeleJSONRPC.sendSync("getBlockByHeight", blockHeight, true) if (result != undefined ){ // first check txhash if (options.txhash != undefined){ let tx = filterTxhashFromBlock(result,options.txhash); if(tx != "") { eventEmitter.emit('data',tx); } }else if(options.account != undefined){ // second check account ( from or to ), txs or debts let txs = filterAccountTxsFromBlock(result,options.account); if(txs.length > 0) { eventEmitter.emit('data',result); } }else{ // third check from or to let txs = filterAddressTxsFromBlock(result,options.from,options.to); if(txs.length > 0) { eventEmitter.emit('data',result); } } } blockHeight = blockHeight + 1; }catch (e) { eventEmitter.emit('error',e.toString()) } },5*1000) } let validateDebtTopicParams = function (host,options){ if (options == undefined) { return new Error("option is undefined"); } let account = options.account; if (account != undefined && !utils.valid(account)){ return new Error("option to address is invalid"); } let debthash = options.debthash; if (debthash == undefined && account == undefined){ return new Error("subscription debt should define at least one option:debthash, account"); } let startHeight = -1; let seeleJSONRPC = new seele(host,2); if (options.startHeight != undefined) { startHeight = options.startHeight; }else{ //get current block height as startHeight let retryTimes = 0; do{ try{ let result = seeleJSONRPC.sendSync("getBlock",host,"",-1,false); if (result.header!=undefined && result.header.Height!=undefined){ options.startHeight = result.header.Height; startHeight = options.startHeight } }catch (e) { retryTimes++; if (retryTimes>=10){ return e; } } }while(startHeight==-1); } return null; } let listenDebt = function(host, options, eventEmitter) { let err = validateDebtTopicParams(host,options); if (err != null && err != ""){ return err.toString(); } let blockHeight = options.startHeight; let seeleJSONRPC = new seele(host); return setInterval(function () { try { let result = seeleJSONRPC.sendSync("getBlockByHeight", blockHeight, true) if (result != undefined ){ // first check txhash if (options.debthash != undefined){ let debt = filterDebthashFromBlock(result,options.debthash); if(debt != "") { eventEmitter.emit('data',debt); } }else if(options.account != undefined){ // second check account let debts = filterAccountDebtsFromBlock(result,options.account); if(debts.length > 0) { eventEmitter.emit('data',debts); } } } blockHeight = blockHeight + 1; }catch (e) { eventEmitter.emit('error',e.toString()) } },5*1000) } function filterTxhashFromBlock(block,txhash){ let txs = block.transactions; for (var i=0; i< txs.length; i++){ if(txs[i].hash==txhash){ return txs[i]; } } return ""; } function filterAccountTxsFromBlock(block,account){ let txs = block.transactions; let retTxs = []; for (var i=0; i< txs.length; i++){ if(txs[i].from==account || txs[i].to==account){ retTxs.push(txs[i]); } } return retTxs; } function filterAddressTxsFromBlock(block,from,to) { let txs = block.transactions; let retTxs = []; for (var i=0; i< txs.length; i++){ if(from != undefined && to == undefined && txs[i].from==from){ // only filter from retTxs.push(txs[i]); }else if (from == undefined && to != undefined && txs[i].to==to){ // only filter to retTxs.push(txs[i]); }else if (from != undefined && to != undefined && txs[i].from==from && txs[i].to==to){ retTxs.push(txs[i]); } // filter both from and to } return retTxs; } function filterDebthashFromBlock(block,debthash){ let debts = block.debts; for (var i=0; i< debts.length; i++){ if(debts[i].Hash==debthash){ return debts[i]; } } return ""; } function filterAccountDebtsFromBlock(block,account){ let debts = block.debts; let retDebts = []; for (var i=0; i< debts.length; i++){ if(debts[i].Data.Account==account){ retDebts.push(debts[i]); } } return retDebts; } module.exports = Subscription;