mirror of https://github.com/lukechilds/Agama.git
committed by
GitHub
19 changed files with 4381 additions and 2736 deletions
@ -1,119 +0,0 @@ |
|||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> |
|||
<html> |
|||
<head> |
|||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1"> |
|||
<link rel="stylesheet" href="EasyDEX-GUI/assets/global/css/bootstrap.min.css"> |
|||
<script>if (typeof module === 'object') {window.module = module; module = undefined;}</script> |
|||
<script type="text/javascript" src="EasyDEX-GUI/assets/global/vendor/jquery/jquery.min.js"></script> |
|||
<script>if (window.module) module = window.module;</script> |
|||
<script type="text/javascript"> |
|||
function resizeMainWindow() { |
|||
/* set default map height */ |
|||
var mapH = $(window).height(); |
|||
|
|||
$(".page-main").outerHeight(mapH); |
|||
} |
|||
function StartIguana() { |
|||
var ajax_data = {"herd":"iguana"}; |
|||
console.log(ajax_data); |
|||
$.ajax({ |
|||
type: 'POST', |
|||
data: JSON.stringify(ajax_data), |
|||
url: 'http://127.0.0.1:17777/shepherd/herd', |
|||
dataType: "xml/html/script/json", // expected format for response |
|||
contentType: "application/json", // send as JSON |
|||
success: function(data, textStatus, jqXHR) { |
|||
var AjaxOutputData = JSON.parse(data); |
|||
console.log('== ActiveHandle Data OutPut =='); |
|||
console.log(AjaxOutputData); |
|||
}, |
|||
error: function(xhr, textStatus, error) { |
|||
console.log(xhr.statusText); |
|||
if ( xhr.readyState == 0 ) { |
|||
} |
|||
console.log(textStatus); |
|||
console.log(error); |
|||
} |
|||
}); |
|||
} |
|||
function StartCorsproxy() { |
|||
var ajax_data = {"herd":"corsproxy"}; |
|||
console.log(ajax_data); |
|||
$.ajax({ |
|||
type: 'POST', |
|||
data: JSON.stringify(ajax_data), |
|||
url: 'http://127.0.0.1:17777/shepherd/herd', |
|||
dataType: "xml/html/script/json", // expected format for response |
|||
contentType: "application/json", // send as JSON |
|||
success: function(data, textStatus, jqXHR) { |
|||
var AjaxOutputData = JSON.parse(data); |
|||
console.log('== ActiveHandle Data OutPut =='); |
|||
console.log(AjaxOutputData); |
|||
}, |
|||
error: function(xhr, textStatus, error) { |
|||
console.log(xhr.statusText); |
|||
if ( xhr.readyState == 0 ) { |
|||
} |
|||
console.log(textStatus); |
|||
console.log(error); |
|||
} |
|||
}); |
|||
} |
|||
function StartKomodod() { |
|||
var ajax_data = {"herd":"komodod"}; |
|||
console.log(ajax_data); |
|||
$.ajax({ |
|||
type: 'POST', |
|||
data: JSON.stringify(ajax_data), |
|||
url: 'http://127.0.0.1:17777/shepherd/herd', |
|||
dataType: "xml/html/script/json", // expected format for response |
|||
contentType: "application/json", // send as JSON |
|||
success: function(data, textStatus, jqXHR) { |
|||
var AjaxOutputData = JSON.parse(data); |
|||
console.log('== ActiveHandle Data OutPut =='); |
|||
console.log(AjaxOutputData); |
|||
}, |
|||
error: function(xhr, textStatus, error) { |
|||
console.log(xhr.statusText); |
|||
if ( xhr.readyState == 0 ) { |
|||
} |
|||
console.log(textStatus); |
|||
console.log(error); |
|||
} |
|||
}); |
|||
} |
|||
function StartKMDNativeIGUI() { |
|||
var secToLaunch = 60; |
|||
$('#kmdNativeBtn').text('Starting Komodo in ' + secToLaunch + 's'); |
|||
StartCorsproxy(); |
|||
StartKomodod(); |
|||
setInterval(function() { |
|||
$('#kmdNativeBtn').text('Starting Komodo in ' + secToLaunch + 's'); |
|||
secToLaunch--; |
|||
}, 1000); |
|||
setTimeout(function() { |
|||
document.location = 'Iguana-GUI/index.html'; |
|||
}, secToLaunch * 1000); |
|||
} |
|||
jQuery(document).ready(function() { |
|||
resizeMainWindow(); |
|||
window.onresize = function(event) { resizeMainWindow(); }; |
|||
}); |
|||
</script> |
|||
</head> |
|||
<body> |
|||
<div class="page-main"> |
|||
<div class="col-xs-6 text-center" style="height: 100%; background: url(bg.jpg) no-repeat fixed; background-color: #c7c7c7; vertical-align: middle;" id="iguanaGuiStart"> |
|||
<h1 style="color: white;">Iguana Wallet<h1> |
|||
<a type="button" class="btn btn-default btn-lg" href="Iguana-GUI/index.html">Open Iguana Wallet</a><br/><br/> |
|||
<a type="button" class="btn btn-default btn-lg" href="#" onclick="StartCorsproxy()">Launch proxy server</a><br/><br/> |
|||
<a type="button" class="btn btn-default btn-lg" href="#" onclick="StartKMDNativeIGUI()" id="kmdNativeBtn">Komodo Native</a><br/><br/> |
|||
<a type="button" class="btn btn-default btn-lg" href="#" onclick="StartIguana()">Start Iguana Core</a> |
|||
</div> |
|||
<div class="col-xs-6 text-center" style="height: 100%; background: url(bg2.jpg) no-repeat fixed; background-color: #d8d8d8; vertical-align: middle;" id="edexGuiStart"> |
|||
<h1 style="color: white;">EasyDEX</h1> |
|||
<a type="button" class="btn btn-default btn-lg" href="EasyDEX-GUI/index.html">Open EasyDEX</a> |
|||
</div> |
|||
</div> |
|||
</body> |
|||
</html> |
@ -1,18 +0,0 @@ |
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="UTF-8"> |
|||
<title>iguanaElectronApp</title> |
|||
</head> |
|||
<body> |
|||
<h3>some version information:</h3> |
|||
node.js <script>document.write(process.versions.node)</script>, |
|||
chromium <script>document.write(process.versions.chrome)</script>, |
|||
electron <script>document.write(process.versions.electron)</script>. |
|||
</body> |
|||
|
|||
<script> |
|||
//bind other code refs |
|||
//require('./xxx.js') |
|||
</script> |
|||
</html> |
@ -1,962 +0,0 @@ |
|||
const fs = require('fs-extra'); |
|||
const request = require('request'); |
|||
const async = require('async'); |
|||
|
|||
var cache = {}; |
|||
var inMemCache; |
|||
var inMemPubkey; |
|||
|
|||
cache.setVar = function(variable, value) { |
|||
cache[variable] = value; |
|||
} |
|||
|
|||
/* |
|||
* cache data is dumped to disk before app quit or after cache.one call is finished |
|||
*/ |
|||
cache.dumpCacheBeforeExit = function() { |
|||
if (inMemCache) { |
|||
cache.shepherd.log('dumping cache before exit'); |
|||
fs.writeFileSync(`${cache.iguanaDir}/shepherd/cache-${inMemPubkey}.json`, JSON.stringify(inMemCache), 'utf8'); |
|||
} |
|||
} |
|||
|
|||
cache.get = function(req, res, next) { |
|||
const pubkey = req.query.pubkey; |
|||
|
|||
if (pubkey) { |
|||
inMemPubkey = pubkey; |
|||
|
|||
if (!inMemCache) { |
|||
cache.shepherd.log('serving cache from disk'); |
|||
|
|||
if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`)) { |
|||
fs.readFile(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, 'utf8', function(err, data) { |
|||
if (err) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: err, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} else { // deprecated
|
|||
try { |
|||
const parsedJSON = JSON.parse(data); |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: parsedJSON, |
|||
}; |
|||
|
|||
inMemCache = parsedJSON; |
|||
res.end(JSON.stringify(successObj)); |
|||
} catch (e) { |
|||
cache.shepherd.log('JSON parse error while reading cache data from disk:'); |
|||
cache.shepherd.log(e); |
|||
if (e.toString().indexOf('at position') > -1) { |
|||
const errorPos = e.toString().split(' '); |
|||
|
|||
cache.shepherd.log(`JSON error ---> ${data.substring(errorPos[errorPos.length - 1] - 20, errorPos[errorPos.length - 1] + 20)} | error sequence: ${data.substring(errorPos[errorPos.length - 1], errorPos[errorPos.length - 1] + 1)}`); |
|||
cache.shepherd.log('attempting to recover JSON data'); |
|||
|
|||
fs.writeFile(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, data.substring(0, errorPos[errorPos.length - 1]), function(err) { |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: data.substring(0, errorPos[errorPos.length - 1]), |
|||
}; |
|||
|
|||
inMemCache = JSON.parse(data.substring(0, errorPos[errorPos.length - 1])); |
|||
res.end(JSON.stringify(successObj)); |
|||
}); |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: `no file with handle ${pubkey}`, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} else { |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: inMemCache, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(successObj)); |
|||
} |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no pubkey provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} |
|||
|
|||
cache.groomGet = function(req, res, next) { |
|||
const _filename = req.query.filename; |
|||
|
|||
if (_filename) { |
|||
if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`)) { |
|||
fs.readFile(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, 'utf8', function(err, data) { |
|||
if (err) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: err, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} else { |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: data ? JSON.parse(data) : '', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(successObj)); |
|||
} |
|||
}); |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: `no file with name ${_filename}`, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no file name provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} |
|||
|
|||
cache.groomDelete = function(req, res, next) { |
|||
const _filename = req.body.filename; |
|||
|
|||
if (_filename) { |
|||
if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`)) { |
|||
inMemCache = null; |
|||
|
|||
fs.unlink(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, function(err) { |
|||
if (err) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: err, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} else { |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: 'deleted', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(successObj)); |
|||
} |
|||
}); |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: `no file with name ${_filename}`, |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no file name provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} |
|||
|
|||
cache.groomPost = function(req, res) { |
|||
const _filename = req.body.filename; |
|||
const _payload = req.body.payload; |
|||
|
|||
if (!cacheCallInProgress) { |
|||
cacheCallInProgress = true; |
|||
|
|||
if (_filename) { |
|||
if (!_payload) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no payload provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} else { |
|||
inMemCache = JSON.parse(_payload); |
|||
cache.shepherd.log('appending groom post to in mem cache'); |
|||
cache.shepherd.log('appending groom post to on disk cache'); |
|||
|
|||
fs.writeFile(`${cache.iguanaDir}/shepherd/cache-${_filename}.json`, _payload, function(err) { |
|||
if (err) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: err, |
|||
}; |
|||
|
|||
cacheCallInProgress = false; |
|||
res.end(JSON.stringify(errorObj)); |
|||
} else { |
|||
const successObj = { |
|||
msg: 'success', |
|||
result: 'done', |
|||
}; |
|||
|
|||
cacheCallInProgress = false; |
|||
res.end(JSON.stringify(successObj)); |
|||
} |
|||
}); |
|||
} |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no file name provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} else { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'another job is in progress', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
} |
|||
} |
|||
|
|||
var cacheCallInProgress = false; |
|||
const cacheGlobLifetime = 600; // sec
|
|||
|
|||
// TODO: reset calls' states on new /cache call start
|
|||
var mock = require('./mock'); |
|||
|
|||
var callStack = {}; |
|||
const checkCallStack = function() { |
|||
let total = 0; |
|||
|
|||
for (let coin in callStack) { |
|||
total =+ callStack[coin]; |
|||
} |
|||
|
|||
if (total / Object.keys(callStack).length === 1) { |
|||
cache.dumpCacheBeforeExit(); |
|||
cacheCallInProgress = false; |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'done', |
|||
resp: 'success', |
|||
}, |
|||
}, |
|||
}); |
|||
} |
|||
}; |
|||
|
|||
/* |
|||
* type: GET |
|||
* params: userpass, pubkey, coin, address, skip |
|||
*/ |
|||
cache.one = function(req, res, next) { |
|||
if (req.query.pubkey && |
|||
!fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${req.query.pubkey}.json`)) { |
|||
cacheCallInProgress = false; |
|||
} |
|||
|
|||
if (cacheCallInProgress) { |
|||
checkCallStack(); |
|||
} |
|||
|
|||
if (!cacheCallInProgress) { |
|||
cache.dumpCacheBeforeExit(); |
|||
|
|||
if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${req.query.pubkey}.json`)) { |
|||
let _data = fs.readFileSync(`${cache.iguanaDir}/shepherd/cache-${req.query.pubkey}.json`, 'utf8'); |
|||
|
|||
if (_data) { |
|||
inMemCache = JSON.parse(_data); |
|||
_data = _data.replace('waiting', 'failed'); |
|||
cache.dumpCacheBeforeExit(); |
|||
} |
|||
} |
|||
|
|||
// TODO: add check to allow only one cache call/sequence in progress
|
|||
cacheCallInProgress = true; |
|||
|
|||
let sessionKey = req.query.userpass; |
|||
let coin = req.query.coin; |
|||
let address = req.query.address; |
|||
let addresses = req.query.addresses && req.query.addresses.indexOf(':') > -1 ? req.query.addresses.split(':') : null; |
|||
let pubkey = req.query.pubkey; |
|||
let mock = req.query.mock; |
|||
let skipTimeout = req.query.skip; |
|||
let callsArray = req.query.calls.split(':'); |
|||
let iguanaCorePort = req.query.port ? req.query.port : cache.appConfig.iguanaCorePort; |
|||
let errorObj = { |
|||
msg: 'error', |
|||
result: 'error', |
|||
}; |
|||
let outObj = {}; |
|||
const writeCache = function(timeStamp) { |
|||
if (timeStamp) { |
|||
outObj.timestamp = timeStamp; |
|||
} |
|||
|
|||
inMemCache = outObj; |
|||
}; |
|||
const checkTimestamp = function(dateToCheck) { |
|||
const currentEpochTime = new Date(Date.now()) / 1000; |
|||
const secondsElapsed = Number(currentEpochTime) - Number(dateToCheck / 1000); |
|||
|
|||
return Math.floor(secondsElapsed); |
|||
}; |
|||
let internalError = false; |
|||
|
|||
inMemPubkey = pubkey; |
|||
callStack[coin] = 1; |
|||
cache.shepherd.log(callsArray); |
|||
cache.shepherd.log(`iguana core port ${iguanaCorePort}`); |
|||
|
|||
if (!sessionKey) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no session key provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
internalError = true; |
|||
} |
|||
|
|||
if (!pubkey) { |
|||
const errorObj = { |
|||
msg: 'error', |
|||
result: 'no pubkey provided', |
|||
}; |
|||
|
|||
res.end(JSON.stringify(errorObj)); |
|||
internalError = true; |
|||
} |
|||
|
|||
cache.shepherd.log('cache-one call started'); |
|||
|
|||
function fixJSON(data) { |
|||
if (data && |
|||
data.length) { |
|||
try { |
|||
const parsedJSON = JSON.parse(data); |
|||
|
|||
return parsedJSON; |
|||
} catch (e) { |
|||
cache.shepherd.log(e); |
|||
if (e.toString().indexOf('at position') > -1) { |
|||
const errorPos = e.toString().split(' '); |
|||
|
|||
cache.shepherd.log(`JSON error ---> ${data.substring(errorPos[errorPos.length - 1] - 20, errorPos[errorPos.length - 1] + 20)} | error sequence: ${data.substring(errorPos[errorPos.length - 1], errorPos[errorPos.length - 1] + 1)}`); |
|||
cache.shepherd.log('attempting to recover JSON data'); |
|||
return JSON.parse(data.substring(0, errorPos[errorPos.length - 1])); |
|||
} |
|||
if (e.toString().indexOf('Unexpected end of JSON input')) { |
|||
return {}; |
|||
} |
|||
} |
|||
} else { |
|||
return {}; |
|||
} |
|||
} |
|||
|
|||
if (fs.existsSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`) && |
|||
coin !== 'all') { |
|||
if (inMemCache) { |
|||
cache.shepherd.log('cache one from mem'); |
|||
outObj = inMemCache; |
|||
} else { |
|||
const _file = fs.readFileSync(`${cache.iguanaDir}/shepherd/cache-${pubkey}.json`, 'utf8'); |
|||
|
|||
cache.shepherd.log('cache one from disk'); |
|||
outObj = fixJSON(_file); |
|||
} |
|||
|
|||
if (!outObj || |
|||
!outObj.basilisk) { |
|||
cache.shepherd.log('no local basilisk info'); |
|||
outObj['basilisk'] = {}; |
|||
outObj['basilisk'][coin] = {}; |
|||
} else { |
|||
if (!outObj['basilisk'][coin]) { |
|||
cache.shepherd.log('no local coin info'); |
|||
outObj['basilisk'][coin] = {}; |
|||
} |
|||
} |
|||
} else { |
|||
outObj['basilisk'] = {}; |
|||
outObj['basilisk'][coin] = {}; |
|||
} |
|||
|
|||
res.end(JSON.stringify({ |
|||
msg: 'success', |
|||
result: 'call is initiated', |
|||
})); |
|||
|
|||
if (!internalError) { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
function execDEXRequests(coin, address) { |
|||
let dexUrls = { |
|||
listunspent: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listunspent?userpass=${sessionKey}&symbol=${coin}&address=${address}`, |
|||
listtransactions: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/listtransactions?userpass=${sessionKey}&count=100&skip=0&symbol=${coin}&address=${address}`, |
|||
getbalance: `http://${cache.appConfig.host}:${iguanaCorePort}/api/dex/getbalance?userpass=${sessionKey}&symbol=${coin}&address=${address}`, |
|||
refresh: `http://${cache.appConfig.host}:${iguanaCorePort}/api/basilisk/refresh?userpass=${sessionKey}&symbol=${coin}&address=${address}` |
|||
}; |
|||
let _dexUrls = {}; |
|||
|
|||
for (let a = 0; a < callsArray.length; a++) { |
|||
_dexUrls[callsArray[a]] = dexUrls[callsArray[a]]; |
|||
} |
|||
|
|||
if (coin === 'BTC' || |
|||
coin === 'SYS') { |
|||
delete _dexUrls.refresh; |
|||
delete _dexUrls.getbalance; |
|||
} |
|||
|
|||
cache.shepherd.log(`${coin} address ${address}`); |
|||
|
|||
if (!outObj.basilisk[coin][address]) { |
|||
outObj.basilisk[coin][address] = {}; |
|||
writeCache(); |
|||
} |
|||
|
|||
// set current call status
|
|||
async.forEachOf(_dexUrls, function(dexUrl, key) { |
|||
if (!outObj.basilisk[coin][address][key]) { |
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].status = 'waiting'; |
|||
} else { |
|||
outObj.basilisk[coin][address][key].status = 'waiting'; |
|||
} |
|||
}); |
|||
writeCache(); |
|||
|
|||
async.forEachOf(_dexUrls, function(dexUrl, key) { |
|||
var tooEarly = false; |
|||
|
|||
if (outObj.basilisk[coin][address][key] && |
|||
outObj.basilisk[coin][address][key].timestamp && |
|||
(!skipTimeout && checkTimestamp(outObj.basilisk[coin][address][key].timestamp) < cacheGlobLifetime)) { |
|||
tooEarly = true; |
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: key, |
|||
coin: coin, |
|||
address: address, |
|||
status: 'done', |
|||
resp: 'too early', |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
} |
|||
if (!tooEarly) { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: key, |
|||
coin: coin, |
|||
address: address, |
|||
status: 'in progress', |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
outObj.basilisk[coin][address][key].status = 'in progress'; |
|||
request({ |
|||
url: mock ? `http://localhost:17777/shepherd/mock?url=${dexUrl}` : dexUrl, |
|||
method: 'GET', |
|||
timeout: 30000, |
|||
}, function(error, response, body) { |
|||
if (response && |
|||
response.statusCode && |
|||
response.statusCode === 200) { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: key, |
|||
coin: coin, |
|||
address: address, |
|||
status: 'done', |
|||
resp: body, |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
// basilisk fallback
|
|||
const _parsedJSON = JSON.parse(body); |
|||
_explorerURL = { |
|||
getbalance: `http://kmd.explorer.supernet.org/api/addr/${address}/?noTxList=1`, |
|||
listtransactions: `https://kmd.explorer.supernet.org/api/txs?address=${address}&pageNum=0` |
|||
}; |
|||
if ((key === 'getbalance' && |
|||
coin === 'KMD' && |
|||
((_parsedJSON && _parsedJSON.balance === 0) || body === [])) || |
|||
(key === 'listtransactions' && |
|||
coin === 'KMD' && |
|||
(_parsedJSON === [] || !_parsedJSON.length))) { |
|||
cache.shepherd.log('fallback to kmd explorer ======>'); |
|||
request({ |
|||
url: _explorerURL[key], |
|||
method: 'GET' |
|||
}, function(error, response, _body) { |
|||
if (response && |
|||
response.statusCode && |
|||
response.statusCode === 200) { |
|||
let _parsedExplorerJSON = JSON.parse(_body); |
|||
_parsedExplorerJSON['source'] = 'explorer'; |
|||
let _formattedTxs = []; |
|||
|
|||
if (key === 'listtransactions' && |
|||
_parsedExplorerJSON['txs'] && |
|||
_parsedExplorerJSON['txs'].length) { |
|||
const _txList = _parsedExplorerJSON['txs']; |
|||
|
|||
for (let i = 0; i < _txList.length; i++) { |
|||
_formattedTxs.push({ |
|||
type: 'unknown', |
|||
height: _txList[i].blockheight, |
|||
confirmations: _txList[i].confirmations, |
|||
timestamp: _txList[i].time, |
|||
amount: _txList[i].vout[1].value, |
|||
txid: _txList[i].txid, |
|||
source: 'explorer', |
|||
}); |
|||
} |
|||
|
|||
_parsedExplorerJSON = _formattedTxs; |
|||
} |
|||
if ((key === 'getbalance' && |
|||
Number(_parsedExplorerJSON.balance) !== Number(_parsedJSON.balance) && |
|||
Number(_parsedExplorerJSON.txApperances) < 500) || key === 'listtransactions') { |
|||
body = JSON.stringify(_parsedExplorerJSON); |
|||
} |
|||
cache.io.emit('messages', { |
|||
'message': { |
|||
'shepherd': { |
|||
'method': 'cache-one', |
|||
'status': 'in progress', |
|||
'iguanaAPI': { |
|||
'method': key, |
|||
'coin': coin, |
|||
'address': address, |
|||
'status': 'done', |
|||
'resp': _body |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].data = JSON.parse(body); |
|||
outObj.basilisk[coin][address][key].timestamp = Date.now(); // add timestamp
|
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
cache.shepherd.log(dexUrl); |
|||
cache.shepherd.log(body); |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
writeCache(); |
|||
} else { |
|||
cache.shepherd.log(`explorer ${key} for address ${address} fallback failed`); |
|||
} |
|||
}); |
|||
} else { |
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].data = JSON.parse(body); |
|||
outObj.basilisk[coin][address][key].timestamp = Date.now(); // add timestamp
|
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
cache.shepherd.log(dexUrl); |
|||
cache.shepherd.log(body); |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
writeCache(); |
|||
} |
|||
} |
|||
if (error || |
|||
!body || |
|||
!response) { |
|||
// basilisk fallback
|
|||
_explorerURL = { |
|||
getbalance: `http://kmd.explorer.supernet.org/api/addr/${address}/?noTxList=1`, |
|||
listtransactions: `https://kmd.explorer.supernet.org/api/txs?address=${address}&pageNum=0` |
|||
}; |
|||
if ((key === 'getbalance' && |
|||
coin === 'KMD') || |
|||
(key === 'listtransactions' && |
|||
coin === 'KMD')) { |
|||
cache.shepherd.log('fallback to kmd explorer ======>'); |
|||
request({ |
|||
url: _explorerURL[key], |
|||
method: 'GET' |
|||
}, function(error, response, _body) { |
|||
if (response && |
|||
response.statusCode && |
|||
response.statusCode === 200) { |
|||
let _parsedExplorerJSON = JSON.parse(_body); |
|||
_parsedExplorerJSON['source'] = 'explorer'; |
|||
|
|||
if ((key === 'getbalance' && |
|||
Number(_parsedExplorerJSON.balance) !== Number(_parsedJSON.balance) && |
|||
Number(_parsedExplorerJSON.txApperances) < 500) || key === 'listtransactions') { |
|||
body = JSON.stringify(_parsedExplorerJSON); |
|||
} |
|||
cache.io.emit('messages', { |
|||
'message': { |
|||
'shepherd': { |
|||
'method': 'cache-one', |
|||
'status': 'in progress', |
|||
'iguanaAPI': { |
|||
'method': key, |
|||
'coin': coin, |
|||
'address': address, |
|||
'status': 'done', |
|||
'resp': _body |
|||
} |
|||
} |
|||
} |
|||
}); |
|||
|
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].data = JSON.parse(body); |
|||
outObj.basilisk[coin][address][key].timestamp = Date.now(); // add timestamp
|
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
cache.shepherd.log(dexUrl); |
|||
cache.shepherd.log(body); |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
writeCache(); |
|||
} else { |
|||
cache.shepherd.log(`explorer ${key} for address ${address} fallback failed`); |
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].data = { 'error': 'request failed' }; |
|||
outObj.basilisk[coin][address][key].timestamp = 1471620867 // add timestamp
|
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} request ${key} for address ${address} failed`); |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
writeCache(); |
|||
} |
|||
}); |
|||
} else { |
|||
outObj.basilisk[coin][address][key] = {}; |
|||
outObj.basilisk[coin][address][key].data = { 'error': 'request failed' }; |
|||
outObj.basilisk[coin][address][key].timestamp = 1471620867 // add timestamp
|
|||
outObj.basilisk[coin][address][key].status = 'done'; |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} request ${key} for address ${address} failed`); |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
writeCache(); |
|||
} |
|||
} |
|||
}); |
|||
} else { |
|||
cache.shepherd.log(`${key} is fresh, check back in ${(cacheGlobLifetime - checkTimestamp(outObj.basilisk[coin][address][key].timestamp))}s`); |
|||
callStack[coin]--; |
|||
cache.shepherd.log(`${coin} _stack len ${callStack[coin]}`); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
checkCallStack(); |
|||
} |
|||
}); |
|||
} |
|||
|
|||
function parseAddresses(coin, addrArray) { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: 'getaddressesbyaccount', |
|||
coin: coin, |
|||
status: 'done', |
|||
resp: addrArray, |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
outObj.basilisk[coin].addresses = addrArray; |
|||
cache.shepherd.log(addrArray); |
|||
writeCache(); |
|||
|
|||
const addrCount = outObj.basilisk[coin].addresses ? outObj.basilisk[coin].addresses.length : 0; |
|||
let callsArrayBTC = callsArray.length; |
|||
|
|||
if (callsArray.indexOf('getbalance') > - 1) { |
|||
callsArrayBTC--; |
|||
} |
|||
if (callsArray.indexOf('refresh') > - 1) { |
|||
callsArrayBTC--; |
|||
} |
|||
callStack[coin] = callStack[coin] + addrCount * (coin === 'BTC' || coin === 'SYS' ? callsArrayBTC : callsArray.length); |
|||
cache.shepherd.log(`${coin} stack len ${callStack[coin]}`); |
|||
|
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
totalStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
async.each(outObj.basilisk[coin].addresses, function(address) { |
|||
execDEXRequests(coin, address); |
|||
}); |
|||
} |
|||
|
|||
function getAddresses(coin) { |
|||
if (addresses) { |
|||
parseAddresses(coin, addresses); |
|||
} else { |
|||
const tempUrl = `http://${cache.appConfig.host}:${cache.appConfig.iguanaCorePort}/api/bitcoinrpc/getaddressesbyaccount?userpass=${sessionKey}&coin=${coin}&account=*`; |
|||
request({ |
|||
url: mock ? `http://localhost:17777/shepherd/mock?url=${tempUrl}` : tempUrl, |
|||
method: 'GET' |
|||
}, function(error, response, body) { |
|||
if (response && |
|||
response.statusCode && |
|||
response.statusCode === 200) { |
|||
parseAddresses(coin, JSON.parse(body).result); |
|||
} else { |
|||
// TODO: error
|
|||
} |
|||
}); |
|||
} |
|||
} |
|||
|
|||
// update all available coin addresses
|
|||
if (!address) { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: 'getaddressesbyaccount', |
|||
coin: coin, |
|||
status: 'in progress', |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
if (coin === 'all') { |
|||
const tempUrl = `http://${cache.appConfig.host}:${cache.appConfig.iguanaCorePort}/api/InstantDEX/allcoins?userpass=${sessionKey}`; |
|||
request({ |
|||
url: mock ? `http://localhost:17777/shepherd/mock?url=${tempUrl}` : tempUrl, |
|||
method: 'GET' |
|||
}, function(error, response, body) { |
|||
if (response && |
|||
response.statusCode && |
|||
response.statusCode === 200) { |
|||
cache.shepherd.log(JSON.parse(body).basilisk); |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: 'allcoins', |
|||
status: 'done', |
|||
resp: body, |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
body = JSON.parse(body); |
|||
// basilisk coins
|
|||
if (body.basilisk && |
|||
body.basilisk.length) { |
|||
// get coin addresses
|
|||
async.each(body.basilisk, function(coin) { |
|||
callStack[coin] = 1; |
|||
}); |
|||
|
|||
async.each(body.basilisk, function(coin) { |
|||
outObj.basilisk[coin] = {}; |
|||
writeCache(); |
|||
|
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
method: 'getaddressesbyaccount', |
|||
coin: coin, |
|||
status: 'in progress', |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
getAddresses(coin); |
|||
}); |
|||
} |
|||
} |
|||
if (error) { // stop further requests on failure, exit
|
|||
callStack[coin] = 1; |
|||
checkCallStack(); |
|||
} |
|||
}); |
|||
} else { |
|||
getAddresses(coin); |
|||
} |
|||
} else { |
|||
let callsArrayBTC = callsArray.length; // restrict BTC and SYS only to listunspent and listtransactions calls
|
|||
|
|||
if (callsArray.indexOf('getbalance') > - 1) { |
|||
callsArrayBTC--; |
|||
} |
|||
if (callsArray.indexOf('refresh') > - 1) { |
|||
callsArrayBTC--; |
|||
} |
|||
|
|||
callStack[coin] = callStack[coin] + (coin === 'BTC' || coin === 'SYS' ? callsArrayBTC : callsArray.length); |
|||
cache.shepherd.log(`${coin} stack len ${callStack[coin]}`); |
|||
|
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-one', |
|||
status: 'in progress', |
|||
iguanaAPI: { |
|||
totalStackLength: callStack[coin], |
|||
currentStackLength: callStack[coin], |
|||
}, |
|||
}, |
|||
}, |
|||
}); |
|||
|
|||
execDEXRequests(coin, address); |
|||
} |
|||
} else { |
|||
cache.io.emit('messages', { |
|||
message: { |
|||
shepherd: { |
|||
method: 'cache-all', |
|||
status: 'done', |
|||
resp: 'internal error', |
|||
}, |
|||
}, |
|||
}); |
|||
cacheCallInProgress = false; |
|||
} |
|||
} else { |
|||
res.end(JSON.stringify({ |
|||
msg: 'error', |
|||
result: 'another call is in progress already', |
|||
})); |
|||
} |
|||
}; |
|||
|
|||
module.exports = cache; |
@ -0,0 +1,347 @@ |
|||
/* |
|||
MIT License |
|||
|
|||
Copyright (c) 2017 Yuki Akiyama, SuperNET |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
*/ |
|||
|
|||
const tls = require('tls'); |
|||
const net = require('net'); |
|||
const EventEmitter = require('events').EventEmitter; |
|||
|
|||
const makeRequest = function(method, params, id) { |
|||
return JSON.stringify({ |
|||
jsonrpc : '2.0', |
|||
method : method, |
|||
params : params, |
|||
id : id, |
|||
}); |
|||
} |
|||
|
|||
const createRecursiveParser = function(maxDepth, delimiter) { |
|||
const MAX_DEPTH = maxDepth; |
|||
const DELIMITER = delimiter; |
|||
const recursiveParser = function(n, buffer, callback) { |
|||
if (buffer.length === 0) { |
|||
return { |
|||
code: 0, |
|||
buffer: buffer, |
|||
}; |
|||
} |
|||
|
|||
if (n > MAX_DEPTH) { |
|||
return { |
|||
code: 1, |
|||
buffer: buffer, |
|||
}; |
|||
} |
|||
|
|||
const xs = buffer.split(DELIMITER); |
|||
|
|||
if (xs.length === 1) { |
|||
return { |
|||
code: 0, |
|||
buffer: buffer, |
|||
}; |
|||
} |
|||
|
|||
callback(xs.shift(), n); |
|||
|
|||
return recursiveParser(n + 1, xs.join(DELIMITER), callback); |
|||
} |
|||
|
|||
return recursiveParser; |
|||
} |
|||
|
|||
const createPromiseResult = function(resolve, reject) { |
|||
return (err, result) => { |
|||
if (err) { |
|||
console.log('electrum error:'); |
|||
console.log(err); |
|||
resolve(err); |
|||
// reject(err);
|
|||
} else { |
|||
resolve(result); |
|||
} |
|||
} |
|||
} |
|||
|
|||
class MessageParser { |
|||
constructor(callback) { |
|||
this.buffer = ''; |
|||
this.callback = callback; |
|||
this.recursiveParser = createRecursiveParser(20, '\n'); |
|||
} |
|||
|
|||
run(chunk) { |
|||
this.buffer += chunk; |
|||
|
|||
while (true) { |
|||
const res = this.recursiveParser(0, this.buffer, this.callback); |
|||
|
|||
this.buffer = res.buffer; |
|||
|
|||
if (res.code === 0) { |
|||
break; |
|||
} |
|||
} |
|||
} |
|||
} |
|||
|
|||
const util = { |
|||
makeRequest, |
|||
createRecursiveParser, |
|||
createPromiseResult, |
|||
MessageParser, |
|||
}; |
|||
|
|||
const getSocket = function(protocol, options) { |
|||
switch (protocol) { |
|||
case 'tcp': |
|||
return new net.Socket(); |
|||
case 'tls': |
|||
// todo
|
|||
case 'ssl': |
|||
return new tls.TLSSocket(options); |
|||
} |
|||
|
|||
throw new Error('unknown protocol'); |
|||
} |
|||
|
|||
const initSocket = function(self, protocol, options) { |
|||
const conn = getSocket(protocol, options); |
|||
|
|||
conn.setEncoding('utf8'); |
|||
conn.setKeepAlive(true, 0); |
|||
conn.setNoDelay(true); |
|||
conn.on('connect', () => { |
|||
self.onConnect(); |
|||
}); |
|||
conn.on('close', (e) => { |
|||
self.onClose(e); |
|||
}); |
|||
conn.on('data', (chunk) => { |
|||
self.onReceive(chunk); |
|||
}); |
|||
conn.on('end', (e) => { |
|||
self.onEnd(e); |
|||
}); |
|||
conn.on('error', (e) => { |
|||
self.onError(e); |
|||
}); |
|||
|
|||
return conn; |
|||
} |
|||
|
|||
class Client { |
|||
constructor(port, host, protocol = 'tcp', options = void 0) { |
|||
this.id = 0; |
|||
this.port = port; |
|||
this.host = host; |
|||
this.callbackMessageQueue = {}; |
|||
this.subscribe = new EventEmitter(); |
|||
this.conn = initSocket(this, protocol, options); |
|||
this.mp = new util.MessageParser((body, n) => { |
|||
this.onMessage(body, n); |
|||
}); |
|||
this.status = 0; |
|||
} |
|||
|
|||
connect() { |
|||
if (this.status) { |
|||
return Promise.resolve(); |
|||
} |
|||
|
|||
this.status = 1; |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
const errorHandler = (e) => reject(e) |
|||
|
|||
this.conn.connect(this.port, this.host, () => { |
|||
this.conn.removeListener('error', errorHandler); |
|||
resolve(); |
|||
}); |
|||
this.conn.on('error', errorHandler); |
|||
}); |
|||
} |
|||
|
|||
close() { |
|||
if (!this.status) { |
|||
return |
|||
} |
|||
|
|||
this.conn.end(); |
|||
this.conn.destroy(); |
|||
this.status = 0; |
|||
} |
|||
|
|||
request(method, params) { |
|||
if (!this.status) { |
|||
return Promise.reject(new Error('ESOCKET')); |
|||
} |
|||
|
|||
return new Promise((resolve, reject) => { |
|||
const id = ++this.id; |
|||
const content = util.makeRequest(method, params, id); |
|||
|
|||
this.callbackMessageQueue[id] = util.createPromiseResult(resolve, reject); |
|||
this.conn.write(`${content}\n`); |
|||
}); |
|||
} |
|||
|
|||
response(msg) { |
|||
const callback = this.callbackMessageQueue[msg.id]; |
|||
|
|||
if (callback) { |
|||
delete this.callbackMessageQueue[msg.id]; |
|||
|
|||
if (msg.error) { |
|||
callback(msg.error); |
|||
} else { |
|||
callback(null, msg.result); |
|||
} |
|||
} else { |
|||
// can't get callback
|
|||
} |
|||
} |
|||
|
|||
onMessage(body, n) { |
|||
const msg = JSON.parse(body); |
|||
|
|||
if (msg instanceof Array) { |
|||
// don't support batch request
|
|||
} else { |
|||
if (msg.id !== void 0) { |
|||
this.response(msg); |
|||
} else { |
|||
this.subscribe.emit(msg.method, msg.params); |
|||
} |
|||
} |
|||
} |
|||
|
|||
onConnect() { |
|||
} |
|||
|
|||
onClose() { |
|||
Object.keys(this.callbackMessageQueue).forEach((key) => { |
|||
this.callbackMessageQueue[key](new Error('close connect')); |
|||
delete this.callbackMessageQueue[key]; |
|||
}); |
|||
} |
|||
|
|||
onReceive(chunk) { |
|||
this.mp.run(chunk); |
|||
} |
|||
|
|||
onEnd() { |
|||
} |
|||
|
|||
onError(e) { |
|||
} |
|||
} |
|||
|
|||
class ElectrumJSCore extends Client { |
|||
constructor(protocol, port, host, options) { |
|||
super(protocol, port, host, options); |
|||
} |
|||
|
|||
onClose() { |
|||
super.onClose(); |
|||
const list = [ |
|||
'server.peers.subscribe', |
|||
'blockchain.numblocks.subscribe', |
|||
'blockchain.headers.subscribe', |
|||
'blockchain.address.subscribe' |
|||
]; |
|||
|
|||
list.forEach(event => this.subscribe.removeAllListeners(event)); |
|||
} |
|||
|
|||
// ref: http://docs.electrum.org/en/latest/protocol.html
|
|||
serverVersion(client_name, protocol_version) { |
|||
return this.request('server.version', [client_name, protocol_version]); |
|||
} |
|||
|
|||
serverBanner() { |
|||
return this.request('server.banner', []); |
|||
} |
|||
|
|||
serverDonationAddress() { |
|||
return this.request('server.donation_address', []); |
|||
} |
|||
|
|||
serverPeersSubscribe() { |
|||
return this.request('server.peers.subscribe', []); |
|||
} |
|||
|
|||
blockchainAddressGetBalance(address) { |
|||
return this.request('blockchain.address.get_balance', [address]); |
|||
} |
|||
|
|||
blockchainAddressGetHistory(address) { |
|||
return this.request('blockchain.address.get_history', [address]); |
|||
} |
|||
|
|||
blockchainAddressGetMempool(address) { |
|||
return this.request('blockchain.address.get_mempool', [address]); |
|||
} |
|||
|
|||
blockchainAddressListunspent(address) { |
|||
return this.request('blockchain.address.listunspent', [address]); |
|||
} |
|||
|
|||
blockchainBlockGetHeader(height) { |
|||
return this.request('blockchain.block.get_header', [height]); |
|||
} |
|||
|
|||
blockchainBlockGetChunk(index) { |
|||
return this.request('blockchain.block.get_chunk', [index]); |
|||
} |
|||
|
|||
blockchainEstimatefee(number) { |
|||
return this.request('blockchain.estimatefee', [number]); |
|||
} |
|||
|
|||
blockchainHeadersSubscribe() { |
|||
return this.request('blockchain.headers.subscribe', []); |
|||
} |
|||
|
|||
blockchainNumblocksSubscribe() { |
|||
return this.request('blockchain.numblocks.subscribe', []); |
|||
} |
|||
|
|||
blockchainRelayfee() { |
|||
return this.request('blockchain.relayfee', []); |
|||
} |
|||
|
|||
blockchainTransactionBroadcast(rawtx) { |
|||
return this.request('blockchain.transaction.broadcast', [rawtx]); |
|||
} |
|||
|
|||
blockchainTransactionGet(tx_hash, height) { |
|||
return this.request('blockchain.transaction.get', [tx_hash, height]); |
|||
} |
|||
|
|||
blockchainTransactionGetMerkle(tx_hash, height) { |
|||
return this.request('blockchain.transaction.get_merkle', [tx_hash, height]); |
|||
} |
|||
} |
|||
|
|||
module.exports = ElectrumJSCore; |
@ -0,0 +1,205 @@ |
|||
'use strict' |
|||
var bitcoin = require('bitcoinjs-lib'); |
|||
|
|||
var networks = exports; |
|||
Object.keys(bitcoin.networks).forEach(function(key){ |
|||
networks[key] = bitcoin.networks[key] |
|||
}); |
|||
|
|||
networks.litecoin = { |
|||
messagePrefix: '\x19Litecoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x019da462, |
|||
private: 0x019d9cfe |
|||
}, |
|||
pubKeyHash: 0x30, |
|||
scriptHash: 0x32, |
|||
wif: 0xb0, |
|||
dustThreshold: 0, // https://github.com/litecoin-project/litecoin/blob/v0.8.7.2/src/main.cpp#L360-L365
|
|||
} |
|||
|
|||
networks.dogecoin = { |
|||
messagePrefix: '\x19Dogecoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x02facafd, |
|||
private: 0x02fac398, |
|||
}, |
|||
pubKeyHash: 0x1e, |
|||
scriptHash: 0x16, |
|||
wif: 0x9e, |
|||
dustThreshold: 0 // https://github.com/dogecoin/dogecoin/blob/v1.7.1/src/core.h#L155-L160
|
|||
}; |
|||
|
|||
// https://github.com/monacoinproject/monacoin/blob/master-0.10/src/chainparams.cpp#L161
|
|||
networks.monacoin = { |
|||
messagePrefix: '\x19Monacoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x32, |
|||
scriptHash: 0x05, |
|||
wif: 0xB2, |
|||
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
|
|||
}; |
|||
|
|||
|
|||
// https://github.com/gamecredits-project/GameCredits/blob/master/src/chainparams.cpp#L136
|
|||
networks.game = { |
|||
messagePrefix: '\x19GameCredits Signed Message:\n', |
|||
bip32: { |
|||
public: 0x043587cf, |
|||
private: 0x04358394, |
|||
}, |
|||
pubKeyHash: 0x6f, |
|||
scriptHash: 0xc4, |
|||
wif: 0xef, |
|||
dustThreshold: 546, // https://github.com/bitcoin/bitcoin/blob/v0.9.2/src/core.h#L151-L162
|
|||
}; |
|||
|
|||
// https://github.com/dashpay/dash/blob/master/src/chainparams.cpp#L171
|
|||
networks.dash = { |
|||
messagePrefix: '\x19DarkCoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x02fe52f8, |
|||
private: 0x02fe52cc, |
|||
}, |
|||
pubKeyHash: 0x4c, |
|||
scriptHash: 0x10, |
|||
wif: 0xcc, |
|||
dustThreshold: 5460, // https://github.com/dashpay/dash/blob/v0.12.0.x/src/primitives/transaction.h#L144-L155
|
|||
}; |
|||
|
|||
// https://github.com/zcoinofficial/zcoin/blob/c93eccb39b07a6132cb3d787ac18be406b24c3fa/src/base58.h#L275
|
|||
networks.zcoin = { |
|||
messagePrefix: '\x19ZCoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, // todo
|
|||
private: 0x0488ade4, // todo
|
|||
}, |
|||
pubKeyHash: 0x52, |
|||
scriptHash: 0x07, |
|||
wif: 0x52 + 128, |
|||
dustThreshold: 1000, // https://github.com/zcoinofficial/zcoin/blob/f755f95a036eedfef7c96bcfb6769cb79278939f/src/main.h#L59
|
|||
}; |
|||
|
|||
// https://raw.githubusercontent.com/jl777/komodo/beta/src/chainparams.cpp
|
|||
networks.komodo = { |
|||
messagePrefix: '\x19Komodo Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x3c, |
|||
scriptHash: 0x55, |
|||
wif: 0xbc, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.viacoin = { |
|||
messagePrefix: '\x19Viacoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x47, |
|||
scriptHash: 0x21, |
|||
wif: 0xc7, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.vertcoin = { |
|||
messagePrefix: '\x19Vertcoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x47, |
|||
scriptHash: 0x5, |
|||
wif: 0x80, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.namecoin = { |
|||
messagePrefix: '\x19Namecoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x34, |
|||
scriptHash: 0xd, |
|||
wif: 0xb4, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.faircoin = { |
|||
messagePrefix: '\x19Faircoin Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x5f, |
|||
scriptHash: 0x24, |
|||
wif: 0xdf, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.digibyte = { |
|||
messagePrefix: '\x19Digibyte Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x1e, |
|||
scriptHash: 0x5, |
|||
wif: 0x80, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.crown = { |
|||
messagePrefix: '\x19Crown Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x0, |
|||
scriptHash: 0x1c, |
|||
wif: 0x80, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.argentum = { |
|||
messagePrefix: '\x19Argentum Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x17, |
|||
scriptHash: 0x5, |
|||
wif: 0x97, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
networks.chips = { |
|||
messagePrefix: '\x19Chips Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x3c, |
|||
scriptHash: 0x55, |
|||
wif: 0xbc, |
|||
dustThreshold: 1000, |
|||
}; |
|||
|
|||
/*networks.zcash = { |
|||
messagePrefix: '\x19Zcash Signed Message:\n', |
|||
bip32: { |
|||
public: 0x0488b21e, |
|||
private: 0x0488ade4, |
|||
}, |
|||
pubKeyHash: 0x1cb8, |
|||
scriptHash: 0x1cbd, |
|||
wif: 0x80, |
|||
dustThreshold: 1000, |
|||
};*/ |
@ -0,0 +1,120 @@ |
|||
/* |
|||
MIT License |
|||
|
|||
Copyright (c) 2017 Yuki Akiyama, SuperNET |
|||
|
|||
Permission is hereby granted, free of charge, to any person obtaining a copy |
|||
of this software and associated documentation files (the "Software"), to deal |
|||
in the Software without restriction, including without limitation the rights |
|||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
|||
copies of the Software, and to permit persons to whom the Software is |
|||
furnished to do so, subject to the following conditions: |
|||
|
|||
The above copyright notice and this permission notice shall be included in all |
|||
copies or substantial portions of the Software. |
|||
|
|||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
|||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
|||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
|||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
|||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
|||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
|||
SOFTWARE. |
|||
*/ |
|||
|
|||
var bitcoin = require('bitcoinjs-lib'); |
|||
|
|||
var decodeFormat = function(tx) { |
|||
var result = { |
|||
txid: tx.getId(), |
|||
version: tx.version, |
|||
locktime: tx.locktime, |
|||
}; |
|||
|
|||
return result; |
|||
} |
|||
|
|||
var decodeInput = function(tx) { |
|||
var result = []; |
|||
|
|||
tx.ins.forEach(function(input, n) { |
|||
var vin = { |
|||
txid: input.hash.reverse().toString('hex'), |
|||
n: input.index, |
|||
script: bitcoin.script.toASM(input.script), |
|||
sequence: input.sequence, |
|||
}; |
|||
|
|||
result.push(vin); |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
var decodeOutput = function(tx, network) { |
|||
var format = function(out, n, network) { |
|||
var vout = { |
|||
satoshi: out.value, |
|||
value: (1e-8 * out.value).toFixed(8), |
|||
n: n, |
|||
scriptPubKey: { |
|||
asm: bitcoin.script.toASM(out.script), |
|||
hex: out.script.toString('hex'), |
|||
type: bitcoin.script.classifyOutput(out.script), |
|||
addresses: [], |
|||
}, |
|||
}; |
|||
|
|||
switch(vout.scriptPubKey.type) { |
|||
case 'pubkeyhash': |
|||
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network)); |
|||
break; |
|||
case 'pubkey': |
|||
const pubKeyBuffer = new Buffer(vout.scriptPubKey.asm.split(' ')[0], 'hex'); |
|||
vout.scriptPubKey.addresses.push(bitcoin.ECPair.fromPublicKeyBuffer(pubKeyBuffer, network).getAddress()); |
|||
break; |
|||
case 'scripthash': |
|||
vout.scriptPubKey.addresses.push(bitcoin.address.fromOutputScript(out.script, network)); |
|||
break; |
|||
} |
|||
|
|||
return vout; |
|||
} |
|||
|
|||
var result = []; |
|||
|
|||
tx.outs.forEach(function(out, n) { |
|||
result.push(format(out, n, network)); |
|||
}); |
|||
|
|||
return result; |
|||
} |
|||
|
|||
var TxDecoder = module.exports = function(rawtx, network) { |
|||
try { |
|||
const _tx = bitcoin.Transaction.fromHex(rawtx); |
|||
|
|||
return { |
|||
tx: _tx, |
|||
network: network, |
|||
format: decodeFormat(_tx), |
|||
inputs: decodeInput(_tx), |
|||
outputs: decodeOutput(_tx, network), |
|||
}; |
|||
} catch (e) { |
|||
return false; |
|||
} |
|||
} |
|||
|
|||
TxDecoder.prototype.decode = function() { |
|||
var result = {}; |
|||
var self = this; |
|||
|
|||
Object.keys(self.format).forEach(function(key) { |
|||
result[key] = self.format[key]; |
|||
}); |
|||
|
|||
result.outputs = self.outputs; |
|||
|
|||
return result; |
|||
} |
@ -1,45 +0,0 @@ |
|||
const fs = require('fs-extra'), |
|||
request = require('request'), |
|||
async = require('async'), |
|||
url = require('url'); |
|||
|
|||
let mock = {}; |
|||
|
|||
mock.setVar = function(variable, value) { |
|||
mock[variable] = value; |
|||
} |
|||
|
|||
mock.get = function(req, res, next) { |
|||
const _url = req.query.url; |
|||
|
|||
if (_url.indexOf('/InstantDEX/allcoins') > -1) { |
|||
res.end(JSON.stringify({ |
|||
'native': [], |
|||
'basilisk': [ 'KMD', 'BTC'], |
|||
'full':[], |
|||
'tag': '18430609759584422959' |
|||
})); |
|||
} |
|||
if (_url.indexOf('/bitcoinrpc/getaddressesbyaccount') > -1) { |
|||
console.log(_url.indexOf('/bitcoinrpc/getaddressesbyaccount')); |
|||
res.end(JSON.stringify({ |
|||
'result': [ |
|||
"RDbGxL8QYdEp8sMULaVZS2E6XThcTKT9Jd", |
|||
"RL4orv22Xch7PhM5w9jUHhVQhX6kF6GkfS", |
|||
"RUrxvPTEKGWEDTvAtgiqbUTTFE53Xdpj8a", |
|||
"RPJoLDa7RezvfUUBr7R3U8wrP16AgUsNw3", |
|||
"RQPTpRJEeafNx5hkDzgjcsPyU4E8RFVApT" |
|||
] |
|||
})); |
|||
} |
|||
if (_url.indexOf('/api/dex/listunspent') > -1 || |
|||
_url.indexOf('/api/dex/listtransactions') > -1 || |
|||
_url.indexOf('/api/ss/getbalance') > -1 || |
|||
_url.indexOf('/api/ww/refresh') > -1) { |
|||
res.end(JSON.stringify({ |
|||
'some key': 'some value' |
|||
})); |
|||
} |
|||
} |
|||
|
|||
module.exports = mock; |
@ -0,0 +1,98 @@ |
|||
const nativeCoind = { |
|||
'btc': { |
|||
name: 'Bitcoin', |
|||
bin: 'bitcoin', |
|||
fullMode: true, |
|||
port: 8332, |
|||
}, |
|||
'btcd': { |
|||
name: 'BitcoinDark', |
|||
bin: 'bitcoindarkd', |
|||
fullMode: true, |
|||
port: 14632, |
|||
}, |
|||
'ltc': { |
|||
name: 'Litecoin', |
|||
bin: 'litecoin', |
|||
fullMode: true, |
|||
port: 9332, |
|||
}, |
|||
'sys': { |
|||
name: 'Syscoin', |
|||
bin: 'syscoin', |
|||
fullMode: true, |
|||
port: 8368, |
|||
}, |
|||
'uno': { |
|||
name: 'Unobtanium', |
|||
bin: 'unobtanium', |
|||
fullMode: true, |
|||
port: 65535, |
|||
}, |
|||
'nmc': { |
|||
name: 'Namecoin', |
|||
bin: 'namecoin', |
|||
fullMode: true, |
|||
port: 8336, |
|||
}, |
|||
'game': { |
|||
name: 'GameCredits', |
|||
bin: 'gamecredits', |
|||
fullMode: true, |
|||
port: 40001, |
|||
}, |
|||
'mzc': { |
|||
name: 'MazaCoin', |
|||
bin: 'maza', |
|||
fullMode: true, |
|||
port: 12832, |
|||
}, |
|||
'frk': { |
|||
name: 'Franko', |
|||
bin: 'franko', |
|||
fullMode: true, |
|||
port: 7913, |
|||
}, |
|||
'doge': { |
|||
name: 'Dogecoin', |
|||
bin: 'dogecoin', |
|||
fullMode: true, |
|||
port: 22555, |
|||
}, |
|||
'dgb': { |
|||
name: 'Digibyte', |
|||
bin: 'digibyte', |
|||
port: 14022, |
|||
}, |
|||
'zet': { |
|||
name: 'Zetacoin', |
|||
bin: 'zetacoin', |
|||
fullMode: true, |
|||
port: 17335, |
|||
}, |
|||
'btm': { |
|||
name: 'Bitmark', |
|||
bin: 'bitmark', |
|||
fullMode: true, |
|||
port: 9266, |
|||
}, |
|||
'carb': { |
|||
name: 'Carboncoin', |
|||
bin: 'carboncoin', |
|||
fullMode: true, |
|||
port: 9351, |
|||
}, |
|||
'anc': { |
|||
name: 'Anoncoin', |
|||
bin: 'anoncoin', |
|||
fullMode: true, |
|||
port: 28332, |
|||
}, |
|||
'lbc': { |
|||
name: 'LBRY Credits', |
|||
bin: 'lbrycrd', |
|||
port: 9245, |
|||
} |
|||
}; |
|||
|
|||
module.exports = nativeCoind; |
File diff suppressed because it is too large
@ -1,3 +1,3 @@ |
|||
version=0.2.0.22a |
|||
version=0.2.0.23a |
|||
type=e-beta |
|||
minversion=0.2.0.2 |
@ -1 +1 @@ |
|||
0.2.0.22a-beta |
|||
0.2.0.23a-beta |
|||
|
Loading…
Reference in new issue