mirror of https://github.com/lukechilds/Agama.git
pbca26
7 years ago
committed by
GitHub
41 changed files with 5571 additions and 5654 deletions
@ -0,0 +1,158 @@ |
|||||
|
let electrumServers = { |
||||
|
/*zcash: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50032, |
||||
|
proto: 'tcp', |
||||
|
},*/ |
||||
|
revs: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50050, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'REVS', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50050', |
||||
|
'136.243.45.140:50050' |
||||
|
], |
||||
|
}, |
||||
|
mnz: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50053, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'MNZ', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50053', |
||||
|
'136.243.45.140:50053' |
||||
|
], |
||||
|
}, |
||||
|
wlc: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50052, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'WLC', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50052', |
||||
|
'136.243.45.140:50052' |
||||
|
], |
||||
|
}, |
||||
|
jumblr: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50051, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'JUMBLR', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50051', |
||||
|
'136.243.45.140:50051' |
||||
|
], |
||||
|
}, |
||||
|
komodo: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50011, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'KMD', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50011', |
||||
|
'136.243.45.140:50011' |
||||
|
], |
||||
|
}, |
||||
|
dogecoin: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50015, |
||||
|
proto: 'tcp', |
||||
|
txfee: 100000000, |
||||
|
abbr: 'DOGE', |
||||
|
}, |
||||
|
viacoin: { // !estimatefee
|
||||
|
address: 'vialectrum.bitops.me', |
||||
|
port: 50002, |
||||
|
proto: 'ssl', |
||||
|
txfee: 100000, |
||||
|
abbr: 'VIA', |
||||
|
}, |
||||
|
vertcoin: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50088, |
||||
|
proto: 'tcp', |
||||
|
txfee: 100000, |
||||
|
abbr: 'VTC', |
||||
|
}, |
||||
|
namecoin: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50036, |
||||
|
proto: 'tcp', |
||||
|
txfee: 100000, |
||||
|
abbr: 'NMC', |
||||
|
}, |
||||
|
monacoin: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50002, |
||||
|
proto: 'tcp', |
||||
|
txfee: 100000, |
||||
|
abbr: 'MONA', |
||||
|
}, |
||||
|
litecoin: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50012, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'LTC', |
||||
|
}, |
||||
|
faircoin: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50005, |
||||
|
proto: 'tcp', |
||||
|
txfee: 1000000, |
||||
|
abbr: 'FAIR', |
||||
|
}, |
||||
|
digibyte: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50022, |
||||
|
proto: 'tcp', |
||||
|
txfee: 100000, |
||||
|
abbr: 'DGB', |
||||
|
}, |
||||
|
dash: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50098, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'DASH', |
||||
|
}, |
||||
|
crown: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50041, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'CRW', |
||||
|
}, |
||||
|
bitcoin: { |
||||
|
address: '173.212.225.176', |
||||
|
port: 50001, |
||||
|
proto: 'tcp', |
||||
|
abbr: 'BTC', |
||||
|
}, |
||||
|
argentum: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50081, |
||||
|
proto: 'tcp', |
||||
|
txfee: 50000, |
||||
|
abbr: 'ARG', |
||||
|
}, |
||||
|
chips: { // !estimatefee
|
||||
|
address: '173.212.225.176', |
||||
|
port: 50076, |
||||
|
proto: 'tcp', |
||||
|
txfee: 10000, |
||||
|
abbr: 'CHIPS', |
||||
|
serverList: [ |
||||
|
'173.212.225.176:50076', |
||||
|
'136.243.45.140:50076' |
||||
|
], |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
module.exports = electrumServers; |
@ -1,197 +0,0 @@ |
|||||
var app = require('http').createServer(handler), |
|
||||
io = require('socket.io')(app), |
|
||||
fs = require('fs'), |
|
||||
request = require('request'), |
|
||||
progress = require('request-progress'); |
|
||||
const path = require('path'), |
|
||||
url = require('url'), |
|
||||
os = require('os'), |
|
||||
sha256 = require('sha256'), |
|
||||
crypto = require('crypto'); |
|
||||
|
|
||||
Promise = require('bluebird'); |
|
||||
|
|
||||
app.listen(3000); |
|
||||
|
|
||||
function handler (req, res) { |
|
||||
fs.readFile(__dirname + '/index.html', |
|
||||
function (err, data) { |
|
||||
if (err) { |
|
||||
res.writeHead(500); |
|
||||
return res.end('Error loading index.html'); |
|
||||
} |
|
||||
|
|
||||
res.writeHead(200); |
|
||||
res.end(data); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
if (os.platform() === 'darwin') { |
|
||||
var PARAMS_DIR = process.env.HOME + '/Library/Application Support/ZcashParams'; |
|
||||
} |
|
||||
if (os.platform() === 'linux') { |
|
||||
var PARAMS_DIR = process.env.HOME + '/.zcash-params'; |
|
||||
} |
|
||||
|
|
||||
var SPROUT_FILES_DATA = [ |
|
||||
{ |
|
||||
'file': 'sprout-proving.key', |
|
||||
'hash': '8bc20a7f013b2b58970cddd2e7ea028975c88ae7ceb9259a5344a16bc2c0eef7' |
|
||||
}, { |
|
||||
'file': 'sprout-verifying.key', |
|
||||
'hash': '4bd498dae0aacfd8e98dc306338d017d9c08dd0918ead18172bd0aec2fc5df82' |
|
||||
} |
|
||||
]; |
|
||||
var SPROUT_DL_URL = 'https://z.cash/downloads/'; |
|
||||
|
|
||||
SPROUT_FILES_DATA.forEach(function(value, index) { |
|
||||
fs.exists(value.file, function(exists) { |
|
||||
if (exists) { |
|
||||
console.log(value.file + ' already exists at location.'); |
|
||||
|
|
||||
var tmphas, |
|
||||
fd = fs.createReadStream(value.file), |
|
||||
hash = crypto.createHash('sha256'); |
|
||||
|
|
||||
hash.setEncoding('hex'); |
|
||||
|
|
||||
fd.on('end', function() { |
|
||||
hash.end(); |
|
||||
|
|
||||
console.log('hash is: '); |
|
||||
console.log(hash.read()); // the desired sha1sum
|
|
||||
console.log(value.hash); |
|
||||
|
|
||||
tmphash = hash.read(); |
|
||||
if (hash.read() === value.hash) { |
|
||||
console.log('File SHA256 sum matches.'); |
|
||||
} else { |
|
||||
console.log('File SHA256 sum does not match.'); |
|
||||
} |
|
||||
}); |
|
||||
// read all file and pipe it (write it) to the hash object
|
|
||||
fd.pipe(hash); |
|
||||
} else { |
|
||||
var DLFile = function() { |
|
||||
return new Promise(function(resolve, reject) { |
|
||||
console.log('file not there.'); |
|
||||
|
|
||||
progress(request(SPROUT_DL_URL + value.file), {}) |
|
||||
.on('progress', function (state) { |
|
||||
console.log('progress', state); |
|
||||
}) |
|
||||
.on('error', function (err) { |
|
||||
console.log(err); |
|
||||
}) |
|
||||
.on('end', function () { |
|
||||
// Do something after request finishes
|
|
||||
console.log('download finished.'); |
|
||||
var result = 'File ==> ' + value.file + ': DOWNLOADED'; |
|
||||
}) |
|
||||
.pipe(fs.createWriteStream(value.file)); |
|
||||
|
|
||||
console.log(result); |
|
||||
resolve(result); |
|
||||
}) |
|
||||
} |
|
||||
var CheckFileSHA = function() { |
|
||||
return new Promise(function(resolve, reject) { |
|
||||
var fd = fs.createReadStream(value.file), |
|
||||
hash = crypto.createHash('sha256'); |
|
||||
|
|
||||
hash.setEncoding('hex'); |
|
||||
|
|
||||
fd.on('end', function() { |
|
||||
hash.end(); |
|
||||
|
|
||||
console.log('hash is: '); |
|
||||
console.log(hash.read()); // the desired sha1sum
|
|
||||
console.log(value.hash); |
|
||||
|
|
||||
if (hash.read() === value.hash) { |
|
||||
console.log('File SHA256 sum matches.'); |
|
||||
} else { |
|
||||
console.log('File SHA256 sum does not match.'); |
|
||||
} |
|
||||
}); |
|
||||
// read all file and pipe it (write it) to the hash object
|
|
||||
fd.pipe(hash); |
|
||||
|
|
||||
var result = 'SHA256 SUM Check: DONE'; |
|
||||
|
|
||||
console.log(result); |
|
||||
resolve(result); |
|
||||
}); |
|
||||
} |
|
||||
|
|
||||
DLFile() |
|
||||
.then(function(result) { |
|
||||
return CheckFileSHA(); |
|
||||
}); |
|
||||
} |
|
||||
}); |
|
||||
}); |
|
||||
|
|
||||
function CheckSHASum(file, hashstr) { |
|
||||
console.log(hashstr); |
|
||||
var shasum; |
|
||||
|
|
||||
// the file you want to get the hash
|
|
||||
if (shasum === hashstr ) { |
|
||||
return true; |
|
||||
} else { |
|
||||
return false; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
/*var CheckFileExists = function() { |
|
||||
|
|
||||
return new Promise(function(resolve, reject) { |
|
||||
|
|
||||
if (path.existsSync('foo.txt')) {} |
|
||||
var result = 'Connecting To Pm2: done' |
|
||||
|
|
||||
console.log(result) |
|
||||
resolve(result); |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
var DLFile = function() { |
|
||||
|
|
||||
return new Promise(function(resolve, reject) { |
|
||||
var result = 'Killing Pm2: done' |
|
||||
|
|
||||
setTimeout(function() { |
|
||||
console.log(result) |
|
||||
resolve(result); |
|
||||
}, 2000) |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
var CheckSHASum = function() { |
|
||||
|
|
||||
return new Promise(function(resolve, reject) { |
|
||||
var result = 'Hiding Main Window: done' |
|
||||
|
|
||||
console.log(result) |
|
||||
resolve(result); |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
var MoveFile = function() { |
|
||||
|
|
||||
return new Promise(function(resolve, reject) { |
|
||||
var result = 'Quiting App: done' |
|
||||
|
|
||||
console.log(result) |
|
||||
resolve(result); |
|
||||
}) |
|
||||
} |
|
||||
|
|
||||
ConnectToPm2() |
|
||||
.then(function(result) { |
|
||||
return KillPm2(); |
|
||||
}) |
|
||||
.then(HideMainWindow) |
|
||||
.then(QuitApp) |
|
||||
*/ |
|
File diff suppressed because it is too large
@ -0,0 +1,103 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.startSPV = (coin) => { |
||||
|
if (coin === 'KMD+REVS+JUMBLR') { |
||||
|
shepherd.addElectrumCoin('KMD'); |
||||
|
shepherd.addElectrumCoin('REVS'); |
||||
|
shepherd.addElectrumCoin('JUMBLR'); |
||||
|
} else { |
||||
|
shepherd.addElectrumCoin(coin); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.startKMDNative = (selection, isManual) => { |
||||
|
if (isManual) { |
||||
|
shepherd.kmdMainPassiveMode = true; |
||||
|
} |
||||
|
|
||||
|
if (selection === 'KMD') { |
||||
|
const herdData = { |
||||
|
'ac_name': 'komodod', |
||||
|
'ac_options': [ |
||||
|
'-daemon=0', |
||||
|
'-addnode=78.47.196.146', |
||||
|
], |
||||
|
}; |
||||
|
|
||||
|
const options = { |
||||
|
url: `http://127.0.0.1:${shepherd.appConfig.agamaPort}/shepherd/herd`, |
||||
|
method: 'POST', |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
body: JSON.stringify({ |
||||
|
herd: 'komodod', |
||||
|
options: herdData, |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
//resolve(body);
|
||||
|
} else { |
||||
|
//resolve(body);
|
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const herdData = [{ |
||||
|
'ac_name': 'komodod', |
||||
|
'ac_options': [ |
||||
|
'-daemon=0', |
||||
|
'-addnode=78.47.196.146', |
||||
|
] |
||||
|
}, { |
||||
|
'ac_name': 'REVS', |
||||
|
'ac_options': [ |
||||
|
'-daemon=0', |
||||
|
'-server', |
||||
|
`-ac_name=REVS`, |
||||
|
'-addnode=78.47.196.146', |
||||
|
'-ac_supply=1300000' |
||||
|
] |
||||
|
}, { |
||||
|
'ac_name': 'JUMBLR', |
||||
|
'ac_options': [ |
||||
|
'-daemon=0', |
||||
|
'-server', |
||||
|
`-ac_name=JUMBLR`, |
||||
|
'-addnode=78.47.196.146', |
||||
|
'-ac_supply=999999' |
||||
|
] |
||||
|
}]; |
||||
|
|
||||
|
for (let i = 0; i < herdData.length; i++) { |
||||
|
setTimeout(() => { |
||||
|
const options = { |
||||
|
url: `http://127.0.0.1:${shepherd.appConfig.agamaPort}/shepherd/herd`, |
||||
|
method: 'POST', |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
body: JSON.stringify({ |
||||
|
herd: 'komodod', |
||||
|
options: herdData[i], |
||||
|
}) |
||||
|
}; |
||||
|
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
//resolve(body);
|
||||
|
} else { |
||||
|
//resolve(body);
|
||||
|
} |
||||
|
}); |
||||
|
}, 100); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,78 @@ |
|||||
|
const formatBytes = (bytes, decimals) => { |
||||
|
if (bytes === 0) { |
||||
|
return '0 Bytes'; |
||||
|
} |
||||
|
|
||||
|
const k = 1000; |
||||
|
const dm = (decimals + 1) || 3; |
||||
|
const sizes = [ |
||||
|
'Bytes', |
||||
|
'KB', |
||||
|
'MB', |
||||
|
'GB', |
||||
|
'TB', |
||||
|
'PB', |
||||
|
'EB', |
||||
|
'ZB', |
||||
|
'YB' |
||||
|
]; |
||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k)); |
||||
|
|
||||
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`; |
||||
|
} |
||||
|
|
||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.SystemInfo = () => { |
||||
|
const os_data = { |
||||
|
'totalmem_bytes': shepherd.os.totalmem(), |
||||
|
'totalmem_readable': formatBytes(shepherd.os.totalmem()), |
||||
|
'arch': shepherd.os.arch(), |
||||
|
'cpu': shepherd.os.cpus()[0].model, |
||||
|
'cpu_cores': shepherd.os.cpus().length, |
||||
|
'platform': shepherd.os.platform(), |
||||
|
'os_release': shepherd.os.release(), |
||||
|
'os_type': shepherd.os.type(), |
||||
|
}; |
||||
|
|
||||
|
return os_data; |
||||
|
} |
||||
|
|
||||
|
shepherd.appInfo = () => { |
||||
|
const sysInfo = shepherd.SystemInfo(); |
||||
|
const releaseInfo = shepherd.appBasicInfo; |
||||
|
const dirs = { |
||||
|
agamaDir: shepherd.agamaDir, |
||||
|
komodoDir: shepherd.komodoDir, |
||||
|
komododBin: shepherd.komododBin, |
||||
|
configLocation: `${shepherd.agamaDir}/config.json`, |
||||
|
cacheLocation: `${shepherd.agamaDir}/shepherd`, |
||||
|
}; |
||||
|
|
||||
|
return { |
||||
|
sysInfo, |
||||
|
releaseInfo, |
||||
|
dirs, |
||||
|
appSession: shepherd.appSessionHash, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/sysinfo', (req, res, next) => { |
||||
|
const obj = shepherd.SystemInfo(); |
||||
|
res.send(obj); |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/appinfo', (req, res, next) => { |
||||
|
const obj = shepherd.appInfo(); |
||||
|
res.send(obj); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,34 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/auth/status', (req, res, next) => { // not finished
|
||||
|
let successObj; |
||||
|
let _status = false; |
||||
|
|
||||
|
if (Object.keys(shepherd.coindInstanceRegistry).length) { |
||||
|
if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { |
||||
|
_status = true; |
||||
|
} else if (Object.keys(shepherd.electrumCoins).length === 1 && !shepherd.electrumCoins.auth) { |
||||
|
_status = true; |
||||
|
} |
||||
|
} else if (Object.keys(shepherd.electrumCoins).length > 1 && shepherd.electrumCoins.auth) { |
||||
|
_status = true; |
||||
|
} else if (Object.keys(shepherd.electrumCoins).length === 1 && !Object.keys(shepherd.coindInstanceRegistry).length) { |
||||
|
_status = true; |
||||
|
} |
||||
|
|
||||
|
successObj = { |
||||
|
pubkey: 'nativeonly', |
||||
|
result: 'success', |
||||
|
handle: '', |
||||
|
status: _status ? 'unlocked' : 'locked', |
||||
|
duration: 2507830, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,239 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.testClearAll = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
shepherd.fs.removeSync(`${iguanaTestDir}`); |
||||
|
resolve('done'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.testBins = (daemonName) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const _bins = { |
||||
|
komodod: shepherd.komododBin, |
||||
|
komodoCli: shepherd.komodocliBin, |
||||
|
}; |
||||
|
const _arg = null; |
||||
|
let _pid; |
||||
|
|
||||
|
shepherd.log('testBins exec ' + _bins[daemonName]); |
||||
|
|
||||
|
if (!shepherd.fs.existsSync(shepherd.agamaTestDir)) { |
||||
|
shepherd.fs.mkdirSync(shepherd.agamaTestDir); |
||||
|
} |
||||
|
|
||||
|
try { |
||||
|
shepherd._fs.access(`${shepherd.agamaTestDir}/${daemonName}Test.log`, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (!err) { |
||||
|
try { |
||||
|
shepherd._fs.unlinkSync(`${shepherd.agamaTestDir}/${daemonName}Test.log`); |
||||
|
} catch (e) {} |
||||
|
} else { |
||||
|
shepherd.log(`path ${shepherd.agamaTestDir}/${daemonName}Test.log doesnt exist`); |
||||
|
} |
||||
|
}); |
||||
|
} catch (e) {} |
||||
|
|
||||
|
if (daemonName === 'komodod') { |
||||
|
try { |
||||
|
shepherd._fs.access(`${iguanaTestDir}/debug.log`, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (!err) { |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/db.log`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/debug.log`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodo.conf`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodod.pid`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/komodostate`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/realtime`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/wallet.dat`); |
||||
|
shepherd._fs.unlinkSync(`${iguanaTestDir}/.lock`); |
||||
|
shepherd.fs.removeSync(`${iguanaTestDir}/blocks`); |
||||
|
shepherd.fs.removeSync(`${iguanaTestDir}/chainstate`); |
||||
|
shepherd.fs.removeSync(`${iguanaTestDir}/database`); |
||||
|
execKomodod(); |
||||
|
} else { |
||||
|
shepherd.log(`test: nothing to remove in ${iguanaTestDir}`); |
||||
|
execKomodod(); |
||||
|
} |
||||
|
}); |
||||
|
} catch (e) {} |
||||
|
|
||||
|
const execKomodod = () => { |
||||
|
let _komododTest = { |
||||
|
port: 'unknown', |
||||
|
start: 'unknown', |
||||
|
getinfo: 'unknown', |
||||
|
errors: { |
||||
|
assertFailed: false, |
||||
|
zcashParams: false, |
||||
|
}, |
||||
|
}; |
||||
|
const _komodoConf = 'rpcuser=user83f3afba8d714993\n' + |
||||
|
'rpcpassword=0d4430ca1543833e35bce5a0cc9e16b3\n' + |
||||
|
'server=1\n' + |
||||
|
'addnode=78.47.196.146\n' + |
||||
|
'addnode=5.9.102.210\n' + |
||||
|
'addnode=178.63.69.164\n' + |
||||
|
'addnode=88.198.65.74\n' + |
||||
|
'addnode=5.9.122.241\n' + |
||||
|
'addnode=144.76.94.3\n' + |
||||
|
'addnode=144.76.94.38\n' + |
||||
|
'addnode=89.248.166.91\n' + |
||||
|
'addnode=148.251.57.148\n' + |
||||
|
'addnode=149.56.28.84\n' + |
||||
|
'addnode=176.9.26.39\n' + |
||||
|
'addnode=94.102.63.199\n' + |
||||
|
'addnode=94.102.63.200\n' + |
||||
|
'addnode=104.255.64.3\n' + |
||||
|
'addnode=221.121.144.140\n' + |
||||
|
'addnode=103.18.58.150\n' + |
||||
|
'addnode=103.18.58.146\n' + |
||||
|
'addnode=213.202.253.10\n' + |
||||
|
'addnode=185.106.121.32\n' + |
||||
|
'addnode=27.100.36.201\n'; |
||||
|
|
||||
|
shepherd.fs.writeFile(`${iguanaTestDir}/komodo.conf`, _komodoConf, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`test: error writing komodo conf in ${iguanaTestDir}`); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.portscanner.checkPortStatus('7771', '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'closed') { |
||||
|
_komododTest.port = 'passed'; |
||||
|
} else { |
||||
|
_komododTest.port = 'failed'; |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/*pm2.connect(true,function(err) { //start up pm2 god |
||||
|
if (err) { |
||||
|
shepherd.error(err); |
||||
|
process.exit(2); |
||||
|
} |
||||
|
|
||||
|
pm2.start({ |
||||
|
script: shepherd.komododBin, // path to binary
|
||||
|
name: 'komodod', |
||||
|
exec_mode : 'fork', |
||||
|
args: [ |
||||
|
'-daemon=0', |
||||
|
'-addnode=78.47.196.146', |
||||
|
`-datadir=${iguanaTestDir}/` |
||||
|
], |
||||
|
output: `${iguanaTestDir}/komododTest.log`, |
||||
|
mergeLogs: true, |
||||
|
}, function(err, apps) { |
||||
|
if (apps[0] && |
||||
|
apps[0].process && |
||||
|
apps[0].process.pid) { |
||||
|
_komododTest.start = 'success'; |
||||
|
shepherd.log(`test: got komodod instance pid = ${apps[0].process.pid}`); |
||||
|
shepherd.writeLog(`test: komodod started with pid ${apps[0].process.pid}`); |
||||
|
} else { |
||||
|
_komododTest.start = 'failed'; |
||||
|
shepherd.log(`unable to start komodod`); |
||||
|
} |
||||
|
|
||||
|
pm2.disconnect(); // Disconnect from PM2
|
||||
|
if (err) { |
||||
|
shepherd.writeLog(`test: error starting komodod`); |
||||
|
shepherd.log(`komodod fork err: ${err}`); |
||||
|
// throw err;
|
||||
|
} |
||||
|
}); |
||||
|
});*/ |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
const options = { |
||||
|
url: `http://localhost:7771`, |
||||
|
method: 'POST', |
||||
|
auth: { |
||||
|
user: 'user83f3afba8d714993', |
||||
|
pass: '0d4430ca1543833e35bce5a0cc9e16b3', |
||||
|
}, |
||||
|
body: JSON.stringify({ |
||||
|
agent: 'bitcoinrpc', |
||||
|
method: 'getinfo', |
||||
|
}), |
||||
|
}; |
||||
|
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
// res.end(body);
|
||||
|
shepherd.log(JSON.stringify(body, null, '\t')); |
||||
|
} else { |
||||
|
// res.end(body);
|
||||
|
shepherd.log(JSON.stringify(body, null, '\t')); |
||||
|
} |
||||
|
}); |
||||
|
}, 10000); |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
pm2.delete('komodod'); |
||||
|
resolve(_komododTest); |
||||
|
}, 20000); |
||||
|
} |
||||
|
// komodod debug.log hooks
|
||||
|
|
||||
|
//"{\"result\":{\"version\":1000850,\"protocolversion\":170002,\"KMDversion\":\"0.1.1\",\"notarized\":0,\"notarizedhash\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid\":\"0000000000000000000000000000000000000000000000000000000000000000\",\"notarizedtxid_height\":\"mempool\",\"notarized_confirms\":0,\"walletversion\":60000,\"balance\":0.00000000,\"interest\":0.00000000,\"blocks\":128,\"longestchain\":472331,\"timeoffset\":0,\"tiptime\":1473827710,\"connections\":1,\"proxy\":\"\",\"difficulty\":1,\"testnet\":false,\"keypoololdest\":1504118047,\"keypoolsize\":101,\"paytxfee\":0.00000000,\"relayfee\":0.00000100,\"errors\":\"\"},\"error\":null,\"id\":null}\n"
|
||||
|
|
||||
|
//2017-08-30 17:51:33 Error: Cannot find the Zcash network parameters in the following directory:
|
||||
|
//"/home/pbca/.zcash-params"
|
||||
|
//Please run 'zcash-fetch-params' or './zcutil/fetch-params.sh' and then restart.
|
||||
|
//EXCEPTION: St13runtime_error
|
||||
|
//Assertion failed.
|
||||
|
//2017-08-30 17:51:14 Using config file /home/pbca/.iguana/test/komodo.conf
|
||||
|
//2017-08-30 18:23:43 UpdateTip: new best=0a47c1323f393650f7221c217d19d149d002d35444f47fde61be2dd90fbde8e6 height=1 log2_work=5.0874628 tx=2 date=2016-09-13 19:04:01 progress=0.000001 cache=0.0MiB(1tx)
|
||||
|
//2017-08-30 18:23:43 UpdateTip: new best=05076a4e1fc9af0f5fda690257b17ae20c12d4796dfba1624804d012c9ec00be height=2 log2_work=5.6724253 tx=3 date=2016-09-13 19:05:28 progress=0.000001 cache=0.0MiB(2tx)
|
||||
|
|
||||
|
/*shepherd.execFile(`${shepherd.komododBin}`, _arg, { |
||||
|
maxBuffer: 1024 * 10000 // 10 mb
|
||||
|
}, function(error, stdout, stderr) { |
||||
|
shepherd.writeLog(`stdout: ${stdout}`); |
||||
|
shepherd.writeLog(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
console.log(`exec error: ${error}`); |
||||
|
shepherd.writeLog(`exec error: ${error}`); |
||||
|
|
||||
|
if (error.toString().indexOf('using -reindex') > -1) { |
||||
|
shepherd.io.emit('service', { |
||||
|
komodod: { |
||||
|
error: 'run -reindex', |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
});*/ |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
// komodod datadir location test
|
||||
|
shepherd.testLocation = (path) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
if (shepherd.path.indexOf(' ') > -1) { |
||||
|
shepherd.log(`error testing path ${path}`); |
||||
|
resolve(-1); |
||||
|
} else { |
||||
|
shepherd.fs.lstat(path, (err, stats) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`error testing path ${path}`); |
||||
|
resolve(-1); |
||||
|
} else { |
||||
|
if (stats.isDirectory()) { |
||||
|
resolve(true); |
||||
|
} else { |
||||
|
shepherd.log(`error testing path ${path} not a folder`); |
||||
|
resolve(false); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,70 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
// osx and linux
|
||||
|
shepherd.binFixRights = () => { |
||||
|
const osPlatform = shepherd.os.platform(); |
||||
|
const _bins = [ |
||||
|
shepherd.komododBin, |
||||
|
shepherd.komodocliBin |
||||
|
]; |
||||
|
|
||||
|
if (osPlatform === 'darwin' || |
||||
|
osPlatform === 'linux') { |
||||
|
for (let i = 0; i < _bins.length; i++) { |
||||
|
shepherd._fs.stat(_bins[i], (err, stat) => { |
||||
|
if (!err) { |
||||
|
if (parseInt(stat.mode.toString(8), 10) !== 100775) { |
||||
|
shepherd.log(`${_bins[i]} fix permissions`); |
||||
|
shepherd.fsnode.chmodSync(_bins[i], '0775'); |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log(`error: ${_bins[i]} not found`); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.killRogueProcess = (processName) => { |
||||
|
// kill rogue process copies on start
|
||||
|
let processGrep; |
||||
|
const osPlatform = shepherd.os.platform(); |
||||
|
|
||||
|
switch (osPlatform) { |
||||
|
case 'darwin': |
||||
|
processGrep = "ps -p $(ps -A | grep -m1 " + processName + " | awk '{print $1}') | grep -i " + processName; |
||||
|
break; |
||||
|
case 'linux': |
||||
|
processGrep = 'ps -p $(pidof ' + processName + ') | grep -i ' + processName; |
||||
|
break; |
||||
|
case 'win32': |
||||
|
processGrep = 'tasklist'; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
shepherd.exec(processGrep, (error, stdout, stderr) => { |
||||
|
if (stdout.indexOf(processName) > -1) { |
||||
|
const pkillCmd = osPlatform === 'win32' ? `taskkill /f /im ${processName}.exe` : `pkill -15 ${processName}`; |
||||
|
|
||||
|
shepherd.log(`found another ${processName} process(es)`); |
||||
|
shepherd.writeLog(`found another ${processName} process(es)`); |
||||
|
|
||||
|
shepherd.exec(pkillCmd, (error, stdout, stderr) => { |
||||
|
shepherd.log(`${pkillCmd} is issued`); |
||||
|
shepherd.writeLog(`${pkillCmd} is issued`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`${pkillCmd} exec error: ${error}`); |
||||
|
shepherd.writeLog(`${pkillCmd} exec error: ${error}`); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`${processGrep} exec error: ${error}`); |
||||
|
shepherd.writeLog(`${processGrep} exec error: ${error}`); |
||||
|
}; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,31 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/InstantDEX/allcoins', (req, res, next) => { |
||||
|
let successObj; |
||||
|
let nativeCoindList = []; |
||||
|
let electrumCoinsList = []; |
||||
|
|
||||
|
for (let key in shepherd.electrumCoins) { |
||||
|
if (key !== 'auth') { |
||||
|
electrumCoinsList.push(shepherd.electrumCoins[key].abbr); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let key in shepherd.coindInstanceRegistry) { |
||||
|
nativeCoindList.push(key === 'komodod' ? 'KMD' : key); |
||||
|
} |
||||
|
|
||||
|
successObj = { |
||||
|
native: nativeCoindList, |
||||
|
spv: electrumCoinsList, |
||||
|
total: Object.keys(shepherd.electrumCoins).length - 1 + Object.keys(nativeCoindList).length, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,71 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/coinslist', (req, res, next) => { |
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/coinslist.json`)) { |
||||
|
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, 'utf8', (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: 'coin list doesn\'t exist', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: payload |
||||
|
*/ |
||||
|
shepherd.post('/coinslist', (req, res, next) => { |
||||
|
const _payload = req.body.payload; |
||||
|
|
||||
|
if (!_payload) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no payload provided', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/coinslist.json`, JSON.stringify(_payload), (err) => { |
||||
|
if (err) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: err, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'done', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,55 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.getMaxconKMDConf = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`kmd conf maxconnections param read failed`); |
||||
|
resolve('unset'); |
||||
|
} else { |
||||
|
const _maxcon = data.match(/maxconnections=\s*(.*)/); |
||||
|
|
||||
|
if (!_maxcon) { |
||||
|
shepherd.log(`kmd conf maxconnections param is unset`); |
||||
|
resolve(false); |
||||
|
} else { |
||||
|
shepherd.log(`kmd conf maxconnections param is already set to ${_maxcon[1]}`); |
||||
|
resolve(_maxcon[1]); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.setMaxconKMDConf = (limit) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
shepherd.fs.readFile(`${shepherd.komodoDir}/komodo.conf`, 'utf8', (err, data) => { |
||||
|
const _maxconVal = limit ? 1 : 10; |
||||
|
|
||||
|
if (err) { |
||||
|
shepherd.log(`error reading ${shepherd.komodoDir}/komodo.conf`); |
||||
|
resolve(false); |
||||
|
} else { |
||||
|
if (data.indexOf('maxconnections=') > -1) { |
||||
|
const _maxcon = data.match(/maxconnections=\s*(.*)/); |
||||
|
|
||||
|
data = data.replace(`maxconnections=${_maxcon[1]}`, `maxconnections=${_maxconVal}`); |
||||
|
} else { |
||||
|
data = `${data}\nmaxconnections=${_maxconVal}\n`; |
||||
|
} |
||||
|
|
||||
|
shepherd.fs.writeFile(`${shepherd.komodoDir}/komodo.conf`, data, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`error writing ${shepherd.komodoDir}/komodo.conf maxconnections=${_maxconVal}`); |
||||
|
resolve(false); |
||||
|
} else { |
||||
|
shepherd.log(`kmd conf maxconnections is set to ${_maxconVal}`); |
||||
|
resolve(true); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,152 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.loadLocalConfig = () => { |
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/config.json`)) { |
||||
|
let localAppConfig = shepherd.fs.readFileSync(`${shepherd.agamaDir}/config.json`, 'utf8'); |
||||
|
|
||||
|
shepherd.log('app config set from local file'); |
||||
|
shepherd.writeLog('app config set from local file'); |
||||
|
|
||||
|
// find diff between local and hardcoded configs
|
||||
|
// append diff to local config
|
||||
|
const compareJSON = (obj1, obj2) => { |
||||
|
let result = {}; |
||||
|
|
||||
|
for (let i in obj1) { |
||||
|
if (!obj2.hasOwnProperty(i)) { |
||||
|
result[i] = obj1[i]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return result; |
||||
|
}; |
||||
|
|
||||
|
if (localAppConfig) { |
||||
|
const compareConfigs = compareJSON(shepherd.appConfig, JSON.parse(localAppConfig)); |
||||
|
|
||||
|
if (Object.keys(compareConfigs).length) { |
||||
|
const newConfig = Object.assign(JSON.parse(localAppConfig), compareConfigs); |
||||
|
|
||||
|
shepherd.log('config diff is found, updating local config'); |
||||
|
shepherd.log('config diff:'); |
||||
|
shepherd.log(compareConfigs); |
||||
|
shepherd.writeLog('aconfig diff is found, updating local config'); |
||||
|
shepherd.writeLog('config diff:'); |
||||
|
shepherd.writeLog(compareConfigs); |
||||
|
|
||||
|
shepherd.saveLocalAppConf(newConfig); |
||||
|
return newConfig; |
||||
|
} else { |
||||
|
return JSON.parse(localAppConfig); |
||||
|
} |
||||
|
} else { |
||||
|
return shepherd.appConfig; |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log('local config file is not found!'); |
||||
|
shepherd.writeLog('local config file is not found!'); |
||||
|
shepherd.saveLocalAppConf(shepherd.appConfig); |
||||
|
|
||||
|
return shepherd.appConfig; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
shepherd.saveLocalAppConf = (appSettings) => { |
||||
|
let appConfFileName = `${shepherd.agamaDir}/config.json`; |
||||
|
|
||||
|
shepherd._fs.access(shepherd.agamaDir, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (!err) { |
||||
|
|
||||
|
const FixFilePermissions = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'config.json file permissions updated to Read/Write'; |
||||
|
|
||||
|
shepherd.fsnode.chmodSync(appConfFileName, '0666'); |
||||
|
|
||||
|
setTimeout(() => { |
||||
|
shepherd.log(result); |
||||
|
shepherd.writeLog(result); |
||||
|
resolve(result); |
||||
|
}, 1000); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const FsWrite = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'config.json write file is done'; |
||||
|
|
||||
|
shepherd.fs.writeFile(appConfFileName, |
||||
|
JSON.stringify(appSettings) |
||||
|
.replace(/,/g, ',\n') // format json in human readable form
|
||||
|
.replace(/":/g, '": ') |
||||
|
.replace(/{/g, '{\n') |
||||
|
.replace(/}/g, '\n}'), 'utf8', (err) => { |
||||
|
if (err) |
||||
|
return shepherd.log(err); |
||||
|
}); |
||||
|
|
||||
|
shepherd.fsnode.chmodSync(appConfFileName, '0666'); |
||||
|
setTimeout(() => { |
||||
|
shepherd.log(result); |
||||
|
shepherd.log(`app conf.json file is created successfully at: ${shepherd.agamaDir}`); |
||||
|
shepherd.writeLog(`app conf.json file is created successfully at: ${shepherd.agamaDir}`); |
||||
|
resolve(result); |
||||
|
}, 2000); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
FsWrite() |
||||
|
.then(FixFilePermissions()); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: payload |
||||
|
*/ |
||||
|
shepherd.post('/appconf', (req, res, next) => { |
||||
|
if (!req.body.payload) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no payload provided', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
shepherd.saveLocalAppConf(req.body.payload); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'config saved', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: none |
||||
|
*/ |
||||
|
shepherd.post('/appconf/reset', (req, res, next) => { |
||||
|
shepherd.saveLocalAppConf(shepherd.defaultAppConfig); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'config saved', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: GET |
||||
|
* |
||||
|
*/ |
||||
|
shepherd.get('/appconf', (req, res, next) => { |
||||
|
const obj = shepherd.loadLocalConfig(); |
||||
|
res.send(obj); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,868 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
const getConf = (flock, coind) => { |
||||
|
let DaemonConfPath = ''; |
||||
|
let nativeCoindDir; |
||||
|
|
||||
|
if (flock === 'CHIPS') { |
||||
|
flock = 'chipsd'; |
||||
|
} |
||||
|
|
||||
|
shepherd.log(flock); |
||||
|
shepherd.log(`getconf coind ${coind}`); |
||||
|
shepherd.writeLog(`getconf flock: ${flock}`); |
||||
|
|
||||
|
switch (shepherd.os.platform()) { |
||||
|
case 'darwin': |
||||
|
nativeCoindDir = `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}`; |
||||
|
break; |
||||
|
case 'linux': |
||||
|
nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; |
||||
|
break; |
||||
|
case 'win32': |
||||
|
nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
switch (flock) { |
||||
|
case 'komodod': |
||||
|
DaemonConfPath = shepherd.komodoDir; |
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
shepherd.log('===>>> SHEPHERD API OUTPUT ===>>>'); |
||||
|
} |
||||
|
break; |
||||
|
case 'zcashd': |
||||
|
DaemonConfPath = shepherd.ZcashDir; |
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
case 'chipsd': |
||||
|
DaemonConfPath = shepherd.chipsDir; |
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
case 'coind': |
||||
|
DaemonConfPath = shepherd.os.platform() === 'win32' ? shepherd.path.normalize(`${shepherd.coindRootDir}/${coind.toLowerCase()}`) : `${shepherd.coindRootDir}/${coind.toLowerCase()}`; |
||||
|
break; |
||||
|
default: |
||||
|
DaemonConfPath = `${shepherd.komodoDir}/${flock}`; |
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.writeLog(`getconf path: ${DaemonConfPath}`); |
||||
|
shepherd.log(`daemon path: ${DaemonConfPath}`); |
||||
|
|
||||
|
return DaemonConfPath; |
||||
|
} |
||||
|
|
||||
|
// TODO: json.stringify wrapper
|
||||
|
|
||||
|
const herder = (flock, data, coind) => { |
||||
|
if (data === undefined) { |
||||
|
data = 'none'; |
||||
|
shepherd.log('it is undefined'); |
||||
|
} |
||||
|
|
||||
|
shepherd.log(`herder flock: ${flock} coind: ${coind}`); |
||||
|
shepherd.log(`selected data: ${JSON.stringify(data, null, '\t')}`); |
||||
|
|
||||
|
// TODO: notify gui that reindex/rescan param is used to reflect on the screen
|
||||
|
// asset chain debug.log unlink
|
||||
|
if (flock === 'komodod') { |
||||
|
let kmdDebugLogLocation = (data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir) + '/debug.log'; |
||||
|
|
||||
|
shepherd.log('komodod flock selected...'); |
||||
|
shepherd.log(`selected data: ${JSON.stringify(data, null, '\t')}`); |
||||
|
shepherd.writeLog('komodod flock selected...'); |
||||
|
shepherd.writeLog(`selected data: ${data}`); |
||||
|
|
||||
|
// datadir case, check if komodo/chain folder exists
|
||||
|
if (shepherd.appConfig.dataDir.length && |
||||
|
data.ac_name !== 'komodod') { |
||||
|
const _dir = data.ac_name !== 'komodod' ? `${shepherd.komodoDir}/${data.ac_name}` : shepherd.komodoDir; |
||||
|
|
||||
|
try { |
||||
|
shepherd._fs.accessSync(_dir, shepherd.fs.R_OK | shepherd.fs.W_OK); |
||||
|
|
||||
|
shepherd.log(`komodod datadir ${_dir} exists`); |
||||
|
} catch (e) { |
||||
|
shepherd.log(`komodod datadir ${_dir} access err: ${e}`); |
||||
|
shepherd.log(`attempting to create komodod datadir ${_dir}`); |
||||
|
|
||||
|
shepherd.fs.mkdirSync(_dir); |
||||
|
|
||||
|
if (shepherd.fs.existsSync(_dir)) { |
||||
|
shepherd.log(`created komodod datadir folder at ${_dir}`); |
||||
|
} else { |
||||
|
shepherd.log(`unable to create komodod datadir folder at ${_dir}`); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// truncate debug.log
|
||||
|
if (!shepherd.kmdMainPassiveMode) { |
||||
|
try { |
||||
|
const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); |
||||
|
|
||||
|
if (_confFileAccess) { |
||||
|
shepherd.log(`error accessing ${kmdDebugLogLocation}`); |
||||
|
shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); |
||||
|
} else { |
||||
|
try { |
||||
|
shepherd.fs.unlinkSync(kmdDebugLogLocation); |
||||
|
shepherd.log(`truncate ${kmdDebugLogLocation}`); |
||||
|
shepherd.writeLog(`truncate ${kmdDebugLogLocation}`); |
||||
|
} catch (e) { |
||||
|
shepherd.log('cant unlink debug.log'); |
||||
|
} |
||||
|
} |
||||
|
} catch (e) { |
||||
|
shepherd.log(`komodod debug.log access err: ${e}`); |
||||
|
shepherd.writeLog(`komodod debug.log access err: ${e}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// get komodod instance port
|
||||
|
const _port = shepherd.assetChainPorts[data.ac_name]; |
||||
|
|
||||
|
try { |
||||
|
// check if komodod instance is already running
|
||||
|
shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'closed') { |
||||
|
// start komodod via exec
|
||||
|
const _customParamDict = { |
||||
|
silent: '&', |
||||
|
reindex: '-reindex', |
||||
|
change: '-pubkey=', |
||||
|
datadir: '-datadir=', |
||||
|
rescan: '-rescan', |
||||
|
}; |
||||
|
let _customParam = ''; |
||||
|
|
||||
|
if (data.ac_custom_param === 'silent' || |
||||
|
data.ac_custom_param === 'reindex' || |
||||
|
data.ac_custom_param === 'rescan') { |
||||
|
_customParam = ` ${_customParamDict[data.ac_custom_param]}`; |
||||
|
} else if (data.ac_custom_param === 'change' && data.ac_custom_param_value) { |
||||
|
_customParam = ` ${_customParamDict[data.ac_custom_param]}${data.ac_custom_param_value}`; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.appConfig.dataDir.length) { |
||||
|
_customParam = _customParam + ' -datadir=' + shepherd.appConfig.dataDir + (data.ac_name !== 'komodod' ? '/' + data.ac_name : ''); |
||||
|
} |
||||
|
|
||||
|
shepherd.log(`exec ${shepherd.komododBin} ${data.ac_options.join(' ')}${_customParam}`); |
||||
|
shepherd.writeLog(`exec ${shepherd.komododBin} ${data.ac_options.join(' ')}${_customParam}`); |
||||
|
|
||||
|
const isChain = data.ac_name.match(/^[A-Z]*$/); |
||||
|
const coindACParam = isChain ? ` -ac_name=${data.ac_name} ` : ''; |
||||
|
shepherd.log(`daemon param ${data.ac_custom_param}`); |
||||
|
|
||||
|
shepherd.coindInstanceRegistry[data.ac_name] = true; |
||||
|
if (!shepherd.kmdMainPassiveMode) { |
||||
|
let _arg = `${coindACParam}${data.ac_options.join(' ')}${_customParam}`; |
||||
|
_arg = _arg.trim().split(' '); |
||||
|
shepherd.execFile(`${shepherd.komododBin}`, _arg, { |
||||
|
maxBuffer: 1024 * 1000000 // 1000 mb
|
||||
|
}, (error, stdout, stderr) => { |
||||
|
shepherd.writeLog(`stdout: ${stdout}`); |
||||
|
shepherd.writeLog(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
shepherd.writeLog(`exec error: ${error}`); |
||||
|
|
||||
|
if (error.toString().indexOf('using -reindex') > -1) { |
||||
|
shepherd.io.emit('service', { |
||||
|
komodod: { |
||||
|
error: 'run -reindex', |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} else { |
||||
|
if (shepherd.kmdMainPassiveMode) { |
||||
|
shepherd.coindInstanceRegistry[data.ac_name] = true; |
||||
|
} |
||||
|
shepherd.log(`port ${_port} (${data.ac_name}) is already in use`); |
||||
|
shepherd.writeLog(`port ${_port} (${data.ac_name}) is already in use`); |
||||
|
} |
||||
|
}); |
||||
|
} catch(e) { |
||||
|
shepherd.log(`failed to start komodod err: ${e}`); |
||||
|
shepherd.writeLog(`failed to start komodod err: ${e}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// TODO: refactor
|
||||
|
if (flock === 'chipsd') { |
||||
|
let kmdDebugLogLocation = `${shepherd.chipsDir}/debug.log`; |
||||
|
|
||||
|
shepherd.log('chipsd flock selected...'); |
||||
|
shepherd.log(`selected data: ${JSON.stringify(data, null, '\t')}`); |
||||
|
shepherd.writeLog('chipsd flock selected...'); |
||||
|
shepherd.writeLog(`selected data: ${data}`); |
||||
|
|
||||
|
// truncate debug.log
|
||||
|
try { |
||||
|
const _confFileAccess = shepherd._fs.accessSync(kmdDebugLogLocation, shepherd.fs.R_OK | shepherd.fs.W_OK); |
||||
|
|
||||
|
if (_confFileAccess) { |
||||
|
shepherd.log(`error accessing ${kmdDebugLogLocation}`); |
||||
|
shepherd.writeLog(`error accessing ${kmdDebugLogLocation}`); |
||||
|
} else { |
||||
|
try { |
||||
|
shepherd.fs.unlinkSync(kmdDebugLogLocation); |
||||
|
shepherd.log(`truncate ${kmdDebugLogLocation}`); |
||||
|
shepherd.writeLog(`truncate ${kmdDebugLogLocation}`); |
||||
|
} catch (e) { |
||||
|
shepherd.log('cant unlink debug.log'); |
||||
|
} |
||||
|
} |
||||
|
} catch(e) { |
||||
|
shepherd.log(`chipsd debug.log access err: ${e}`); |
||||
|
shepherd.writeLog(`chipsd debug.log access err: ${e}`); |
||||
|
} |
||||
|
|
||||
|
// get komodod instance port
|
||||
|
const _port = shepherd.assetChainPorts.chipsd; |
||||
|
|
||||
|
try { |
||||
|
// check if komodod instance is already running
|
||||
|
shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'closed') { |
||||
|
// start komodod via exec
|
||||
|
const _customParamDict = { |
||||
|
silent: '&', |
||||
|
reindex: '-reindex', |
||||
|
change: '-pubkey=', |
||||
|
rescan: '-rescan', |
||||
|
}; |
||||
|
let _customParam = ''; |
||||
|
|
||||
|
if (data.ac_custom_param === 'silent' || |
||||
|
data.ac_custom_param === 'reindex' || |
||||
|
data.ac_custom_param === 'rescan') { |
||||
|
_customParam = ` ${_customParamDict[data.ac_custom_param]}`; |
||||
|
} else if (data.ac_custom_param === 'change' && data.ac_custom_param_value) { |
||||
|
_customParam = ` ${_customParamDict[data.ac_custom_param]}${data.ac_custom_param_value}`; |
||||
|
} |
||||
|
|
||||
|
shepherd.log(`exec ${shepherd.chipsBin} ${_customParam}`); |
||||
|
shepherd.writeLog(`exec ${shepherd.chipsBin} ${_customParam}`); |
||||
|
|
||||
|
shepherd.log(`daemon param ${data.ac_custom_param}`); |
||||
|
|
||||
|
shepherd.coindInstanceRegistry['CHIPS'] = true; |
||||
|
let _arg = `${_customParam}`; |
||||
|
_arg = _arg.trim().split(' '); |
||||
|
|
||||
|
if (_arg && |
||||
|
_arg.length > 1) { |
||||
|
shepherd.execFile(`${shepherd.chipsBin}`, _arg, { |
||||
|
maxBuffer: 1024 * 1000000 // 1000 mb
|
||||
|
}, (error, stdout, stderr) => { |
||||
|
shepherd.writeLog(`stdout: ${stdout}`); |
||||
|
shepherd.writeLog(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
shepherd.writeLog(`exec error: ${error}`); |
||||
|
|
||||
|
if (error.toString().indexOf('using -reindex') > -1) { |
||||
|
shepherd.io.emit('service', { |
||||
|
komodod: { |
||||
|
error: 'run -reindex', |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.execFile(`${shepherd.chipsBin}`, { |
||||
|
maxBuffer: 1024 * 1000000 // 1000 mb
|
||||
|
}, (error, stdout, stderr) => { |
||||
|
shepherd.writeLog(`stdout: ${stdout}`); |
||||
|
shepherd.writeLog(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
shepherd.writeLog(`exec error: ${error}`); |
||||
|
|
||||
|
if (error.toString().indexOf('using -reindex') > -1) { |
||||
|
shepherd.io.emit('service', { |
||||
|
komodod: { |
||||
|
error: 'run -reindex', |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} catch(e) { |
||||
|
shepherd.log(`failed to start chipsd err: ${e}`); |
||||
|
shepherd.writeLog(`failed to start chipsd err: ${e}`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (flock === 'zcashd') { // TODO: fix(?)
|
||||
|
let kmdDebugLogLocation = `${shepherd.zcashDir}/debug.log`; |
||||
|
|
||||
|
shepherd.log('zcashd flock selected...'); |
||||
|
shepherd.log(`selected data: ${data}`); |
||||
|
shepherd.writeLog('zcashd flock selected...'); |
||||
|
shepherd.writeLog(`selected data: ${data}`); |
||||
|
|
||||
|
/*pm2.connect(true, function(err) { // start up pm2 god |
||||
|
if (err) { |
||||
|
shepherd.error(err); |
||||
|
process.exit(2); |
||||
|
} |
||||
|
|
||||
|
pm2.start({ |
||||
|
script: shepherd.zcashdBin, // path to binary
|
||||
|
name: data.ac_name, // REVS, USD, EUR etc.
|
||||
|
exec_mode: 'fork', |
||||
|
cwd: shepherd.zcashDir, |
||||
|
args: data.ac_options |
||||
|
}, function(err, apps) { |
||||
|
shepherd.writeLog(`zcashd fork started ${data.ac_name} ${JSON.stringify(data.ac_options)}`); |
||||
|
|
||||
|
pm2.disconnect(); // Disconnect from PM2
|
||||
|
if (err) { |
||||
|
shepherd.writeLog(`pm2.disconnect err: ${err}`); |
||||
|
shepherd.log(`pm2.disconnect err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
}); |
||||
|
});*/ |
||||
|
} |
||||
|
|
||||
|
if (flock === 'coind') { |
||||
|
const _osHome = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; |
||||
|
let coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}/debug.log`; |
||||
|
|
||||
|
shepherd.log(`coind ${coind} flock selected...`); |
||||
|
shepherd.log(`selected data: ${JSON.stringify(data, null, '\t')}`); |
||||
|
shepherd.writeLog(`coind ${coind} flock selected...`); |
||||
|
shepherd.writeLog(`selected data: ${data}`); |
||||
|
|
||||
|
// truncate debug.log
|
||||
|
try { |
||||
|
shepherd._fs.access(coindDebugLogLocation, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`error accessing ${coindDebugLogLocation}`); |
||||
|
shepherd.writeLog(`error accessing ${coindDebugLogLocation}`); |
||||
|
} else { |
||||
|
shepherd.log(`truncate ${coindDebugLogLocation}`); |
||||
|
shepherd.writeLog(`truncate ${coindDebugLogLocation}`); |
||||
|
shepherd.fs.unlink(coindDebugLogLocation); |
||||
|
} |
||||
|
}); |
||||
|
} catch(e) { |
||||
|
shepherd.log(`coind ${coind} debug.log access err: ${e}`); |
||||
|
shepherd.writeLog(`coind ${coind} debug.log access err: ${e}`); |
||||
|
} |
||||
|
|
||||
|
// get komodod instance port
|
||||
|
const _port = shepherd.nativeCoindList[coind.toLowerCase()].port; |
||||
|
const coindBin = `${shepherd.coindRootDir}/${coind.toLowerCase()}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}d`; |
||||
|
|
||||
|
try { |
||||
|
// check if coind instance is already running
|
||||
|
shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'closed') { |
||||
|
shepherd.log(`exec ${coindBin} ${data.ac_options.join(' ')}`); |
||||
|
shepherd.writeLog(`exec ${coindBin} ${data.ac_options.join(' ')}`); |
||||
|
|
||||
|
shepherd.coindInstanceRegistry[coind] = true; |
||||
|
let _arg = `${data.ac_options.join(' ')}`; |
||||
|
_arg = _arg.trim().split(' '); |
||||
|
shepherd.execFile(`${coindBin}`, _arg, { |
||||
|
maxBuffer: 1024 * 1000000 // 1000 mb
|
||||
|
}, (error, stdout, stderr) => { |
||||
|
shepherd.writeLog(`stdout: ${stdout}`); |
||||
|
shepherd.writeLog(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
shepherd.writeLog(`exec error: ${error}`); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.log(`port ${_port} (${coind}) is already in use`); |
||||
|
shepherd.writeLog(`port ${_port} (${coind}) is already in use`); |
||||
|
} |
||||
|
}); |
||||
|
} catch(e) { |
||||
|
shepherd.log(`failed to start ${coind} err: ${e}`); |
||||
|
shepherd.writeLog(`failed to start ${coind} err: ${e}`); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const setConf = (flock, coind) => { |
||||
|
let nativeCoindDir; |
||||
|
let DaemonConfPath; |
||||
|
|
||||
|
shepherd.log(flock); |
||||
|
shepherd.writeLog(`setconf ${flock}`); |
||||
|
|
||||
|
if (shepherd.os.platform() === 'darwin') { |
||||
|
nativeCoindDir = coind ? `${process.env.HOME}/Library/Application Support/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.os.platform() === 'linux') { |
||||
|
nativeCoindDir = coind ? `${process.env.HOME}/.${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}` : null; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
nativeCoindDir = coind ? `${process.env.APPDATA}/${shepherd.nativeCoindList[coind.toLowerCase()].bin}` : null; |
||||
|
} |
||||
|
|
||||
|
switch (flock) { |
||||
|
case 'komodod': |
||||
|
DaemonConfPath = `${shepherd.komodoDir}/komodo.conf`; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
case 'zcashd': |
||||
|
DaemonConfPath = `${shepherd.ZcashDir}/zcash.conf`; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
case 'chipsd': |
||||
|
DaemonConfPath = `${shepherd.chipsDir}/chips.conf`; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
case 'coind': |
||||
|
DaemonConfPath = `${nativeCoindDir}/${shepherd.nativeCoindList[coind.toLowerCase()].bin.toLowerCase()}.conf`; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
break; |
||||
|
default: |
||||
|
DaemonConfPath = `${shepherd.komodoDir}/${flock}/${flock}.conf`; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
DaemonConfPath = shepherd.path.normalize(DaemonConfPath); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.log(DaemonConfPath); |
||||
|
shepherd.writeLog(`setconf ${DaemonConfPath}`); |
||||
|
|
||||
|
const CheckFileExists = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'Check Conf file exists is done'; |
||||
|
|
||||
|
const confFileExist = shepherd.fs.ensureFileSync(DaemonConfPath); |
||||
|
if (confFileExist) { |
||||
|
shepherd.log(result); |
||||
|
shepherd.writeLog(`setconf ${result}`); |
||||
|
|
||||
|
resolve(result); |
||||
|
} else { |
||||
|
shepherd.log('conf file doesnt exist'); |
||||
|
resolve('conf file doesnt exist'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const FixFilePermissions = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'Conf file permissions updated to Read/Write'; |
||||
|
|
||||
|
shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); |
||||
|
shepherd.log(result); |
||||
|
shepherd.writeLog(`setconf ${result}`); |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const RemoveLines = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'RemoveLines is done'; |
||||
|
|
||||
|
shepherd.fs.readFile(DaemonConfPath, 'utf8', (err, data) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`setconf error ${err}`); |
||||
|
return shepherd.log(err); |
||||
|
} |
||||
|
|
||||
|
const rmlines = data.replace(/(?:(?:\r\n|\r|\n)\s*){2}/gm, '\n'); |
||||
|
|
||||
|
shepherd.fs.writeFile(DaemonConfPath, rmlines, 'utf8', (err) => { |
||||
|
if (err) |
||||
|
return shepherd.log(err); |
||||
|
|
||||
|
shepherd.fsnode.chmodSync(DaemonConfPath, '0666'); |
||||
|
shepherd.writeLog(`setconf ${result}`); |
||||
|
shepherd.log(result); |
||||
|
resolve(result); |
||||
|
}); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const CheckConf = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'CheckConf is done'; |
||||
|
|
||||
|
shepherd.setconf.status(DaemonConfPath, (err, status) => { |
||||
|
const rpcuser = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'checking rpcuser...'; |
||||
|
|
||||
|
if (status[0].hasOwnProperty('rpcuser')) { |
||||
|
shepherd.log('rpcuser: OK'); |
||||
|
shepherd.writeLog('rpcuser: OK'); |
||||
|
} else { |
||||
|
const randomstring = shepherd.md5((Math.random() * Math.random() * 999).toString()); |
||||
|
|
||||
|
shepherd.log('rpcuser: NOT FOUND'); |
||||
|
shepherd.writeLog('rpcuser: NOT FOUND'); |
||||
|
|
||||
|
shepherd.fs.appendFile(DaemonConfPath, `\nrpcuser=user${randomstring.substring(0, 16)}`, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`append daemon conf err: ${err}`); |
||||
|
shepherd.log(`append daemon conf err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
shepherd.log('rpcuser: ADDED'); |
||||
|
shepherd.writeLog('rpcuser: ADDED'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const rpcpass = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'checking rpcpassword...'; |
||||
|
|
||||
|
if (status[0].hasOwnProperty('rpcpassword')) { |
||||
|
shepherd.log('rpcpassword: OK'); |
||||
|
shepherd.writeLog('rpcpassword: OK'); |
||||
|
} else { |
||||
|
const randomstring = shepherd.md5((Math.random() * Math.random() * 999).toString()); |
||||
|
|
||||
|
shepherd.log('rpcpassword: NOT FOUND'); |
||||
|
shepherd.writeLog('rpcpassword: NOT FOUND'); |
||||
|
|
||||
|
shepherd.fs.appendFile(DaemonConfPath, `\nrpcpassword=${randomstring}`, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`append daemon conf err: ${err}`); |
||||
|
shepherd.log(`append daemon conf err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
shepherd.log('rpcpassword: ADDED'); |
||||
|
shepherd.writeLog('rpcpassword: ADDED'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const rpcbind = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'checking rpcbind...'; |
||||
|
|
||||
|
if (status[0].hasOwnProperty('rpcbind')) { |
||||
|
shepherd.log('rpcbind: OK'); |
||||
|
shepherd.writeLog('rpcbind: OK'); |
||||
|
} else { |
||||
|
shepherd.log('rpcbind: NOT FOUND'); |
||||
|
shepherd.writeLog('rpcbind: NOT FOUND'); |
||||
|
|
||||
|
shepherd.fs.appendFile(DaemonConfPath, '\nrpcbind=127.0.0.1', (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`append daemon conf err: ${err}`); |
||||
|
shepherd.log(`append daemon conf err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
shepherd.log('rpcbind: ADDED'); |
||||
|
shepherd.writeLog('rpcbind: ADDED'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const server = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'checking server...'; |
||||
|
|
||||
|
if (status[0].hasOwnProperty('server')) { |
||||
|
shepherd.log('server: OK'); |
||||
|
shepherd.writeLog('server: OK'); |
||||
|
} else { |
||||
|
shepherd.log('server: NOT FOUND'); |
||||
|
shepherd.writeLog('server: NOT FOUND'); |
||||
|
|
||||
|
shepherd.fs.appendFile(DaemonConfPath, '\nserver=1', (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`append daemon conf err: ${err}`); |
||||
|
shepherd.log(`append daemon conf err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
shepherd.log('server: ADDED'); |
||||
|
shepherd.writeLog('server: ADDED'); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
const addnode = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const result = 'checking addnode...'; |
||||
|
|
||||
|
if (flock === 'chipsd' || |
||||
|
flock === 'komodod') { |
||||
|
if (status[0].hasOwnProperty('addnode')) { |
||||
|
shepherd.log('addnode: OK'); |
||||
|
shepherd.writeLog('addnode: OK'); |
||||
|
} else { |
||||
|
let nodesList; |
||||
|
|
||||
|
if (flock === 'chipsd') { |
||||
|
nodesList = '\naddnode=95.110.191.193' + |
||||
|
'\naddnode=144.76.167.66' + |
||||
|
'\naddnode=158.69.248.93' + |
||||
|
'\naddnode=149.202.49.218' + |
||||
|
'\naddnode=95.213.205.222' + |
||||
|
'\naddnode=5.9.253.198' + |
||||
|
'\naddnode=164.132.224.253' + |
||||
|
'\naddnode=163.172.4.66' + |
||||
|
'\naddnode=217.182.194.216' + |
||||
|
'\naddnode=94.130.96.114' + |
||||
|
'\naddnode=5.9.253.195'; |
||||
|
} else if (flock === 'komodod') { |
||||
|
nodesList = '\naddnode=78.47.196.146' + |
||||
|
'\naddnode=5.9.102.210' + |
||||
|
'\naddnode=178.63.69.164' + |
||||
|
'\naddnode=88.198.65.74' + |
||||
|
'\naddnode=5.9.122.241' + |
||||
|
'\naddnode=144.76.94.3'; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('addnode: NOT FOUND'); |
||||
|
shepherd.fs.appendFile(DaemonConfPath, nodesList, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`append daemon conf err: ${err}`); |
||||
|
shepherd.log(`append daemon conf err: ${err}`); |
||||
|
} |
||||
|
// throw err;
|
||||
|
shepherd.log('addnode: ADDED'); |
||||
|
shepherd.writeLog('addnode: ADDED'); |
||||
|
}); |
||||
|
} |
||||
|
} else { |
||||
|
result = 'skip addnode'; |
||||
|
} |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
rpcuser() |
||||
|
.then((result) => { |
||||
|
return rpcpass(); |
||||
|
}) |
||||
|
.then(server) |
||||
|
.then(rpcbind) |
||||
|
.then(addnode); |
||||
|
}); |
||||
|
|
||||
|
shepherd.log(result); |
||||
|
shepherd.writeLog(`checkconf addnode ${result}`); |
||||
|
|
||||
|
resolve(result); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
CheckFileExists() |
||||
|
.then((result) => { |
||||
|
return FixFilePermissions(); |
||||
|
}) |
||||
|
.then(RemoveLines) |
||||
|
.then(CheckConf); |
||||
|
} |
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: herd |
||||
|
*/ |
||||
|
shepherd.post('/herd', (req, res) => { |
||||
|
shepherd.log('======= req.body ======='); |
||||
|
shepherd.log(req.body); |
||||
|
|
||||
|
if (req.body.options && |
||||
|
!shepherd.kmdMainPassiveMode) { |
||||
|
const testCoindPort = (skipError) => { |
||||
|
if (!shepherd.lockDownAddCoin) { |
||||
|
const _port = shepherd.assetChainPorts[req.body.options.ac_name]; |
||||
|
|
||||
|
shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'open') { |
||||
|
if (!skipError) { |
||||
|
shepherd.log(`komodod service start error at port ${_port}, reason: port is closed`); |
||||
|
shepherd.writeLog(`komodod service start error at port ${_port}, reason: port is closed`); |
||||
|
shepherd.io.emit('service', { |
||||
|
komodod: { |
||||
|
error: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'error', |
||||
|
result: `error starting ${req.body.herd} ${req.body.options.ac_name} daemon. Port ${_port} is already taken!`, |
||||
|
}; |
||||
|
|
||||
|
res.status(500); |
||||
|
res.end(JSON.stringify(obj)); |
||||
|
} else { |
||||
|
shepherd.log(`komodod service start success at port ${_port}`); |
||||
|
shepherd.writeLog(`komodod service start success at port ${_port}`); |
||||
|
} |
||||
|
} else { |
||||
|
if (!skipError) { |
||||
|
herder(req.body.herd, req.body.options); |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'success', |
||||
|
result: 'result', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(obj)); |
||||
|
} else { |
||||
|
shepherd.log(`komodod service start error at port ${_port}, reason: unknown`); |
||||
|
shepherd.writeLog(`komodod service start error at port ${_port}, reason: unknown`); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (req.body.herd === 'komodod') { |
||||
|
// check if komodod instance is already running
|
||||
|
testCoindPort(); |
||||
|
setTimeout(() => { |
||||
|
testCoindPort(true); |
||||
|
}, 10000); |
||||
|
} else { |
||||
|
herder(req.body.herd, req.body.options, req.body.coind); |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'success', |
||||
|
result: 'result', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(obj)); |
||||
|
} |
||||
|
} else { |
||||
|
// (?)
|
||||
|
herder(req.body.herd, req.body.options); |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'success', |
||||
|
result: 'result', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(obj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
*/ |
||||
|
shepherd.post('/setconf', (req, res) => { |
||||
|
shepherd.log('======= req.body ======='); |
||||
|
shepherd.log(req.body); |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32' && |
||||
|
req.body.chain == 'komodod') { |
||||
|
setkomodoconf = spawn(shepherd.path.join(__dirname, '../assets/bin/win64/genkmdconf.bat')); |
||||
|
} else { |
||||
|
shepherd.setConf(req.body.chain); |
||||
|
} |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'success', |
||||
|
result: 'result', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(obj)); |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
*/ |
||||
|
shepherd.post('/getconf', (req, res) => { |
||||
|
shepherd.log('======= req.body ======='); |
||||
|
shepherd.log(req.body); |
||||
|
|
||||
|
const confpath = getConf(req.body.chain, req.body.coind); |
||||
|
|
||||
|
shepherd.log('got conf path is:'); |
||||
|
shepherd.log(confpath); |
||||
|
shepherd.writeLog('got conf path is:'); |
||||
|
shepherd.writeLog(confpath); |
||||
|
|
||||
|
const obj = { |
||||
|
msg: 'success', |
||||
|
result: confpath, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(obj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.setConfKMD = (isChips) => { |
||||
|
// check if kmd conf exists
|
||||
|
shepherd._fs.access(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.komodoDir}/komodo.conf`, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log(isChips ? 'creating chips conf' : 'creating komodo conf'); |
||||
|
shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); |
||||
|
setConf(isChips ? 'chipsd' : 'komodod'); |
||||
|
} else { |
||||
|
const _confSize = shepherd.fs.lstatSync(isChips ? `${shepherd.chipsDir}/chips.conf` : `${shepherd.komodoDir}/komodo.conf`); |
||||
|
|
||||
|
if (_confSize.size === 0) { |
||||
|
shepherd.log(isChips ? 'err: chips conf file is empty, creating chips conf' : 'err: komodo conf file is empty, creating komodo conf'); |
||||
|
shepherd.writeLog(isChips ? `creating chips conf in ${shepherd.chipsDir}/chips.conf` : `creating komodo conf in ${shepherd.komodoDir}/komodo.conf`); |
||||
|
setConf(isChips ? 'chipsd' : 'komodod'); |
||||
|
} else { |
||||
|
shepherd.writeLog(isChips ? 'chips conf exists' : 'komodo conf exists'); |
||||
|
shepherd.log(isChips ? 'chips conf exists' : 'komodo conf exists'); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,294 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* Combined native dashboard update same as in gui |
||||
|
* type: GET |
||||
|
* params: coin |
||||
|
*/ |
||||
|
shepherd.post('/native/dashboard/update', (req, res, next) => { |
||||
|
let _returnObj; |
||||
|
let _promiseStack; |
||||
|
const _coin = req.body.coin; |
||||
|
|
||||
|
if (_coin === 'CHIPS') { |
||||
|
_returnObj = { |
||||
|
getinfo: {}, |
||||
|
listtransactions: [], |
||||
|
getbalance: {}, |
||||
|
listunspent: {}, |
||||
|
addresses: {}, |
||||
|
}; |
||||
|
_promiseStack = [ |
||||
|
'getinfo', |
||||
|
'listtransactions', |
||||
|
'getbalance', |
||||
|
]; |
||||
|
} else { |
||||
|
_returnObj = { |
||||
|
getinfo: {}, |
||||
|
listtransactions: [], |
||||
|
z_gettotalbalance: {}, |
||||
|
z_getoperationstatus: {}, |
||||
|
listunspent: {}, |
||||
|
addresses: {}, |
||||
|
}; |
||||
|
_promiseStack = [ |
||||
|
'getinfo', |
||||
|
'listtransactions', |
||||
|
'z_gettotalbalance', |
||||
|
'z_getoperationstatus' |
||||
|
]; |
||||
|
} |
||||
|
|
||||
|
const getAddressesNative = (coin) => { |
||||
|
const type = [ |
||||
|
'public', |
||||
|
'private' |
||||
|
]; |
||||
|
|
||||
|
if (coin === 'CHIPS') { |
||||
|
type.pop(); |
||||
|
} |
||||
|
|
||||
|
shepherd.Promise.all(type.map((_type, index) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
_bitcoinRPC( |
||||
|
coin, |
||||
|
_type === 'public' ? 'getaddressesbyaccount' : 'z_listaddresses', |
||||
|
[''] |
||||
|
).then((_json) => { |
||||
|
if (_json === 'Work queue depth exceeded' || |
||||
|
!_json) { |
||||
|
resolve({ error: 'daemon is busy' }); |
||||
|
} else { |
||||
|
resolve(JSON.parse(_json).result); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(result => { |
||||
|
if (result[0] && |
||||
|
result[0].length) { |
||||
|
const calcBalance = (result, json) => { |
||||
|
if (json && |
||||
|
json.length) { |
||||
|
const allAddrArray = json.map(res => res.address).filter((x, i, a) => a.indexOf(x) == i); |
||||
|
|
||||
|
for (let a = 0; a < allAddrArray.length; a++) { |
||||
|
const filteredArray = json.filter(res => res.address === allAddrArray[a]).map(res => res.amount); |
||||
|
|
||||
|
let isNewAddr = true; |
||||
|
for (let x = 0; x < result.length && isNewAddr; x++) { |
||||
|
for (let y = 0; y < result[x].length && isNewAddr; y++) { |
||||
|
if (allAddrArray[a] === result[x][y]) { |
||||
|
isNewAddr = false; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (isNewAddr && |
||||
|
(allAddrArray[a].substring(0, 2) === 'zc' || |
||||
|
allAddrArray[a].substring(0, 2) === 'zt')) { |
||||
|
result[1][result[1].length] = allAddrArray[a]; |
||||
|
} else { |
||||
|
result[0][result[0].length] = allAddrArray[a]; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// remove addr duplicates
|
||||
|
if (result[0] && |
||||
|
result[0].length) { |
||||
|
result[0] = result[0].filter((elem, pos) => { |
||||
|
return result[0].indexOf(elem) === pos; |
||||
|
}); |
||||
|
} |
||||
|
if (result[1] && |
||||
|
result[1].length) { |
||||
|
result[1] = result[1].filter((elem, pos) => { |
||||
|
return result[1].indexOf(elem) === pos; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
let newAddressArray = []; |
||||
|
for (let a = 0; a < result.length; a++) { |
||||
|
newAddressArray[a] = []; |
||||
|
|
||||
|
if (result[a]) { |
||||
|
for (let b = 0; b < result[a].length; b++) { |
||||
|
let filteredArray; |
||||
|
|
||||
|
filteredArray = json.filter(res => res.address === result[a][b]).map(res => res.amount); |
||||
|
|
||||
|
let sum = 0; |
||||
|
for (let i = 0; i < filteredArray.length; i++) { |
||||
|
sum += filteredArray[i]; |
||||
|
} |
||||
|
|
||||
|
newAddressArray[a][b] = { |
||||
|
address: result[a][b], |
||||
|
amount: sum, |
||||
|
type: a === 0 ? 'public': 'private', |
||||
|
}; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// get zaddr balance
|
||||
|
if (result[1] && |
||||
|
result[1].length) { |
||||
|
shepherd.Promise.all(result[1].map((_address, index) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
_bitcoinRPC(coin, 'z_getbalance', [_address]) |
||||
|
.then((__json) => { |
||||
|
__json = JSON.parse(__json); |
||||
|
if (__json && |
||||
|
__json.error) { |
||||
|
resolve(0); |
||||
|
} else { |
||||
|
resolve(__json.result) |
||||
|
newAddressArray[1][index] = { |
||||
|
address: _address, |
||||
|
amount: __json.result, |
||||
|
type: 'private', |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(zresult => { |
||||
|
_returnObj.addresses = { |
||||
|
public: newAddressArray[0], |
||||
|
private: newAddressArray[1], |
||||
|
}; |
||||
|
|
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: _returnObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
_returnObj.addresses = { |
||||
|
public: newAddressArray[0], |
||||
|
private: newAddressArray[1], |
||||
|
}; |
||||
|
|
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: _returnObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
_bitcoinRPC(coin, 'listunspent') |
||||
|
.then((__json) => { |
||||
|
if (__json === 'Work queue depth exceeded' || |
||||
|
!__json) { |
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: _returnObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
} else { |
||||
|
_returnObj.listunspent = JSON.parse(__json); |
||||
|
|
||||
|
calcBalance( |
||||
|
result, |
||||
|
JSON.parse(__json).result |
||||
|
); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
_returnObj.addresses = { |
||||
|
public: {}, |
||||
|
private: {}, |
||||
|
}; |
||||
|
|
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: _returnObj, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
|
||||
|
const _bitcoinRPC = (coin, cmd, params) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
let _payload; |
||||
|
|
||||
|
if (params) { |
||||
|
_payload = { |
||||
|
mode: null, |
||||
|
chain: coin, |
||||
|
cmd: cmd, |
||||
|
params: params, |
||||
|
}; |
||||
|
} else { |
||||
|
_payload = { |
||||
|
mode: null, |
||||
|
chain: coin, |
||||
|
cmd: cmd, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
const options = { |
||||
|
url: `http://127.0.0.1:${shepherd.appConfig.agamaPort}/shepherd/cli`, |
||||
|
method: 'POST', |
||||
|
headers: { |
||||
|
'Content-Type': 'application/json', |
||||
|
}, |
||||
|
body: JSON.stringify({ payload: _payload }), |
||||
|
timeout: 120000, |
||||
|
}; |
||||
|
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
resolve(body); |
||||
|
} else { |
||||
|
resolve(body); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.Promise.all(_promiseStack.map((_call, index) => { |
||||
|
let _params; |
||||
|
if (_call === 'listtransactions') { |
||||
|
_params = [ |
||||
|
'', |
||||
|
300, |
||||
|
0 |
||||
|
]; |
||||
|
} |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
_bitcoinRPC( |
||||
|
_coin, |
||||
|
_call, |
||||
|
_params |
||||
|
) |
||||
|
.then((json) => { |
||||
|
if (json === 'Work queue depth exceeded' || |
||||
|
!json) { |
||||
|
_returnObj[_call] = { error: 'daemon is busy' }; |
||||
|
} else { |
||||
|
_returnObj[_call] = JSON.parse(json); |
||||
|
} |
||||
|
resolve(json); |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(result => { |
||||
|
getAddressesNative(_coin); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,92 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: GET |
||||
|
* params: herd, lastLines |
||||
|
*/ |
||||
|
shepherd.post('/debuglog', (req, res) => { |
||||
|
let _herd = req.body.herdname; |
||||
|
let _ac = req.body.ac; |
||||
|
let _lastNLines = req.body.lastLines; |
||||
|
let _location; |
||||
|
|
||||
|
if (shepherd.os.platform() === 'darwin') { |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.os.platform() === 'linux') { |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.os.platform() === 'win32') { |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`; |
||||
|
shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); |
||||
|
} |
||||
|
|
||||
|
if (_herd === 'komodo') { |
||||
|
_location = shepherd.komodoDir; |
||||
|
} |
||||
|
|
||||
|
if (_ac) { |
||||
|
_location = `${shepherd.komodoDir}/${_ac}`; |
||||
|
|
||||
|
if (_ac === 'CHIPS') { |
||||
|
_location = shepherd.chipsDir; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.readDebugLog(`${_location}/debug.log`, _lastNLines) |
||||
|
.then((result) => { |
||||
|
const _obj = { |
||||
|
msg: 'success', |
||||
|
result: result, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(_obj)); |
||||
|
}, (result) => { |
||||
|
const _obj = { |
||||
|
msg: 'error', |
||||
|
result: result, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(_obj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
|
||||
|
shepherd.readDebugLog = (fileLocation, lastNLines) => { |
||||
|
return new shepherd.Promise( |
||||
|
(resolve, reject) => { |
||||
|
if (lastNLines) { |
||||
|
try { |
||||
|
shepherd._fs.access(fileLocation, shepherd.fs.constants.R_OK, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log(`error reading ${fileLocation}`); |
||||
|
shepherd.writeLog(`error reading ${fileLocation}`); |
||||
|
reject(`readDebugLog error: ${err}`); |
||||
|
} else { |
||||
|
shepherd.log(`reading ${fileLocation}`); |
||||
|
shepherd._fs.readFile(fileLocation, 'utf-8', (err, data) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog(`readDebugLog err: ${err}`); |
||||
|
shepherd.log(`readDebugLog err: ${err}`); |
||||
|
} |
||||
|
|
||||
|
const lines = data.trim().split('\n'); |
||||
|
const lastLine = lines.slice(lines.length - lastNLines, lines.length).join('\n'); |
||||
|
|
||||
|
resolve(lastLine); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} catch (e) { |
||||
|
reject(`readDebugLog error: ${e}`); |
||||
|
} |
||||
|
} else { |
||||
|
reject('readDebugLog error: lastNLines param is not provided!'); |
||||
|
} |
||||
|
} |
||||
|
); |
||||
|
}; |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,44 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* list native coind |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
shepherd.get('/coind/list', (req, res, next) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: shepherd.nativeCoindList, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.scanNativeCoindBins = () => { |
||||
|
let nativeCoindList = {}; |
||||
|
|
||||
|
// check if coind bins are present in agama
|
||||
|
for (let key in shepherd.nativeCoind) { |
||||
|
nativeCoindList[key] = { |
||||
|
name: shepherd.nativeCoind[key].name, |
||||
|
port: shepherd.nativeCoind[key].port, |
||||
|
bin: shepherd.nativeCoind[key].bin, |
||||
|
bins: { |
||||
|
daemon: false, |
||||
|
cli: false, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}d${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { |
||||
|
nativeCoindList[key].bins.daemon = true; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.fs.existsSync(`${shepherd.coindRootDir}/${key}/${shepherd.nativeCoind[key].bin}-cli${shepherd.os.platform() === 'win32' ? '.exe' : ''}`)) { |
||||
|
nativeCoindList[key].bins.cli = true; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return nativeCoindList; |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,165 @@ |
|||||
|
const remoteBinLocation = { |
||||
|
win32: 'https://artifacts.supernet.org/latest/windows/', |
||||
|
darwin: 'https://artifacts.supernet.org/latest/osx/', |
||||
|
linux: 'https://artifacts.supernet.org/latest/linux/', |
||||
|
}; |
||||
|
const localBinLocation = { |
||||
|
win32: 'assets/bin/win64/', |
||||
|
darwin: 'assets/bin/osx/', |
||||
|
linux: 'assets/bin/linux64/', |
||||
|
}; |
||||
|
const latestBins = { |
||||
|
win32: [ |
||||
|
'komodo-cli.exe', |
||||
|
'komodod.exe', |
||||
|
'libcrypto-1_1.dll', |
||||
|
'libcurl-4.dll', |
||||
|
'libcurl.dll', |
||||
|
'libgcc_s_sjlj-1.dll', |
||||
|
'libnanomsg.dll', |
||||
|
'libssl-1_1.dll', |
||||
|
'libwinpthread-1.dll', |
||||
|
'nanomsg.dll', |
||||
|
'pthreadvc2.dll', |
||||
|
], |
||||
|
darwin: [ |
||||
|
'komodo-cli', |
||||
|
'komodod', |
||||
|
'libgcc_s.1.dylib', |
||||
|
'libgomp.1.dylib', |
||||
|
'libnanomsg.5.0.0.dylib', |
||||
|
'libstdc++.6.dylib', // encode %2B
|
||||
|
], |
||||
|
linux: [ |
||||
|
'komodo-cli', |
||||
|
'komodod', |
||||
|
], |
||||
|
}; |
||||
|
|
||||
|
let binsToUpdate = []; |
||||
|
|
||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* Check bins file size |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
// TODO: promises
|
||||
|
shepherd.get('/update/bins/check', (req, res, next) => { |
||||
|
const rootLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'bins', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
|
||||
|
const _os = shepherd.os.platform(); |
||||
|
shepherd.log(`checking bins: ${_os}`); |
||||
|
|
||||
|
shepherd.io.emit('patch', { |
||||
|
patch: { |
||||
|
type: 'bins-check', |
||||
|
status: 'progress', |
||||
|
message: `checking bins: ${_os}`, |
||||
|
}, |
||||
|
}); |
||||
|
// get list of bins/dlls that can be updated to the latest
|
||||
|
for (let i = 0; i < latestBins[_os].length; i++) { |
||||
|
shepherd.remoteFileSize(remoteBinLocation[_os] + latestBins[_os][i], (err, remoteBinSize) => { |
||||
|
const localBinSize = shepherd.fs.statSync(rootLocation + localBinLocation[_os] + latestBins[_os][i]).size; |
||||
|
|
||||
|
shepherd.log('remote url: ' + (remoteBinLocation[_os] + latestBins[_os][i]) + ' (' + remoteBinSize + ')'); |
||||
|
shepherd.log('local file: ' + (rootLocation + localBinLocation[_os] + latestBins[_os][i]) + ' (' + localBinSize + ')'); |
||||
|
|
||||
|
if (remoteBinSize !== localBinSize) { |
||||
|
shepherd.log(`${latestBins[_os][i]} can be updated`); |
||||
|
binsToUpdate.push({ |
||||
|
name: latestBins[_os][i], |
||||
|
rSize: remoteBinSize, |
||||
|
lSize: localBinSize, |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
if (i === latestBins[_os].length - 1) { |
||||
|
shepherd.io.emit('patch', { |
||||
|
patch: { |
||||
|
type: 'bins-check', |
||||
|
status: 'done', |
||||
|
fileList: binsToUpdate, |
||||
|
}, |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* Update bins |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
shepherd.get('/update/bins', (req, res, next) => { |
||||
|
const rootLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
const _os = shepherd.os.platform(); |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
filesCount: binsToUpdate.length, |
||||
|
list: binsToUpdate, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
|
||||
|
for (let i = 0; i < binsToUpdate.length; i++) { |
||||
|
shepherd.downloadFile({ |
||||
|
remoteFile: remoteBinLocation[_os] + binsToUpdate[i].name, |
||||
|
localFile: `${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`, |
||||
|
onProgress: (received, total) => { |
||||
|
const percentage = (received * 100) / total; |
||||
|
|
||||
|
if (percentage.toString().indexOf('.10') > -1) { |
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
type: 'bins-update', |
||||
|
status: 'progress', |
||||
|
file: binsToUpdate[i].name, |
||||
|
bytesTotal: total, |
||||
|
bytesReceived: received, |
||||
|
}, |
||||
|
}); |
||||
|
// shepherd.log(`${binsToUpdate[i].name} ${percentage}% | ${received} bytes out of ${total} bytes.`);
|
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
.then(() => { |
||||
|
// verify that remote file is matching to DL'ed file
|
||||
|
const localBinSize = shepherd.fs.statSync(`${rootLocation}${localBinLocation[_os]}patch/${binsToUpdate[i].name}`).size; |
||||
|
shepherd.log('compare dl file size'); |
||||
|
|
||||
|
if (localBinSize === binsToUpdate[i].rSize) { |
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
type: 'bins-update', |
||||
|
file: binsToUpdate[i].name, |
||||
|
status: 'done', |
||||
|
}, |
||||
|
}); |
||||
|
shepherd.log(`file ${binsToUpdate[i].name} succesfully downloaded`); |
||||
|
} else { |
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
type: 'bins-update', |
||||
|
file: binsToUpdate[i].name, |
||||
|
message: 'size mismatch', |
||||
|
}, |
||||
|
}); |
||||
|
shepherd.log(`error: ${binsToUpdate[i].name} file size doesnt match remote!`); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,154 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* DL app patch |
||||
|
* type: GET |
||||
|
* params: patchList |
||||
|
*/ |
||||
|
shepherd.get('/update/patch', (req, res, next) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'dl started' |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
|
||||
|
shepherd.updateAgama(); |
||||
|
}); |
||||
|
|
||||
|
shepherd.updateAgama = () => { |
||||
|
const rootLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
|
||||
|
shepherd.downloadFile({ |
||||
|
remoteFile: 'https://github.com/pbca26/dl-test/raw/master/patch.zip', |
||||
|
localFile: `${rootLocation}patch.zip`, |
||||
|
onProgress: (received, total) => { |
||||
|
const percentage = (received * 100) / total; |
||||
|
|
||||
|
if (percentage.toString().indexOf('.10') > -1) { |
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
status: 'progress', |
||||
|
type: 'ui', |
||||
|
progress: percentage, |
||||
|
bytesTotal: total, |
||||
|
bytesReceived: received, |
||||
|
}, |
||||
|
}); |
||||
|
//shepherd.log(`patch ${percentage}% | ${received} bytes out of ${total} bytes.`);
|
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
.then(() => { |
||||
|
shepherd.remoteFileSize('https://github.com/pbca26/dl-test/raw/master/patch.zip', (err, remotePatchSize) => { |
||||
|
// verify that remote file is matching to DL'ed file
|
||||
|
const localPatchSize = shepherd.fs.statSync(`${rootLocation}patch.zip`).size; |
||||
|
shepherd.log('compare dl file size'); |
||||
|
|
||||
|
if (localPatchSize === remotePatchSize) { |
||||
|
const zip = new shepherd.AdmZip(`${rootLocation}patch.zip`); |
||||
|
|
||||
|
shepherd.log('patch succesfully downloaded'); |
||||
|
shepherd.log('extracting contents'); |
||||
|
|
||||
|
if (shepherd.appConfig.dev) { |
||||
|
if (!shepherd.fs.existsSync(`${rootLocation}/patch`)) { |
||||
|
shepherd.fs.mkdirSync(`${rootLocation}/patch`); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
zip.extractAllTo(/*target path*/rootLocation + (shepherd.appConfig.dev ? '/patch' : ''), /*overwrite*/true); |
||||
|
// TODO: extract files in chunks
|
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
type: 'ui', |
||||
|
status: 'done', |
||||
|
}, |
||||
|
}); |
||||
|
shepherd.fs.unlinkSync(`${rootLocation}patch.zip`); |
||||
|
} else { |
||||
|
shepherd.io.emit('patch', { |
||||
|
msg: { |
||||
|
type: 'ui', |
||||
|
status: 'error', |
||||
|
message: 'size mismatch', |
||||
|
}, |
||||
|
}); |
||||
|
shepherd.log('patch file size doesnt match remote!'); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* check latest version |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
shepherd.get('/update/patch/check', (req, res, next) => { |
||||
|
const rootLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
const options = { |
||||
|
url: 'https://github.com/pbca26/dl-test/raw/master/version', |
||||
|
method: 'GET', |
||||
|
}; |
||||
|
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
const remoteVersion = body.split('\n'); |
||||
|
const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); |
||||
|
let localVersion; |
||||
|
|
||||
|
if (localVersionFile.indexOf('\r\n') > -1) { |
||||
|
localVersion = localVersionFile.split('\r\n'); |
||||
|
} else { |
||||
|
localVersion = localVersionFile.split('\n'); |
||||
|
} |
||||
|
|
||||
|
if (remoteVersion[0] === localVersion[0]) { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'latest', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'update', |
||||
|
version: { |
||||
|
local: localVersion[0], |
||||
|
remote: remoteVersion[0], |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} else { |
||||
|
res.end({ |
||||
|
err: 'error getting update', |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* unpack zip |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
shepherd.get('/unpack', (req, res, next) => { |
||||
|
const dlLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
const zip = new shepherd.AdmZip(`${dlLocation}patch.zip`); |
||||
|
zip.extractAllTo(/*target path*/ `${dlLocation}/patch/unpack`, /*overwrite*/true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'unpack started', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,49 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/** |
||||
|
* Promise based download file method |
||||
|
*/ |
||||
|
shepherd.downloadFile = (configuration) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
// Save variable to know progress
|
||||
|
let receivedBytes = 0; |
||||
|
let totalBytes = 0; |
||||
|
|
||||
|
let req = shepherd.request({ |
||||
|
method: 'GET', |
||||
|
uri: configuration.remoteFile, |
||||
|
agentOptions: { |
||||
|
keepAlive: true, |
||||
|
keepAliveMsecs: 15000, |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
let out = shepherd.fs.createWriteStream(configuration.localFile); |
||||
|
req.pipe(out); |
||||
|
|
||||
|
req.on('response', (data) => { |
||||
|
// Change the total bytes value to get progress later.
|
||||
|
totalBytes = parseInt(data.headers['content-length']); |
||||
|
}); |
||||
|
|
||||
|
// Get progress if callback exists
|
||||
|
if (configuration.hasOwnProperty('onProgress')) { |
||||
|
req.on('data', (chunk) => { |
||||
|
// Update the received bytes
|
||||
|
receivedBytes += chunk.length; |
||||
|
configuration.onProgress(receivedBytes, totalBytes); |
||||
|
}); |
||||
|
} else { |
||||
|
req.on('data', (chunk) => { |
||||
|
// Update the received bytes
|
||||
|
receivedBytes += chunk.length; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
req.on('end', () => { |
||||
|
resolve(); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,127 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.zcashParamsDownloadLinks = { |
||||
|
'agama.komodoplatform.com': { |
||||
|
proving: 'https://agama.komodoplatform.com/file/supernet/sprout-proving.key', |
||||
|
verifying: 'https://agama.komodoplatform.com/file/supernet/sprout-verifying.key', |
||||
|
}, |
||||
|
'agama-yq0ysrdtr.stackpathdns.com': { |
||||
|
proving: 'http://agama-yq0ysrdtr.stackpathdns.com/file/supernet/sprout-proving.key', |
||||
|
verifying: 'http://agama-yq0ysrdtr.stackpathdns.com/file/supernet/sprout-verifying.key', |
||||
|
}, |
||||
|
'zcash.dl.mercerweiss.com': { |
||||
|
proving: 'https://zcash.dl.mercerweiss.com/sprout-proving.key', |
||||
|
verifying: 'https://zcash.dl.mercerweiss.com/sprout-verifying.key', |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
shepherd.zcashParamsExist = () => { |
||||
|
let _checkList = { |
||||
|
rootDir: shepherd._fs.existsSync(shepherd.zcashParamsDir), |
||||
|
provingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-proving.key`), |
||||
|
provingKeySize: false, |
||||
|
verifyingKey: shepherd._fs.existsSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`), |
||||
|
verifyingKeySize: false, |
||||
|
errors: false, |
||||
|
}; |
||||
|
|
||||
|
if (_checkList.rootDir && |
||||
|
_checkList.provingKey || |
||||
|
_checkList.verifyingKey) { |
||||
|
// verify each key size
|
||||
|
const _provingKeySize = _checkList.provingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-proving.key`) : 0; |
||||
|
const _verifyingKeySize = _checkList.verifyingKey ? shepherd.fs.lstatSync(`${shepherd.zcashParamsDir}/sprout-verifying.key`) : 0; |
||||
|
|
||||
|
if (Number(_provingKeySize.size) === 910173851) { // bytes
|
||||
|
_checkList.provingKeySize = true; |
||||
|
} |
||||
|
if (Number(_verifyingKeySize.size) === 1449) { |
||||
|
_checkList.verifyingKeySize = true; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('zcashparams exist'); |
||||
|
} else { |
||||
|
shepherd.log('zcashparams doesnt exist'); |
||||
|
} |
||||
|
|
||||
|
if (!_checkList.rootDir || |
||||
|
!_checkList.provingKey || |
||||
|
!_checkList.verifyingKey || |
||||
|
!_checkList.provingKeySize || |
||||
|
!_checkList.verifyingKeySize) { |
||||
|
_checkList.errors = true; |
||||
|
} |
||||
|
|
||||
|
return _checkList; |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* Update bins |
||||
|
* type: |
||||
|
* params: |
||||
|
*/ |
||||
|
shepherd.get('/zcparamsdl', (req, res, next) => { |
||||
|
// const dlLocation = shepherd.zcashParamsDir + '/test';
|
||||
|
const dlLocation = shepherd.zcashParamsDir; |
||||
|
const dlOption = req.query.dloption; |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'zcash params dl started', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
|
||||
|
for (let key in shepherd.zcashParamsDownloadLinks[dlOption]) { |
||||
|
shepherd.downloadFile({ |
||||
|
remoteFile: shepherd.zcashParamsDownloadLinks[dlOption][key], |
||||
|
localFile: `${dlLocation}/sprout-${key}.key`, |
||||
|
onProgress: (received, total) => { |
||||
|
const percentage = (received * 100) / total; |
||||
|
|
||||
|
if (percentage.toString().indexOf('.10') > -1) { |
||||
|
shepherd.io.emit('zcparams', { |
||||
|
msg: { |
||||
|
type: 'zcpdownload', |
||||
|
status: 'progress', |
||||
|
file: key, |
||||
|
bytesTotal: total, |
||||
|
bytesReceived: received, |
||||
|
progress: percentage, |
||||
|
}, |
||||
|
}); |
||||
|
// shepherd.log(`${key} ${percentage}% | ${received} bytes out of ${total} bytes.`);
|
||||
|
} |
||||
|
} |
||||
|
}) |
||||
|
.then(() => { |
||||
|
const checkZcashParams = shepherd.zcashParamsExist(); |
||||
|
|
||||
|
shepherd.log(`${key} dl done`); |
||||
|
|
||||
|
if (checkZcashParams.error) { |
||||
|
shepherd.io.emit('zcparams', { |
||||
|
msg: { |
||||
|
type: 'zcpdownload', |
||||
|
file: key, |
||||
|
status: 'error', |
||||
|
message: 'size mismatch', |
||||
|
progress: 100, |
||||
|
}, |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.io.emit('zcparams', { |
||||
|
msg: { |
||||
|
type: 'zcpdownload', |
||||
|
file: key, |
||||
|
progress: 100, |
||||
|
status: 'done', |
||||
|
}, |
||||
|
}); |
||||
|
shepherd.log(`file ${key} succesfully downloaded`); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,38 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.get('/electrum/login', (req, res, next) => { |
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
const _abbr = shepherd.electrumServers[key].abbr; |
||||
|
const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); |
||||
|
|
||||
|
shepherd.electrumKeys[_abbr] = { |
||||
|
priv, |
||||
|
pub, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
shepherd.electrumCoins.auth = true; |
||||
|
|
||||
|
shepherd.log(JSON.stringify(shepherd.electrumKeys, null, '\t'), true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'true', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/dev/logout', (req, res, next) => { |
||||
|
shepherd.electrumCoins.auth = false; |
||||
|
shepherd.electrumKeys = {}; |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'true', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,145 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.get('/electrum/getbalance', (req, res, next) => { |
||||
|
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
shepherd.log('electrum getbalance =>', true); |
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainAddressGetBalance(req.query.address) |
||||
|
.then((json) => { |
||||
|
if (json && |
||||
|
json.hasOwnProperty('confirmed') && |
||||
|
json.hasOwnProperty('unconfirmed')) { |
||||
|
if (network === 'komodo') { |
||||
|
ecl.connect(); |
||||
|
ecl.blockchainAddressListunspent(req.query.address) |
||||
|
.then((utxoList) => { |
||||
|
if (utxoList && |
||||
|
utxoList.length) { |
||||
|
// filter out < 10 KMD amounts
|
||||
|
let _utxo = []; |
||||
|
|
||||
|
for (let i = 0; i < utxoList.length; i++) { |
||||
|
shepherd.log(`utxo ${utxoList[i]['tx_hash']} sats ${utxoList[i].value} value ${Number(utxoList[i].value) * 0.00000001}`, true); |
||||
|
|
||||
|
if (Number(utxoList[i].value) * 0.00000001 >= 10) { |
||||
|
_utxo.push(utxoList[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.log('filtered utxo list =>', true); |
||||
|
shepherd.log(_utxo, true); |
||||
|
|
||||
|
if (_utxo && |
||||
|
_utxo.length) { |
||||
|
let interestTotal = 0; |
||||
|
|
||||
|
shepherd.Promise.all(_utxo.map((_utxoItem, index) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
ecl.blockchainTransactionGet(_utxoItem['tx_hash']) |
||||
|
.then((_rawtxJSON) => { |
||||
|
shepherd.log('electrum gettransaction ==>', true); |
||||
|
shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); |
||||
|
shepherd.log(_rawtxJSON, true); |
||||
|
|
||||
|
// decode tx
|
||||
|
const _network = shepherd.getNetworkData(network); |
||||
|
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); |
||||
|
|
||||
|
if (decodedTx && |
||||
|
decodedTx.format && |
||||
|
decodedTx.format.locktime > 0) { |
||||
|
interestTotal += shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value); |
||||
|
} |
||||
|
|
||||
|
shepherd.log('decoded tx =>', true); |
||||
|
shepherd.log(decodedTx, true); |
||||
|
|
||||
|
resolve(true); |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(promiseResult => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
balance: Number((0.00000001 * json.confirmed).toFixed(8)), |
||||
|
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), |
||||
|
unconfirmedSats: json.unconfirmed, |
||||
|
balanceSats: json.confirmed, |
||||
|
interest: Number(interestTotal.toFixed(8)), |
||||
|
interestSats: Math.floor(interestTotal * 100000000), |
||||
|
total: interestTotal > 0 ? Number((0.00000001 * json.confirmed + interestTotal).toFixed(8)) : 0, |
||||
|
totalSats: interestTotal > 0 ?json.confirmed + Math.floor(interestTotal * 100000000) : 0, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
balance: Number((0.00000001 * json.confirmed).toFixed(8)), |
||||
|
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), |
||||
|
unconfirmedSats: json.unconfirmed, |
||||
|
balanceSats: json.confirmed, |
||||
|
interest: 0, |
||||
|
interestSats: 0, |
||||
|
total: 0, |
||||
|
totalSats: 0, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
balance: Number((0.00000001 * json.confirmed).toFixed(8)), |
||||
|
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), |
||||
|
unconfirmedSats: json.unconfirmed, |
||||
|
balanceSats: json.confirmed, |
||||
|
interest: 0, |
||||
|
interestSats: 0, |
||||
|
total: 0, |
||||
|
totalSats: 0, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum getbalance ==>', true); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
balance: Number((0.00000001 * json.confirmed).toFixed(8)), |
||||
|
unconfirmed: Number((0.00000001 * json.unconfirmed).toFixed(8)), |
||||
|
unconfirmedSats: json.unconfirmed, |
||||
|
balanceSats: json.confirmed, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,59 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.get('/electrum/getblockinfo', (req, res, next) => { |
||||
|
shepherd.electrumGetBlockInfo(req.query.height, req.query.network) |
||||
|
.then((json) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
shepherd.electrumGetBlockInfo = (height, network) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainBlockGetHeader(height) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum getblockinfo ==>', true); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
resolve(json); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/getcurrentblock', (req, res, next) => { |
||||
|
shepherd.electrumGetCurrentBlock(req.query.network) |
||||
|
.then((json) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
shepherd.electrumGetCurrentBlock = (network) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainNumblocksSubscribe() |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum currentblock ==>', true); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
resolve(json); |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,69 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.findCoinName = (network) => { |
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
if (key === network) { |
||||
|
return shepherd.electrumServers[key].abbr; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.addElectrumCoin = (coin) => { |
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
if (shepherd.electrumServers[key].abbr === coin) { |
||||
|
shepherd.electrumCoins[coin] = { |
||||
|
name: key, |
||||
|
abbr: coin, |
||||
|
server: { |
||||
|
ip: shepherd.electrumServers[key].address, |
||||
|
port: shepherd.electrumServers[key].port, |
||||
|
}, |
||||
|
serverList: shepherd.electrumServers[key].serverList ? shepherd.electrumServers[key].serverList : 'none', |
||||
|
txfee: 'calculated' /*shepherd.electrumServers[key].txfee*/, |
||||
|
}; |
||||
|
|
||||
|
return true; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/coins/remove', (req, res, next) => { |
||||
|
delete shepherd.electrumCoins[req.query.coin]; |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/coins/add', (req, res, next) => { |
||||
|
const result = shepherd.addElectrumCoin(req.query.coin); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/coins', (req, res, next) => { |
||||
|
let _electrumCoins = JSON.parse(JSON.stringify(shepherd.electrumCoins)); // deep cloning
|
||||
|
|
||||
|
for (let key in _electrumCoins) { |
||||
|
if (shepherd.electrumKeys[key]) { |
||||
|
_electrumCoins[key].pub = shepherd.electrumKeys[key].pub; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: _electrumCoins, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,411 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
// single sig
|
||||
|
shepherd.buildSignedTx = (sendTo, changeAddress, wif, network, utxo, changeValue, spendValue) => { |
||||
|
let key = shepherd.bitcoinJS.ECPair.fromWIF(wif, shepherd.getNetworkData(network)); |
||||
|
let tx = new shepherd.bitcoinJS.TransactionBuilder(shepherd.getNetworkData(network)); |
||||
|
|
||||
|
shepherd.log('buildSignedTx'); |
||||
|
// console.log(`buildSignedTx priv key ${wif}`);
|
||||
|
shepherd.log(`buildSignedTx pub key ${key.getAddress().toString()}`, true); |
||||
|
// console.log('buildSignedTx std tx fee ' + shepherd.electrumServers[network].txfee);
|
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
tx.addInput(utxo[i].txid, utxo[i].vout); |
||||
|
} |
||||
|
|
||||
|
tx.addOutput(sendTo, Number(spendValue)); |
||||
|
|
||||
|
if (changeValue > 0) { |
||||
|
tx.addOutput(changeAddress, Number(changeValue)); |
||||
|
} |
||||
|
|
||||
|
if (network === 'komodo' || |
||||
|
network === 'KMD') { |
||||
|
const _locktime = Math.floor(Date.now() / 1000) - 777; |
||||
|
tx.setLockTime(_locktime); |
||||
|
shepherd.log(`kmd tx locktime set to ${_locktime}`, true); |
||||
|
} |
||||
|
|
||||
|
shepherd.log('buildSignedTx unsigned tx data vin', true); |
||||
|
shepherd.log(tx.tx.ins, true); |
||||
|
shepherd.log('buildSignedTx unsigned tx data vout', true); |
||||
|
shepherd.log(tx.tx.outs, true); |
||||
|
shepherd.log('buildSignedTx unsigned tx data', true); |
||||
|
shepherd.log(tx, true); |
||||
|
|
||||
|
for (let i = 0; i < utxo.length; i++) { |
||||
|
tx.sign(i, key); |
||||
|
} |
||||
|
|
||||
|
const rawtx = tx.build().toHex(); |
||||
|
|
||||
|
shepherd.log('buildSignedTx signed tx hex', true); |
||||
|
shepherd.log(rawtx, true); |
||||
|
|
||||
|
return rawtx; |
||||
|
} |
||||
|
|
||||
|
shepherd.maxSpendBalance = (utxoList, fee) => { |
||||
|
let maxSpendBalance = 0; |
||||
|
|
||||
|
for (let i = 0; i < utxoList.length; i++) { |
||||
|
maxSpendBalance += Number(utxoList[i].value); |
||||
|
} |
||||
|
|
||||
|
if (fee) { |
||||
|
return Number(maxSpendBalance) - Number(fee); |
||||
|
} else { |
||||
|
return maxSpendBalance; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/createrawtx', (req, res, next) => { |
||||
|
// txid 64 char
|
||||
|
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
const outputAddress = req.query.address; |
||||
|
const changeAddress = req.query.change; |
||||
|
const value = Number(req.query.value); |
||||
|
const push = req.query.push; |
||||
|
const fee = shepherd.electrumServers[network].txfee; |
||||
|
let wif = req.query.wif; |
||||
|
|
||||
|
if (req.query.gui) { |
||||
|
wif = shepherd.electrumKeys[req.query.coin].priv; |
||||
|
} |
||||
|
|
||||
|
shepherd.log('electrum createrawtx =>', true); |
||||
|
|
||||
|
ecl.connect(); |
||||
|
shepherd.listunspent(ecl, changeAddress, network, true, true) |
||||
|
.then((utxoList) => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
if (utxoList && |
||||
|
utxoList.length) { |
||||
|
let utxoListFormatted = []; |
||||
|
let totalInterest = 0; |
||||
|
let totalInterestUTXOCount = 0; |
||||
|
let interestClaimThreshold = 200; |
||||
|
let utxoVerified = true; |
||||
|
|
||||
|
for (let i = 0; i < utxoList.length; i++) { |
||||
|
if (network === 'komodo') { |
||||
|
utxoListFormatted.push({ |
||||
|
txid: utxoList[i].txid, |
||||
|
vout: utxoList[i].vout, |
||||
|
value: Number(utxoList[i].amountSats), |
||||
|
interestSats: Number(utxoList[i].interestSats), |
||||
|
verified: utxoList[i].verified ? utxoList[i].verified : false, |
||||
|
}); |
||||
|
} else { |
||||
|
utxoListFormatted.push({ |
||||
|
txid: utxoList[i].txid, |
||||
|
vout: utxoList[i].vout, |
||||
|
value: Number(utxoList[i].amountSats), |
||||
|
verified: utxoList[i].verified ? utxoList[i].verified : false, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.log('electrum listunspent unformatted ==>', true); |
||||
|
shepherd.log(utxoList, true); |
||||
|
|
||||
|
shepherd.log('electrum listunspent formatted ==>', true); |
||||
|
shepherd.log(utxoListFormatted, true); |
||||
|
|
||||
|
const _maxSpendBalance = Number(shepherd.maxSpendBalance(utxoListFormatted)); |
||||
|
let targets = [{ |
||||
|
address: outputAddress, |
||||
|
value: value > _maxSpendBalance ? _maxSpendBalance : value, |
||||
|
}]; |
||||
|
shepherd.log('targets =>', true); |
||||
|
shepherd.log(targets, true); |
||||
|
|
||||
|
const feeRate = 20; // sats/byte
|
||||
|
|
||||
|
// default coin selection algo blackjack with fallback to accumulative
|
||||
|
// make a first run, calc approx tx fee
|
||||
|
// if ins and outs are empty reduce max spend by txfee
|
||||
|
let { inputs, outputs, fee } = shepherd.coinSelect(utxoListFormatted, targets, feeRate); |
||||
|
|
||||
|
shepherd.log('coinselect res =>', true); |
||||
|
shepherd.log('coinselect inputs =>', true); |
||||
|
shepherd.log(inputs, true); |
||||
|
shepherd.log('coinselect outputs =>', true); |
||||
|
shepherd.log(outputs, true); |
||||
|
shepherd.log('coinselect calculated fee =>', true); |
||||
|
shepherd.log(fee, true); |
||||
|
|
||||
|
if (!inputs && |
||||
|
!outputs) { |
||||
|
targets[0].value = targets[0].value - shepherd.electrumServers[network].txfee; |
||||
|
shepherd.log('second run', true); |
||||
|
shepherd.log('coinselect adjusted targets =>', true); |
||||
|
shepherd.log(targets, true); |
||||
|
|
||||
|
const secondRun = shepherd.coinSelect(utxoListFormatted, targets, feeRate); |
||||
|
inputs = secondRun.inputs; |
||||
|
outputs = secondRun.outputs; |
||||
|
fee = secondRun.fee; |
||||
|
|
||||
|
shepherd.log('coinselect inputs =>', true); |
||||
|
shepherd.log(inputs, true); |
||||
|
shepherd.log('coinselect outputs =>', true); |
||||
|
shepherd.log(outputs, true); |
||||
|
shepherd.log('coinselect fee =>', true); |
||||
|
shepherd.log(fee, true); |
||||
|
} |
||||
|
|
||||
|
let _change = 0; |
||||
|
|
||||
|
if (outputs && |
||||
|
outputs.length === 2) { |
||||
|
_change = outputs[1].value; |
||||
|
} |
||||
|
|
||||
|
// check if any outputs are unverified
|
||||
|
if (inputs && |
||||
|
inputs.length) { |
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
if (!inputs[i].verified) { |
||||
|
utxoVerified = false; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
if (Number(inputs[i].interestSats) > interestClaimThreshold) { |
||||
|
totalInterest += Number(inputs[i].interestSats); |
||||
|
totalInterestUTXOCount++; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const _maxSpend = shepherd.maxSpendBalance(utxoListFormatted); |
||||
|
|
||||
|
if (value > _maxSpend) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: `Spend value is too large. Max available amount is ${Number((_maxSpend * 0.00000001.toFixed(8)))}`, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
shepherd.log(`maxspend ${_maxSpend} (${_maxSpend * 0.00000001})`, true); |
||||
|
shepherd.log(`value ${value}`, true); |
||||
|
shepherd.log(`sendto ${outputAddress} amount ${value} (${value * 0.00000001})`, true); |
||||
|
shepherd.log(`changeto ${changeAddress} amount ${_change} (${_change * 0.00000001})`, true); |
||||
|
|
||||
|
// account for KMD interest
|
||||
|
if (network === 'komodo' && |
||||
|
totalInterest > 0) { |
||||
|
// account for extra vout
|
||||
|
const _feeOverhead = outputs.length === 1 ? shepherd.estimateTxSize(0, 1) * feeRate : 0; |
||||
|
|
||||
|
shepherd.log(`max interest to claim ${totalInterest} (${totalInterest * 0.00000001})`, true); |
||||
|
shepherd.log(`estimated fee overhead ${_feeOverhead}`, true); |
||||
|
shepherd.log(`current change amount ${_change} (${_change * 0.00000001}), boosted change amount ${_change + (totalInterest - _feeOverhead)} (${(_change + (totalInterest - _feeOverhead)) * 0.00000001})`, true); |
||||
|
|
||||
|
_change = _change + (totalInterest - _feeOverhead); |
||||
|
} |
||||
|
|
||||
|
if (!inputs && |
||||
|
!outputs) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Can\'t find best fit utxo. Try lower amount.', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
let vinSum = 0; |
||||
|
|
||||
|
for (let i = 0; i < inputs.length; i++) { |
||||
|
vinSum += inputs[i].value; |
||||
|
} |
||||
|
|
||||
|
const _estimatedFee = vinSum - outputs[0].value - _change; |
||||
|
|
||||
|
shepherd.log(`vin sum ${vinSum} (${vinSum * 0.00000001})`, true); |
||||
|
shepherd.log(`estimatedFee ${_estimatedFee} (${_estimatedFee * 0.00000001})`, true); |
||||
|
|
||||
|
const _rawtx = shepherd.buildSignedTx( |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
wif, |
||||
|
network, |
||||
|
inputs, |
||||
|
_change, |
||||
|
value |
||||
|
); |
||||
|
|
||||
|
if (!push || |
||||
|
push === 'false') { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
// wif,
|
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionBroadcast(_rawtx) |
||||
|
.then((txid) => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
if (txid && |
||||
|
txid.indexOf('bad-txns-inputs-spent') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
if (txid && |
||||
|
txid.length === 64) { |
||||
|
if (txid.indexOf('bad-txns-in-belowout') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
fee, |
||||
|
// wif,
|
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} else { |
||||
|
if (txid && |
||||
|
txid.indexOf('bad-txns-in-belowout') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Bad transaction inputs spent', |
||||
|
raw: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'Can\'t broadcast transaction', |
||||
|
raw: { |
||||
|
utxoSet: inputs, |
||||
|
change: _change, |
||||
|
fee, |
||||
|
value, |
||||
|
outputAddress, |
||||
|
changeAddress, |
||||
|
network, |
||||
|
rawtx: _rawtx, |
||||
|
txid, |
||||
|
utxoVerified, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: utxoList, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/pushtx', (req, res, next) => { |
||||
|
const rawtx = req.query.rawtx; |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionBroadcast(rawtx) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum pushtx ==>', true); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,26 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.get('/electrum/estimatefee', (req, res, next) => { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainEstimatefee(req.query.blocks) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum estimatefee ==>', true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
shepherd.estimateTxSize = (numVins, numOuts) => { |
||||
|
// in x 180 + out x 34 + 10 plus or minus in
|
||||
|
return numVins * 180 + numOuts * 34 + 11; |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,37 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.kmdCalcInterest = (locktime, value) => { // value in sats
|
||||
|
const timestampDiff = Math.floor(Date.now() / 1000) - locktime - 777; |
||||
|
const hoursPassed = Math.floor(timestampDiff / 3600); |
||||
|
const minutesPassed = Math.floor((timestampDiff - (hoursPassed * 3600)) / 60); |
||||
|
const secondsPassed = timestampDiff - (hoursPassed * 3600) - (minutesPassed * 60); |
||||
|
let timestampDiffMinutes = timestampDiff / 60; |
||||
|
let interest = 0; |
||||
|
|
||||
|
shepherd.log('kmdCalcInterest', true); |
||||
|
shepherd.log(`locktime ${locktime}`, true); |
||||
|
shepherd.log(`minutes converted ${timestampDiffMinutes}`, true); |
||||
|
shepherd.log(`passed ${hoursPassed}h ${minutesPassed}m ${secondsPassed}s`, true); |
||||
|
|
||||
|
// calc interest
|
||||
|
if (timestampDiffMinutes >= 60) { |
||||
|
if (timestampDiffMinutes > 365 * 24 * 60) { |
||||
|
timestampDiffMinutes = 365 * 24 * 60; |
||||
|
} |
||||
|
timestampDiffMinutes -= 59; |
||||
|
|
||||
|
shepherd.log(`minutes if statement ${timestampDiffMinutes}`, true); |
||||
|
|
||||
|
// TODO: check if interest is > 5% yr
|
||||
|
// calc ytd and 5% for 1 yr
|
||||
|
// const hoursInOneYear = 365 * 24;
|
||||
|
// const hoursDiff = hoursInOneYear - hoursPassed;
|
||||
|
|
||||
|
interest = ((Number(value) * 0.00000001) / 10512000) * timestampDiffMinutes; |
||||
|
shepherd.log(`interest ${interest}`, true); |
||||
|
} |
||||
|
|
||||
|
return interest; |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,128 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.seedToWif = (seed, network, iguana) => { |
||||
|
const bytes = shepherd.sha256(seed, { asBytes: true }); |
||||
|
|
||||
|
if (iguana) { |
||||
|
bytes[0] &= 248; |
||||
|
bytes[31] &= 127; |
||||
|
bytes[31] |= 64; |
||||
|
} |
||||
|
|
||||
|
const toHexString = (byteArray) => { |
||||
|
return Array.from(byteArray, (byte) => { |
||||
|
return ('0' + (byte & 0xFF).toString(16)).slice(-2); |
||||
|
}).join(''); |
||||
|
} |
||||
|
|
||||
|
const hex = toHexString(bytes); |
||||
|
|
||||
|
const key = new shepherd.CoinKey(new Buffer(hex, 'hex'), { |
||||
|
private: shepherd.getNetworkData(network).wif, |
||||
|
public: shepherd.getNetworkData(network).pubKeyHash, |
||||
|
}); |
||||
|
|
||||
|
key.compressed = true; |
||||
|
|
||||
|
shepherd.log(`seedtowif priv key ${key.privateWif}`, true); |
||||
|
shepherd.log(`seedtowif pub key ${key.publicAddress}`, true); |
||||
|
|
||||
|
return { |
||||
|
priv: key.privateWif, |
||||
|
pub: key.publicAddress, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/seedtowif', (req, res, next) => { |
||||
|
const keys = shepherd.seedToWif(req.query.seed, req.query.network, req.query.iguana); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
keys, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/keys', (req, res, next) => { |
||||
|
let _matchingKeyPairs = 0; |
||||
|
let _electrumKeys = {}; |
||||
|
|
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
const _abbr = shepherd.electrumServers[key].abbr; |
||||
|
const { priv, pub } = shepherd.seedToWif(req.query.seed, shepherd.findNetworkObj(_abbr), req.query.iguana); |
||||
|
|
||||
|
if (shepherd.electrumKeys[_abbr].pub === pub && |
||||
|
shepherd.electrumKeys[_abbr].priv === priv) { |
||||
|
_matchingKeyPairs++; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (req.query.active) { |
||||
|
_electrumKeys = JSON.parse(JSON.stringify(shepherd.electrumKeys)); |
||||
|
|
||||
|
for (let key in _electrumKeys) { |
||||
|
if (!shepherd.electrumCoins[key]) { |
||||
|
delete _electrumKeys[key]; |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
_electrumKeys = shepherd.electrumKeys; |
||||
|
} |
||||
|
|
||||
|
shepherd.log(JSON.stringify(_electrumKeys, null, '\t'), true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: _matchingKeyPairs === Object.keys(shepherd.electrumKeys).length ? _electrumKeys : false, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
// spv v2
|
||||
|
/*shepherd.get('/electrum/bip39/seed', (req, res, next) => { |
||||
|
// TODO
|
||||
|
const bip39 = require('bip39'); // npm i -S bip39
|
||||
|
const crypto = require('crypto'); |
||||
|
|
||||
|
// what you describe as 'seed'
|
||||
|
const randomBytes = crypto.randomBytes(16); // 128 bits is enough
|
||||
|
|
||||
|
// your 12 word phrase
|
||||
|
const mnemonic = bip39.entropyToMnemonic(randomBytes.toString('hex')); |
||||
|
|
||||
|
// what is accurately described as the wallet seed
|
||||
|
// var seed = bip39.mnemonicToSeed(mnemonic) // you'll use this in #3 below
|
||||
|
const seed = bip39.mnemonicToSeed(req.query.seed); |
||||
|
|
||||
|
console.log(seed); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
servers: shepherd.electrumServers, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
|
||||
|
console.log(shepherd.bitcoinJS.networks.komodo); |
||||
|
const hdMaster = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo); // seed from above
|
||||
|
|
||||
|
const key1 = hdMaster.derivePath('m/0'); |
||||
|
const key2 = hdMaster.derivePath('m/1'); |
||||
|
console.log(hdMaster); |
||||
|
|
||||
|
console.log(key1.keyPair.toWIF()); |
||||
|
console.log(key1.keyPair.getAddress()); |
||||
|
console.log(key2.keyPair.toWIF()); |
||||
|
|
||||
|
const hdnode = shepherd.bitcoinJS.HDNode.fromSeedBuffer(seed, shepherd.electrumJSNetworks.komodo).deriveHardened(0).derive(0).derive(1); |
||||
|
console.log(`address: ${hdnode.getAddress()}`); |
||||
|
console.log(`priv (WIF): ${hdnode.keyPair.toWIF()}`); |
||||
|
});*/ |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,194 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.listunspent = (ecl, address, network, full, verify) => { |
||||
|
let _atLeastOneDecodeTxFailed = false; |
||||
|
|
||||
|
if (full) { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
ecl.connect(); |
||||
|
ecl.blockchainAddressListunspent(address) |
||||
|
.then((_utxoJSON) => { |
||||
|
if (_utxoJSON && |
||||
|
_utxoJSON.length) { |
||||
|
let formattedUtxoList = []; |
||||
|
let _utxo = []; |
||||
|
|
||||
|
ecl.blockchainNumblocksSubscribe() |
||||
|
.then((currentHeight) => { |
||||
|
if (currentHeight && |
||||
|
Number(currentHeight) > 0) { |
||||
|
// filter out unconfirmed utxos
|
||||
|
for (let i = 0; i < _utxoJSON.length; i++) { |
||||
|
if (Number(currentHeight) - Number(_utxoJSON[i].height) !== 0) { |
||||
|
_utxo.push(_utxoJSON[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!_utxo.length) { // no confirmed utxo
|
||||
|
resolve('no valid utxo'); |
||||
|
} else { |
||||
|
shepherd.Promise.all(_utxo.map((_utxoItem, index) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
ecl.blockchainTransactionGet(_utxoItem['tx_hash']) |
||||
|
.then((_rawtxJSON) => { |
||||
|
shepherd.log('electrum gettransaction ==>', true); |
||||
|
shepherd.log(index + ' | ' + (_rawtxJSON.length - 1), true); |
||||
|
shepherd.log(_rawtxJSON, true); |
||||
|
|
||||
|
// decode tx
|
||||
|
const _network = shepherd.getNetworkData(network); |
||||
|
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); |
||||
|
|
||||
|
shepherd.log('decoded tx =>', true); |
||||
|
shepherd.log(decodedTx, true); |
||||
|
|
||||
|
if (!decodedTx) { |
||||
|
_atLeastOneDecodeTxFailed = true; |
||||
|
resolve('cant decode tx'); |
||||
|
} else { |
||||
|
if (network === 'komodo') { |
||||
|
let interest = 0; |
||||
|
|
||||
|
if (Number(_utxoItem.value) * 0.00000001 >= 10 && |
||||
|
decodedTx.format.locktime > 0) { |
||||
|
interest = shepherd.kmdCalcInterest(decodedTx.format.locktime, _utxoItem.value); |
||||
|
} |
||||
|
|
||||
|
let _resolveObj = { |
||||
|
txid: _utxoItem['tx_hash'], |
||||
|
vout: _utxoItem['tx_pos'], |
||||
|
address, |
||||
|
amount: Number(_utxoItem.value) * 0.00000001, |
||||
|
amountSats: _utxoItem.value, |
||||
|
interest: interest, |
||||
|
interestSats: Math.floor(interest * 100000000), |
||||
|
confirmations: currentHeight - _utxoItem.height, |
||||
|
spendable: true, |
||||
|
verified: false, |
||||
|
}; |
||||
|
|
||||
|
// merkle root verification agains another electrum server
|
||||
|
if (verify) { |
||||
|
shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) |
||||
|
.then((verifyMerkleRes) => { |
||||
|
if (verifyMerkleRes && verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) { |
||||
|
verifyMerkleRes = false; |
||||
|
} |
||||
|
|
||||
|
_resolveObj.verified = verifyMerkleRes; |
||||
|
resolve(_resolveObj); |
||||
|
}); |
||||
|
} else { |
||||
|
resolve(_resolveObj); |
||||
|
} |
||||
|
} else { |
||||
|
let _resolveObj = { |
||||
|
txid: _utxoItem['tx_hash'], |
||||
|
vout: _utxoItem['tx_pos'], |
||||
|
address, |
||||
|
amount: Number(_utxoItem.value) * 0.00000001, |
||||
|
amountSats: _utxoItem.value, |
||||
|
confirmations: currentHeight - _utxoItem.height, |
||||
|
spendable: true, |
||||
|
verified: false, |
||||
|
}; |
||||
|
|
||||
|
// merkle root verification agains another electrum server
|
||||
|
if (verify) { |
||||
|
shepherd.verifyMerkleByCoin(shepherd.findCoinName(network), _utxoItem['tx_hash'], _utxoItem.height) |
||||
|
.then((verifyMerkleRes) => { |
||||
|
if (verifyMerkleRes && |
||||
|
verifyMerkleRes === shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA) { |
||||
|
verifyMerkleRes = false; |
||||
|
} |
||||
|
|
||||
|
_resolveObj.verified = verifyMerkleRes; |
||||
|
resolve(_resolveObj); |
||||
|
}); |
||||
|
} else { |
||||
|
resolve(_resolveObj); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(promiseResult => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
if (!_atLeastOneDecodeTxFailed) { |
||||
|
shepherd.log(promiseResult, true); |
||||
|
resolve(promiseResult); |
||||
|
} else { |
||||
|
shepherd.log('listunspent error, cant decode tx(s)', true); |
||||
|
resolve('decode error'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} else { |
||||
|
resolve('cant get current height'); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
ecl.close(); |
||||
|
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} else { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
ecl.connect(); |
||||
|
ecl.blockchainAddressListunspent(address) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
if (json && |
||||
|
json.length) { |
||||
|
resolve(json); |
||||
|
} else { |
||||
|
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/listunspent', (req, res, next) => { |
||||
|
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
if (req.query.full && |
||||
|
req.query.full === 'true') { |
||||
|
shepherd.listunspent( |
||||
|
ecl, |
||||
|
req.query.address, |
||||
|
network, |
||||
|
true, |
||||
|
req.query.verify |
||||
|
).then((listunspent) => { |
||||
|
shepherd.log('electrum listunspent ==>', true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: listunspent, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.listunspent(ecl, req.query.address, network) |
||||
|
.then((listunspent) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('electrum listunspent ==>', true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: listunspent, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,145 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
// get merkle root
|
||||
|
shepherd.getMerkleRoot = (txid, proof, pos) => { |
||||
|
const reverse = require('buffer-reverse'); |
||||
|
let hash = txid; |
||||
|
let serialized; |
||||
|
const _sha256 = (data) => { |
||||
|
return shepherd.crypto.createHash('sha256').update(data).digest(); |
||||
|
} |
||||
|
|
||||
|
shepherd.log(`getMerkleRoot txid ${txid}`, true); |
||||
|
shepherd.log(`getMerkleRoot pos ${pos}`, true); |
||||
|
shepherd.log('getMerkleRoot proof', true); |
||||
|
shepherd.log(`getMerkleRoot ${proof}`, true); |
||||
|
|
||||
|
for (i = 0; i < proof.length; i++) { |
||||
|
const _hashBuff = new Buffer(hash, 'hex'); |
||||
|
const _proofBuff = new Buffer(proof[i], 'hex'); |
||||
|
|
||||
|
if ((pos & 1) == 0) { |
||||
|
serialized = Buffer.concat([reverse(_hashBuff), reverse(_proofBuff)]); |
||||
|
} else { |
||||
|
serialized = Buffer.concat([reverse(_proofBuff), reverse(_hashBuff)]); |
||||
|
} |
||||
|
|
||||
|
hash = reverse(_sha256(_sha256(serialized))).toString('hex'); |
||||
|
pos /= 2; |
||||
|
} |
||||
|
|
||||
|
return hash; |
||||
|
} |
||||
|
|
||||
|
shepherd.verifyMerkle = (txid, height, serverList, mainServer) => { |
||||
|
// select random server
|
||||
|
const getRandomIntInclusive = (min, max) => { |
||||
|
min = Math.ceil(min); |
||||
|
max = Math.floor(max); |
||||
|
|
||||
|
return Math.floor(Math.random() * (max - min + 1)) + min; // the maximum is inclusive and the minimum is inclusive
|
||||
|
} |
||||
|
|
||||
|
const _rnd = getRandomIntInclusive(0, serverList.length - 1); |
||||
|
const randomServer = serverList[_rnd]; |
||||
|
const _randomServer = randomServer.split(':'); |
||||
|
const _mainServer = mainServer.split(':'); |
||||
|
|
||||
|
let ecl = new shepherd.electrumJSCore(_mainServer[1], _mainServer[0], 'tcp'); // tcp or tls
|
||||
|
|
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
shepherd.log(`main server: ${mainServer}`, true); |
||||
|
shepherd.log(`verification server: ${randomServer}`, true); |
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionGetMerkle(txid, height) |
||||
|
.then((merkleData) => { |
||||
|
if (merkleData && |
||||
|
merkleData.merkle && |
||||
|
merkleData.pos) { |
||||
|
shepherd.log('electrum getmerkle =>', true); |
||||
|
shepherd.log(merkleData, true); |
||||
|
ecl.close(); |
||||
|
|
||||
|
const _res = shepherd.getMerkleRoot(txid, merkleData.merkle, merkleData.pos); |
||||
|
shepherd.log(_res, true); |
||||
|
|
||||
|
ecl = new shepherd.electrumJSCore(_randomServer[1], _randomServer[0], 'tcp'); |
||||
|
ecl.connect(); |
||||
|
|
||||
|
ecl.blockchainBlockGetHeader(height) |
||||
|
.then((blockInfo) => { |
||||
|
if (blockInfo && |
||||
|
blockInfo['merkle_root']) { |
||||
|
ecl.close(); |
||||
|
shepherd.log('blockinfo =>', true); |
||||
|
shepherd.log(blockInfo, true); |
||||
|
shepherd.log(blockInfo['merkle_root'], true); |
||||
|
|
||||
|
if (blockInfo && |
||||
|
blockInfo['merkle_root']) { |
||||
|
if (_res === blockInfo['merkle_root']) { |
||||
|
resolve(true); |
||||
|
} else { |
||||
|
resolve(false); |
||||
|
} |
||||
|
} else { |
||||
|
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); |
||||
|
} |
||||
|
} else { |
||||
|
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
resolve(shepherd.CONNECTION_ERROR_OR_INCOMPLETE_DATA); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.verifyMerkleByCoin = (coin, txid, height) => { |
||||
|
const _serverList = shepherd.electrumCoins[coin].serverList; |
||||
|
|
||||
|
shepherd.log(`verifyMerkleByCoin`, true); |
||||
|
shepherd.log(shepherd.electrumCoins[coin].server, true); |
||||
|
shepherd.log(shepherd.electrumCoins[coin].serverList, true); |
||||
|
|
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
if (_serverList !== 'none') { |
||||
|
let _filteredServerList = []; |
||||
|
|
||||
|
for (let i = 0; i < _serverList.length; i++) { |
||||
|
if (_serverList[i] !== shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port) { |
||||
|
_filteredServerList.push(_serverList[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.verifyMerkle( |
||||
|
txid, |
||||
|
height, |
||||
|
_filteredServerList, |
||||
|
shepherd.electrumCoins[coin].server.ip + ':' + shepherd.electrumCoins[coin].server.port |
||||
|
).then((proof) => { |
||||
|
resolve(proof); |
||||
|
}); |
||||
|
} else { |
||||
|
resolve(false); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/merkle/verify', (req, res, next) => { |
||||
|
shepherd.verifyMerkleByCoin(req.query.coin, req.query.txid, req.query.height) |
||||
|
.then((verifyMerkleRes) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
merkleProof: verifyMerkleRes, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,141 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.getNetworkData = (network) => { |
||||
|
const coin = shepherd.findNetworkObj(network) || shepherd.findNetworkObj(network.toUpperCase()) || shepherd.findNetworkObj(network.toLowerCase()); |
||||
|
const coinUC = coin ? coin.toUpperCase() : null; |
||||
|
|
||||
|
if (coin === 'SUPERNET' || |
||||
|
coin === 'REVS' || |
||||
|
coin === 'SUPERNET' || |
||||
|
coin === 'PANGEA' || |
||||
|
coin === 'DEX' || |
||||
|
coin === 'JUMBLR' || |
||||
|
coin === 'BET' || |
||||
|
coin === 'CRYPTO' || |
||||
|
coin === 'COQUI' || |
||||
|
coin === 'HODL' || |
||||
|
coin === 'SHARK' || |
||||
|
coin === 'BOTS' || |
||||
|
coin === 'MGW' || |
||||
|
coin === 'MVP' || |
||||
|
coin === 'KV' || |
||||
|
coin === 'CEAL' || |
||||
|
coin === 'MESH' || |
||||
|
coin === 'WLC' || |
||||
|
coin === 'MNZ' || |
||||
|
coinUC === 'SUPERNET' || |
||||
|
coinUC === 'REVS' || |
||||
|
coinUC === 'SUPERNET' || |
||||
|
coinUC === 'PANGEA' || |
||||
|
coinUC === 'DEX' || |
||||
|
coinUC === 'JUMBLR' || |
||||
|
coinUC === 'BET' || |
||||
|
coinUC === 'CRYPTO' || |
||||
|
coinUC === 'COQUI' || |
||||
|
coinUC === 'HODL' || |
||||
|
coinUC === 'SHARK' || |
||||
|
coinUC === 'BOTS' || |
||||
|
coinUC === 'MGW' || |
||||
|
coinUC === 'MVP' || |
||||
|
coinUC === 'KV' || |
||||
|
coinUC === 'CEAL' || |
||||
|
coinUC === 'MESH' || |
||||
|
coinUC === 'WLC' || |
||||
|
coinUC === 'MNZ') { |
||||
|
return shepherd.electrumJSNetworks.komodo; |
||||
|
} else { |
||||
|
return shepherd.electrumJSNetworks[network]; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.findNetworkObj = (coin) => { |
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
if (shepherd.electrumServers[key].abbr === coin) { |
||||
|
return key; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/servers', (req, res, next) => { |
||||
|
if (req.query.abbr) { |
||||
|
let _electrumServers = {}; |
||||
|
|
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
_electrumServers[shepherd.electrumServers[key].abbr] = shepherd.electrumServers[key]; |
||||
|
} |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
servers: _electrumServers, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
servers: shepherd.electrumServers, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/coins/server/set', (req, res, next) => { |
||||
|
shepherd.electrumCoins[req.query.coin].server = { |
||||
|
ip: req.query.address, |
||||
|
port: req.query.port, |
||||
|
}; |
||||
|
|
||||
|
for (let key in shepherd.electrumServers) { |
||||
|
if (shepherd.electrumServers[key].abbr === req.query.coin) { // a bit risky
|
||||
|
shepherd.electrumServers[key].address = req.query.address; |
||||
|
shepherd.electrumServers[key].port = req.query.port; |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.log(JSON.stringify(shepherd.electrumCoins[req.query.coin], null, '\t'), true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: true, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/servers/test', (req, res, next) => { |
||||
|
const ecl = new shepherd.electrumJSCore(req.query.port, req.query.address, 'tcp'); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.serverVersion() |
||||
|
.then((serverData) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log('serverData', true); |
||||
|
shepherd.log(serverData, true); |
||||
|
|
||||
|
if (serverData && |
||||
|
typeof serverData === 'string' && |
||||
|
serverData.indexOf('Electrum') > -1) { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: true, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: false, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,397 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.sortTransactions = (transactions) => { |
||||
|
return transactions.sort((b, a) => { |
||||
|
if (a.height < b.height) { |
||||
|
return -1; |
||||
|
} |
||||
|
|
||||
|
if (a.height > b.height) { |
||||
|
return 1; |
||||
|
} |
||||
|
|
||||
|
return 0; |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/listtransactions', (req, res, next) => { |
||||
|
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
shepherd.log('electrum listtransactions ==>', true); |
||||
|
|
||||
|
if (!req.query.full) { |
||||
|
ecl.connect(); |
||||
|
ecl.blockchainAddressGetHistory(req.query.address) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
json = shepherd.sortTransactions(json); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
// !expensive call!
|
||||
|
// TODO: limit e.g. 1-10, 10-20 etc
|
||||
|
const MAX_TX = req.query.maxlength || 10; |
||||
|
ecl.connect(); |
||||
|
|
||||
|
ecl.blockchainNumblocksSubscribe() |
||||
|
.then((currentHeight) => { |
||||
|
if (currentHeight && |
||||
|
Number(currentHeight) > 0) { |
||||
|
ecl.blockchainAddressGetHistory(req.query.address) |
||||
|
.then((json) => { |
||||
|
if (json && |
||||
|
json.length) { |
||||
|
json = shepherd.sortTransactions(json); |
||||
|
json = json.slice(0, MAX_TX); |
||||
|
let _rawtx = []; |
||||
|
|
||||
|
shepherd.log(json.length, true); |
||||
|
|
||||
|
shepherd.Promise.all(json.map((transaction, index) => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
ecl.blockchainBlockGetHeader(transaction.height) |
||||
|
.then((blockInfo) => { |
||||
|
if (blockInfo && |
||||
|
blockInfo.timestamp) { |
||||
|
ecl.blockchainTransactionGet(transaction['tx_hash']) |
||||
|
.then((_rawtxJSON) => { |
||||
|
shepherd.log('electrum gettransaction ==>', true); |
||||
|
shepherd.log((index + ' | ' + (_rawtxJSON.length - 1)), true); |
||||
|
shepherd.log(_rawtxJSON, true); |
||||
|
|
||||
|
// decode tx
|
||||
|
const _network = shepherd.getNetworkData(network); |
||||
|
const decodedTx = shepherd.electrumJSTxDecoder(_rawtxJSON, _network); |
||||
|
|
||||
|
let txInputs = []; |
||||
|
|
||||
|
shepherd.log('decodedtx =>', true); |
||||
|
shepherd.log(decodedTx.outputs, true); |
||||
|
|
||||
|
if (decodedTx && |
||||
|
decodedTx.inputs) { |
||||
|
shepherd.Promise.all(decodedTx.inputs.map((_decodedInput, index) => { |
||||
|
return new shepherd.Promise((_resolve, _reject) => { |
||||
|
if (_decodedInput.txid !== '0000000000000000000000000000000000000000000000000000000000000000') { |
||||
|
ecl.blockchainTransactionGet(_decodedInput.txid) |
||||
|
.then((rawInput) => { |
||||
|
const decodedVinVout = shepherd.electrumJSTxDecoder(rawInput, _network); |
||||
|
|
||||
|
shepherd.log('electrum raw input tx ==>', true); |
||||
|
|
||||
|
if (decodedVinVout) { |
||||
|
shepherd.log(decodedVinVout.outputs[_decodedInput.n], true); |
||||
|
txInputs.push(decodedVinVout.outputs[_decodedInput.n]); |
||||
|
_resolve(true); |
||||
|
} else { |
||||
|
_resolve(true); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
_resolve(true); |
||||
|
} |
||||
|
}); |
||||
|
})) |
||||
|
.then(promiseResult => { |
||||
|
const _parsedTx = { |
||||
|
network: decodedTx.network, |
||||
|
format: decodedTx.format, |
||||
|
inputs: txInputs, |
||||
|
outputs: decodedTx.outputs, |
||||
|
height: transaction.height, |
||||
|
timestamp: blockInfo.timestamp, |
||||
|
confirmations: currentHeight - transaction.height, |
||||
|
}; |
||||
|
|
||||
|
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); |
||||
|
|
||||
|
if (formattedTx.type) { |
||||
|
formattedTx.height = transaction.height; |
||||
|
formattedTx.blocktime = blockInfo.timestamp; |
||||
|
formattedTx.timereceived = blockInfo.timereceived; |
||||
|
formattedTx.hex = _rawtxJSON; |
||||
|
formattedTx.inputs = decodedTx.inputs; |
||||
|
formattedTx.outputs = decodedTx.outputs; |
||||
|
formattedTx.locktime = decodedTx.format.locktime; |
||||
|
_rawtx.push(formattedTx); |
||||
|
} else { |
||||
|
formattedTx[0].height = transaction.height; |
||||
|
formattedTx[0].blocktime = blockInfo.timestamp; |
||||
|
formattedTx[0].timereceived = blockInfo.timereceived; |
||||
|
formattedTx[0].hex = _rawtxJSON; |
||||
|
formattedTx[0].inputs = decodedTx.inputs; |
||||
|
formattedTx[0].outputs = decodedTx.outputs; |
||||
|
formattedTx[0].locktime = decodedTx.format.locktime; |
||||
|
formattedTx[1].height = transaction.height; |
||||
|
formattedTx[1].blocktime = blockInfo.timestamp; |
||||
|
formattedTx[1].timereceived = blockInfo.timereceived; |
||||
|
formattedTx[1].hex = _rawtxJSON; |
||||
|
formattedTx[1].inputs = decodedTx.inputs; |
||||
|
formattedTx[1].outputs = decodedTx.outputs; |
||||
|
formattedTx[1].locktime = decodedTx.format.locktime; |
||||
|
_rawtx.push(formattedTx[0]); |
||||
|
_rawtx.push(formattedTx[1]); |
||||
|
} |
||||
|
resolve(true); |
||||
|
}); |
||||
|
} else { |
||||
|
const _parsedTx = { |
||||
|
network: decodedTx.network, |
||||
|
format: 'cant parse', |
||||
|
inputs: 'cant parse', |
||||
|
outputs: 'cant parse', |
||||
|
height: transaction.height, |
||||
|
timestamp: blockInfo.timestamp, |
||||
|
confirmations: currentHeight - transaction.height, |
||||
|
}; |
||||
|
|
||||
|
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); |
||||
|
_rawtx.push(formattedTx); |
||||
|
resolve(true); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const _parsedTx = { |
||||
|
network: 'cant parse', |
||||
|
format: 'cant parse', |
||||
|
inputs: 'cant parse', |
||||
|
outputs: 'cant parse', |
||||
|
height: transaction.height, |
||||
|
timestamp: 'cant get block info', |
||||
|
confirmations: currentHeight - transaction.height, |
||||
|
}; |
||||
|
const formattedTx = shepherd.parseTransactionAddresses(_parsedTx, req.query.address, network); |
||||
|
_rawtx.push(formattedTx); |
||||
|
resolve(true); |
||||
|
} |
||||
|
}); |
||||
|
}); |
||||
|
})) |
||||
|
.then(promiseResult => { |
||||
|
ecl.close(); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: _rawtx, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: [], |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'error', |
||||
|
result: 'cant get current height', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/electrum/gettransaction', (req, res, next) => { |
||||
|
const network = req.query.network || shepherd.findNetworkObj(req.query.coin); |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[network].port, shepherd.electrumServers[network].address, shepherd.electrumServers[network].proto); // tcp or tls
|
||||
|
|
||||
|
shepherd.log('electrum gettransaction =>', true); |
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionGet(req.query.txid) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: json, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
shepherd.parseTransactionAddresses = (tx, targetAddress, network) => { |
||||
|
// TODO: - sum vins / sum vouts to the same address
|
||||
|
// - multi vin multi vout
|
||||
|
// - detect change address
|
||||
|
let result = []; |
||||
|
let _parse = { |
||||
|
inputs: {}, |
||||
|
outputs: {}, |
||||
|
}; |
||||
|
let _sum = { |
||||
|
inputs: 0, |
||||
|
outputs: 0, |
||||
|
}; |
||||
|
let _total = { |
||||
|
inputs: 0, |
||||
|
outputs: 0, |
||||
|
}; |
||||
|
|
||||
|
shepherd.log('parseTransactionAddresses result ==>', true); |
||||
|
|
||||
|
if (tx.format === 'cant parse') { |
||||
|
return { |
||||
|
type: 'unknown', |
||||
|
amount: 'unknown', |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let key in _parse) { |
||||
|
if (!tx[key].length) { |
||||
|
_parse[key] = []; |
||||
|
_parse[key].push(tx[key]); |
||||
|
} else { |
||||
|
_parse[key] = tx[key]; |
||||
|
} |
||||
|
|
||||
|
for (let i = 0; i < _parse[key].length; i++) { |
||||
|
shepherd.log(`key ==>`, true); |
||||
|
shepherd.log(_parse[key][i], true); |
||||
|
shepherd.log(Number(_parse[key][i].value), true); |
||||
|
|
||||
|
_total[key] += Number(_parse[key][i].value); |
||||
|
|
||||
|
if (_parse[key][i].scriptPubKey && |
||||
|
_parse[key][i].scriptPubKey.addresses && |
||||
|
_parse[key][i].scriptPubKey.addresses[0] === targetAddress && |
||||
|
_parse[key][i].value) { |
||||
|
_sum[key] += Number(_parse[key][i].value); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (_sum.inputs > 0 && |
||||
|
_sum.outputs > 0) { |
||||
|
// vin + change, break into two tx
|
||||
|
result = [{ // reorder since tx sort by default is from newest to oldest
|
||||
|
type: 'sent', |
||||
|
amount: Number(_sum.inputs.toFixed(8)), |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
}, { |
||||
|
type: 'received', |
||||
|
amount: Number(_sum.outputs.toFixed(8)), |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
}]; |
||||
|
|
||||
|
if (network === 'komodo') { // calc claimed interest amount
|
||||
|
const vinVoutDiff = _total.inputs - _total.outputs; |
||||
|
|
||||
|
if (vinVoutDiff < 0) { |
||||
|
result[1].interest = Number(vinVoutDiff.toFixed(8)); |
||||
|
} |
||||
|
} |
||||
|
} else if (_sum.inputs === 0 && _sum.outputs > 0) { |
||||
|
result = { |
||||
|
type: 'received', |
||||
|
amount: Number(_sum.outputs.toFixed(8)), |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
}; |
||||
|
} else if (_sum.inputs > 0 && _sum.outputs === 0) { |
||||
|
result = { |
||||
|
type: 'sent', |
||||
|
amount: Number(_sum.inputs.toFixed(8)), |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
}; |
||||
|
} else { |
||||
|
// (?)
|
||||
|
result = { |
||||
|
type: 'other', |
||||
|
amount: 'unknown', |
||||
|
address: targetAddress, |
||||
|
timestamp: tx.timestamp, |
||||
|
txid: tx.format.txid, |
||||
|
confirmations: tx.confirmations, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
shepherd.log(_sum, true); |
||||
|
shepherd.log(result, true); |
||||
|
|
||||
|
return result; |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/electrum/decoderawtx', (req, res, next) => { |
||||
|
const _network = shepherd.getNetworkData(req.query.network); |
||||
|
const _rawtx = req.query.rawtx; |
||||
|
// const _rawtx = '0100000001dd6d064f5665f8454293ecaa9dbb55accf4f7e443d35f3b5ab7760f54b6c15fe000000006a473044022056355585a4a501ec9afc96aa5df124cf29ad3ac6454b47cd07cd7d89ec95ec2b022074c4604ee349d30e5336f210598e4dc576bf16ebeb67eeac3f4e82f56e930fee012103b90ba01af308757054e0484bb578765d5df59c4a57adbb94e2419df5e7232a63feffffff0289fc923b000000001976a91424af38fcb13bbc171b0b42bb017244a53b6bb2fa88ac20a10700000000001976a9142f4c0f91fc06ac228c120aee41741d0d3909683288ac49258b58';
|
||||
|
const decodedTx = shepherd.electrumJSTxDecoder(_rawtx, _network); |
||||
|
|
||||
|
shepherd.log('electrum decoderawtx input tx ==>', true); |
||||
|
|
||||
|
if (req.query.parseonly || |
||||
|
decodedTx.inputs[0].txid === '0000000000000000000000000000000000000000000000000000000000000000') { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
network: decodedTx.network, |
||||
|
format: decodedTx.format, |
||||
|
inputs: decodedTx.inputs, |
||||
|
outputs: decodedTx.outputs, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
shepherd.log(successObj.result, true); |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} else { |
||||
|
const ecl = new shepherd.electrumJSCore(shepherd.electrumServers[req.query.network].port, shepherd.electrumServers[req.query.network].address, shepherd.electrumServers[req.query.network].proto); // tcp or tls
|
||||
|
|
||||
|
ecl.connect(); |
||||
|
ecl.blockchainTransactionGet(decodedTx.inputs[0].txid) |
||||
|
.then((json) => { |
||||
|
ecl.close(); |
||||
|
shepherd.log(json, true); |
||||
|
|
||||
|
const decodedVin = shepherd.electrumJSTxDecoder(json, _network); |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: { |
||||
|
network: decodedTx.network, |
||||
|
format: decodedTx.format, |
||||
|
inputs: decodedVin.outputs[decodedTx.inputs[0].n], |
||||
|
outputs: decodedTx.outputs, |
||||
|
}, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,52 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.readVersionFile = () => { |
||||
|
// read app version
|
||||
|
const rootLocation = shepherd.path.join(__dirname, '../../'); |
||||
|
const localVersionFile = shepherd.fs.readFileSync(`${rootLocation}version`, 'utf8'); |
||||
|
|
||||
|
return localVersionFile; |
||||
|
} |
||||
|
|
||||
|
shepherd.createAgamaDirs = () => { |
||||
|
if (!shepherd.fs.existsSync(shepherd.agamaDir)) { |
||||
|
shepherd.fs.mkdirSync(shepherd.agamaDir); |
||||
|
|
||||
|
if (shepherd.fs.existsSync(shepherd.agamaDir)) { |
||||
|
shepherd.log(`created agama folder at ${shepherd.agamaDir}`); |
||||
|
shepherd.writeLog(`created agama folder at ${shepherd.agamaDir}`); |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log('agama folder already exists'); |
||||
|
} |
||||
|
|
||||
|
if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { |
||||
|
shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd`); |
||||
|
|
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd`)) { |
||||
|
shepherd.log(`created shepherd folder at ${shepherd.agamaDir}/shepherd`); |
||||
|
shepherd.writeLog(`create shepherd folder at ${shepherd.agamaDir}/shepherd`); |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log('agama/shepherd folder already exists'); |
||||
|
} |
||||
|
|
||||
|
if (!shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { |
||||
|
shepherd.fs.mkdirSync(`${shepherd.agamaDir}/shepherd/pin`); |
||||
|
|
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { |
||||
|
shepherd.log(`created pin folder at ${shepherd.agamaDir}/shepherd/pin`); |
||||
|
shepherd.writeLog(`create pin folder at ${shepherd.agamaDir}/shepherd/pin`); |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log('shepherd/pin folder already exists'); |
||||
|
} |
||||
|
|
||||
|
if (!shepherd.fs.existsSync(shepherd.zcashParamsDir)) { |
||||
|
shepherd.fs.mkdirSync(shepherd.zcashParamsDir); |
||||
|
} else { |
||||
|
shepherd.log('zcashparams folder already exists'); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,150 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: GET |
||||
|
* params: coin, type |
||||
|
* TODO: reorganize to work with coind |
||||
|
*/ |
||||
|
shepherd.get('/kick', (req, res, next) => { |
||||
|
const _coin = req.query.coin; |
||||
|
const _type = req.query.type; |
||||
|
|
||||
|
if (!_coin) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no coin name provided', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
|
||||
|
if (!_type) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no type provided', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
|
||||
|
const kickStartDirs = { |
||||
|
soft: [ |
||||
|
{ |
||||
|
name: 'DB/[coin]', |
||||
|
type: 'pattern', |
||||
|
match: 'balancecrc.', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/utxoaddrs', |
||||
|
type: 'file', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/accounts', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/fastfind', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'tmp/[coin]', |
||||
|
type: 'folder', |
||||
|
} |
||||
|
], |
||||
|
hard: [ |
||||
|
{ |
||||
|
name: 'DB/[coin]', |
||||
|
type: 'pattern', |
||||
|
match: 'balancecrc.', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/utxoaddrs', |
||||
|
type: 'file', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]', |
||||
|
type: 'pattern', |
||||
|
match: 'utxoaddrs.', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/accounts', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/fastfind', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/[coin]/spends', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'tmp/[coin]', |
||||
|
type: 'folder', |
||||
|
} |
||||
|
], |
||||
|
brutal: [ // delete all coin related data
|
||||
|
{ |
||||
|
name: 'DB/[coin]', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/purgeable/[coin]', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'DB/ro/[coin]', |
||||
|
type: 'folder', |
||||
|
}, |
||||
|
{ |
||||
|
name: 'tmp/[coin]', |
||||
|
type: 'folder', |
||||
|
} |
||||
|
] |
||||
|
}; |
||||
|
|
||||
|
if (_coin && |
||||
|
_type) { |
||||
|
for (let i = 0; i < kickStartDirs[_type].length; i++) { |
||||
|
let currentKickItem = kickStartDirs[_type][i]; |
||||
|
|
||||
|
shepherd.log('deleting ' + currentKickItem.type + (currentKickItem.match ? ' ' + currentKickItem.match : '') + ' ' + iguanaDir + '/' + currentKickItem.name.replace('[coin]', _coin)); |
||||
|
if (currentKickItem.type === 'folder' || |
||||
|
currentKickItem.type === 'file') { |
||||
|
/*rimraf(`${iguanaDir}/${currentKickItem.name.replace('[coin]', _coin)}`, function(err) {
|
||||
|
if (err) { |
||||
|
shepherd.writeLog(`kickstart err: ${err}`); |
||||
|
shepherd.log(`kickstart err: ${err}`); |
||||
|
} |
||||
|
});*/ |
||||
|
} else if (currentKickItem.type === 'pattern') { |
||||
|
let dirItems = shepherd.fs.readdirSync(`${iguanaDir}/currentKickItem.name.replace('[coin]', _coin)`); |
||||
|
|
||||
|
if (dirItems && |
||||
|
dirItems.length) { |
||||
|
for (let j = 0; j < dirItems.length; j++) { |
||||
|
if (dirItems[j].indexOf(currentKickItem.match) > -1) { |
||||
|
/*rimraf(`${iguanaDir}/${currentKickItem.name.replace('[coin]', _coin)}/${dirItems[j]}`, function(err) {
|
||||
|
if (err) { |
||||
|
shepherd.writeLog(`kickstart err: ${err}`); |
||||
|
shepherd.log(`kickstart err: ${err}`); |
||||
|
} |
||||
|
});*/ |
||||
|
|
||||
|
shepherd.log(`deleting ${dirItems[j]}`); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: 'kickstart: brutal is executed', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,141 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.log = (msg, isSpvOut) => { |
||||
|
if (shepherd.appConfig.dev || |
||||
|
shepherd.appConfig.debug) { |
||||
|
console.log(msg); |
||||
|
} |
||||
|
|
||||
|
if (!isSpvOut) { |
||||
|
shepherd.appRuntimeLog.push({ |
||||
|
time: Date.now(), |
||||
|
msg: msg, |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.appRuntimeSPVLog.push({ |
||||
|
time: Date.now(), |
||||
|
msg: msg, |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.writeLog = (data) => { |
||||
|
const logLocation = `${shepherd.agamaDir}/shepherd`; |
||||
|
const timeFormatted = new Date(Date.now()).toLocaleString('en-US', { hour12: false }); |
||||
|
|
||||
|
if (shepherd.appConfig.debug) { |
||||
|
if (shepherd.fs.existsSync(`${logLocation}/agamalog.txt`)) { |
||||
|
shepherd.fs.appendFile(`${logLocation}/agamalog.txt`, `${timeFormatted} ${data}\r\n`, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log('error writing log file'); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
shepherd.fs.writeFile(`${logLocation}/agamalog.txt`, `${timeFormatted} ${data}\r\n`, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log('error writing log file'); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.get('/log/runtime', (req, res, next) => { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: req.query.spv && req.query.spv === 'true' ? shepherd.appRuntimeSPVLog : shepherd.appRuntimeLog, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
}); |
||||
|
|
||||
|
shepherd.getAppRuntimeLog = () => { |
||||
|
return new shepherd.Promise((resolve, reject) => { |
||||
|
resolve(shepherd.appRuntimeLog); |
||||
|
}); |
||||
|
}; |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: payload |
||||
|
*/ |
||||
|
shepherd.post('/guilog', (req, res, next) => { |
||||
|
const logLocation = `${shepherd.agamaDir}/shepherd`; |
||||
|
|
||||
|
if (!shepherd.guiLog[shepherd.appSessionHash]) { |
||||
|
shepherd.guiLog[shepherd.appSessionHash] = {}; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp]) { |
||||
|
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].status = req.body.status; |
||||
|
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp].response = req.body.response; |
||||
|
} else { |
||||
|
shepherd.guiLog[shepherd.appSessionHash][req.body.timestamp] = { |
||||
|
function: req.body.function, |
||||
|
type: req.body.type, |
||||
|
url: req.body.url, |
||||
|
payload: req.body.payload, |
||||
|
status: req.body.status, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
shepherd.fs.writeFile(`${logLocation}/agamalog.json`, JSON.stringify(shepherd.guiLog), (err) => { |
||||
|
if (err) { |
||||
|
shepherd.writeLog('error writing gui log file'); |
||||
|
} |
||||
|
|
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: 'gui log entry is added', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
}); |
||||
|
}); |
||||
|
|
||||
|
/* |
||||
|
* type: GET |
||||
|
* params: type |
||||
|
*/ |
||||
|
shepherd.get('/getlog', (req, res, next) => { |
||||
|
const logExt = req.query.type === 'txt' ? 'txt' : 'json'; |
||||
|
|
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`)) { |
||||
|
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/agamalog.${logExt}`, 'utf8', (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: `agama.${logExt} doesnt exist`, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.printDirs = () => { |
||||
|
shepherd.log(`agama dir: ${shepherd.agamaDir}`); |
||||
|
shepherd.log('--------------------------') |
||||
|
shepherd.log(`komodo dir: ${shepherd.komododBin}`); |
||||
|
shepherd.log(`komodo bin: ${shepherd.komodoDir}`); |
||||
|
shepherd.writeLog(`agama dir: ${shepherd.agamaDir}`); |
||||
|
shepherd.writeLog(`komodo dir: ${shepherd.komododBin}`); |
||||
|
shepherd.writeLog(`komodo bin: ${shepherd.komodoDir}`); |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,74 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.pathsAgama = () => { |
||||
|
switch (shepherd.os.platform()) { |
||||
|
case 'darwin': |
||||
|
shepherd.fixPath(); |
||||
|
shepherd.agamaDir = `${process.env.HOME}/Library/Application Support/Agama`; |
||||
|
break; |
||||
|
|
||||
|
case 'linux': |
||||
|
shepherd.agamaDir = `${process.env.HOME}/.agama`; |
||||
|
break; |
||||
|
|
||||
|
case 'win32': |
||||
|
shepherd.agamaDir = `${process.env.APPDATA}/Agama`; |
||||
|
shepherd.agamaDir = shepherd.path.normalize(shepherd.agamaDir); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
shepherd.pathsDaemons = () => { |
||||
|
switch (shepherd.os.platform()) { |
||||
|
case 'darwin': |
||||
|
shepherd.fixPath(); |
||||
|
shepherd.agamaTestDir = `${process.env.HOME}/Library/Application Support/Agama/test`, |
||||
|
shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/osx/komodod'), |
||||
|
shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/osx/komodo-cli'), |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/Library/Application Support/Komodo`, |
||||
|
shepherd.zcashdBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcashd', |
||||
|
shepherd.zcashcliBin = '/Applications/ZCashSwingWalletUI.app/Contents/MacOS/zcash-cli', |
||||
|
shepherd.zcashDir = `${process.env.HOME}/Library/Application Support/Zcash`, |
||||
|
shepherd.zcashParamsDir = `${process.env.HOME}/Library/Application Support/ZcashParams`, |
||||
|
shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/osx/chipsd'), |
||||
|
shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/osx/chips-cli'), |
||||
|
shepherd.chipsDir = `${process.env.HOME}/Library/Application Support/Chips`, |
||||
|
shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/osx/dex/coind'); |
||||
|
break; |
||||
|
|
||||
|
case 'linux': |
||||
|
shepherd.agamaTestDir = `${process.env.HOME}/.agama/test`, |
||||
|
shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/komodod'), |
||||
|
shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/komodo-cli'), |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.HOME}/.komodo`, |
||||
|
shepherd.zcashParamsDir = `${process.env.HOME}/.zcash-params`, |
||||
|
shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/chipsd'), |
||||
|
shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/linux64/chips-cli'), |
||||
|
shepherd.chipsDir = `${process.env.HOME}/.chips`, |
||||
|
shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/linux64/dex/coind'); |
||||
|
break; |
||||
|
|
||||
|
case 'win32': |
||||
|
shepherd.agamaTestDir = `${process.env.APPDATA}/Agama/test`; |
||||
|
shepherd.agamaTestDir = shepherd.path.normalize(shepherd.agamaTestDir); |
||||
|
shepherd.komododBin = shepherd.path.join(__dirname, '../../assets/bin/win64/komodod.exe'), |
||||
|
shepherd.komododBin = shepherd.path.normalize(shepherd.komododBin), |
||||
|
shepherd.komodocliBin = shepherd.path.join(__dirname, '../../assets/bin/win64/komodo-cli.exe'), |
||||
|
shepherd.komodocliBin = shepherd.path.normalize(shepherd.komodocliBin), |
||||
|
shepherd.komodoDir = shepherd.appConfig.dataDir.length ? shepherd.appConfig.dataDir : `${process.env.APPDATA}/Komodo`, |
||||
|
shepherd.komodoDir = shepherd.path.normalize(shepherd.komodoDir); |
||||
|
shepherd.chipsBin = shepherd.path.join(__dirname, '../../assets/bin/win64/chipsd.exe'), |
||||
|
shepherd.chipsBin = shepherd.path.normalize(shepherd.chipsBin), |
||||
|
shepherd.chipscliBin = shepherd.path.join(__dirname, '../../assets/bin/win64/chips-cli.exe'), |
||||
|
shepherd.chipscliBin = shepherd.path.normalize(shepherd.chipscliBin), |
||||
|
shepherd.chipsDir = `${process.env.APPDATA}/Chips`, |
||||
|
shepherd.chipsDir = shepherd.path.normalize(shepherd.chipsDir); |
||||
|
shepherd.zcashParamsDir = `${process.env.APPDATA}/ZcashParams`; |
||||
|
shepherd.zcashParamsDir = shepherd.path.normalize(shepherd.zcashParamsDir); |
||||
|
shepherd.coindRootDir = shepherd.path.join(__dirname, '../../assets/bin/osx/dex/coind'); |
||||
|
shepherd.coindRootDir = shepherd.path.normalize(shepherd.coindRootDir); |
||||
|
break; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,146 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: none |
||||
|
*/ |
||||
|
shepherd.post('/encryptkey', (req, res, next) => { |
||||
|
if (req.body.key && |
||||
|
req.body.string && |
||||
|
req.body.pubkey) { |
||||
|
const encryptedString = shepherd.aes256.encrypt(req.body.key, req.body.string); |
||||
|
|
||||
|
// test pin security
|
||||
|
// - at least 1 char in upper case
|
||||
|
// - at least 1 digit
|
||||
|
// - at least one special character
|
||||
|
// - min length 8
|
||||
|
|
||||
|
const _pin = req.body.key; |
||||
|
const _pinTest = _pin.match('^(?=.*[A-Z])(?=.*[^<>{}\"/|;:.,~!?@#$%^=&*\\]\\\\()\\[_+]*$)(?=.*[0-9])(?=.*[a-z]).{8}$'); |
||||
|
|
||||
|
shepherd.fs.writeFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, encryptedString, (err) => { |
||||
|
if (err) { |
||||
|
shepherd.log('error writing pin file'); |
||||
|
} |
||||
|
|
||||
|
const returnObj = { |
||||
|
msg: 'success', |
||||
|
result: encryptedString, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
}); |
||||
|
} else { |
||||
|
let errorObj = { |
||||
|
msg: 'error', |
||||
|
result: '', |
||||
|
}; |
||||
|
const _paramsList = [ |
||||
|
'key', |
||||
|
'string', |
||||
|
'pubkey' |
||||
|
]; |
||||
|
let _errorParamsList = []; |
||||
|
|
||||
|
for (let i = 0; i < _paramsList.length; i++) { |
||||
|
if (!req.query[_paramsList[i]]) { |
||||
|
_errorParamsList.push(_paramsList[i]); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
errorObj.result = `missing param ${_errorParamsList.join(', ')}`; |
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.post('/decryptkey', (req, res, next) => { |
||||
|
if (req.body.key && |
||||
|
req.body.pubkey) { |
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`)) { |
||||
|
shepherd.fs.readFile(`${shepherd.agamaDir}/shepherd/pin/${req.body.pubkey}.pin`, 'utf8', (err, data) => { |
||||
|
if (err) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: err, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
const encryptedKey = shepherd.aes256.decrypt(req.body.key, data); |
||||
|
// test if stored encrypted passphrase is decrypted correctly
|
||||
|
// if not then the key is wrong
|
||||
|
const _regexTest = encryptedKey.match(/^[0-9a-zA-Z ]+$/g); |
||||
|
let returnObj; |
||||
|
|
||||
|
if (!_regexTest) { |
||||
|
returnObj = { |
||||
|
msg: 'error', |
||||
|
result: 'wrong key', |
||||
|
}; |
||||
|
} else { |
||||
|
returnObj = { |
||||
|
msg: 'success', |
||||
|
result: encryptedKey, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
res.end(JSON.stringify(returnObj)); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: `file ${req.query.pubkey}.pin doesnt exist`, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
} else { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'missing key or pubkey param', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
shepherd.get('/getpinlist', (req, res, next) => { |
||||
|
if (shepherd.fs.existsSync(`${shepherd.agamaDir}/shepherd/pin`)) { |
||||
|
shepherd.fs.readdir(`${shepherd.agamaDir}/shepherd/pin`, (err, items) => { |
||||
|
let _pins = []; |
||||
|
|
||||
|
for (let i = 0; i < items.length; i++) { |
||||
|
if (items[i].substr(items[i].length - 4, 4) === '.pin') { |
||||
|
_pins.push(items[i].substr(0, items[i].length - 4)); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
if (!items.length) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no pins', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
const successObj = { |
||||
|
msg: 'success', |
||||
|
result: _pins, |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(successObj)); |
||||
|
} |
||||
|
}); |
||||
|
} else { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'pin folder doesnt exist', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,82 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.quitKomodod = (timeout = 100) => { |
||||
|
// if komodod is under heavy load it may not respond to cli stop the first time
|
||||
|
// exit komodod gracefully
|
||||
|
let coindExitInterval = {}; |
||||
|
shepherd.lockDownAddCoin = true; |
||||
|
|
||||
|
for (let key in shepherd.coindInstanceRegistry) { |
||||
|
const chain = key !== 'komodod' ? key : null; |
||||
|
let _coindQuitCmd = shepherd.komodocliBin; |
||||
|
|
||||
|
// any coind
|
||||
|
if (shepherd.nativeCoindList[key.toLowerCase()]) { |
||||
|
_coindQuitCmd = `${shepherd.coindRootDir}/${key.toLowerCase()}/${shepherd.nativeCoindList[key.toLowerCase()].bin.toLowerCase()}-cli`; |
||||
|
} |
||||
|
if (key === 'CHIPS') { |
||||
|
_coindQuitCmd = shepherd.chipscliBin; |
||||
|
} |
||||
|
|
||||
|
const execCliStop = () => { |
||||
|
let _arg = []; |
||||
|
if (chain && !shepherd.nativeCoindList[key.toLowerCase()] && key !== 'CHIPS') { |
||||
|
_arg.push(`-ac_name=${chain}`); |
||||
|
|
||||
|
if (shepherd.appConfig.dataDir.length) { |
||||
|
_arg.push(`-datadir=${shepherd.appConfig.dataDir + (key !== 'komodod' ? '/' + key : '')}`); |
||||
|
} |
||||
|
} else if (key === 'komodod' && shepherd.appConfig.dataDir.length) { |
||||
|
_arg.push(`-datadir=${shepherd.appConfig.dataDir}`); |
||||
|
} |
||||
|
|
||||
|
_arg.push('stop'); |
||||
|
shepherd.execFile(`${_coindQuitCmd}`, _arg, (error, stdout, stderr) => { |
||||
|
shepherd.log(`stdout: ${stdout}`); |
||||
|
shepherd.log(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (stdout.indexOf('EOF reached') > -1 || |
||||
|
stderr.indexOf('EOF reached') > -1 || |
||||
|
(error && error.toString().indexOf('Command failed') > -1 && !stderr) || // win "special snowflake" case
|
||||
|
stdout.indexOf('connect to server: unknown (code -1)') > -1 || |
||||
|
stderr.indexOf('connect to server: unknown (code -1)') > -1) { |
||||
|
delete shepherd.coindInstanceRegistry[key]; |
||||
|
clearInterval(coindExitInterval[key]); |
||||
|
} |
||||
|
|
||||
|
// workaround for AGT-65
|
||||
|
const _port = shepherd.assetChainPorts[key]; |
||||
|
setTimeout(() => { |
||||
|
shepherd.portscanner.checkPortStatus(_port, '127.0.0.1', (error, status) => { |
||||
|
// Status is 'open' if currently in use or 'closed' if available
|
||||
|
if (status === 'closed') { |
||||
|
delete shepherd.coindInstanceRegistry[key]; |
||||
|
clearInterval(coindExitInterval[key]); |
||||
|
} |
||||
|
}); |
||||
|
}, 100); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
} |
||||
|
|
||||
|
if (key === 'CHIPS') { |
||||
|
setTimeout(() => { |
||||
|
shepherd.killRogueProcess('chips-cli'); |
||||
|
}, 100); |
||||
|
} else { |
||||
|
setTimeout(() => { |
||||
|
shepherd.killRogueProcess('komodo-cli'); |
||||
|
}, 100); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
execCliStop(); |
||||
|
coindExitInterval[key] = setInterval(() => { |
||||
|
execCliStop(); |
||||
|
}, timeout); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
@ -0,0 +1,246 @@ |
|||||
|
module.exports = (shepherd) => { |
||||
|
shepherd.getConf = (chain) => { |
||||
|
let _confLocation = chain === 'komodod' ? `${shepherd.komodoDir}/komodo.conf` : `${shepherd.komodoDir}/${chain}/${chain}.conf`; |
||||
|
_confLocation = chain === 'CHIPS' ? `${shepherd.chipsDir}/chips.conf` : _confLocation; |
||||
|
|
||||
|
// any coind
|
||||
|
if (chain) { |
||||
|
if (shepherd.nativeCoindList[chain.toLowerCase()]) { |
||||
|
const _osHome = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; |
||||
|
let coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[chain.toLowerCase()].bin.toLowerCase()}/debug.log`; |
||||
|
|
||||
|
_confLocation = `${_osHome}/.${shepherd.nativeCoindList[chain.toLowerCase()].bin.toLowerCase()}/${shepherd.nativeCoindList[chain.toLowerCase()].bin.toLowerCase()}.conf`; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.fs.existsSync(_confLocation)) { |
||||
|
let _port = shepherd.assetChainPorts[chain]; |
||||
|
const _rpcConf = shepherd.fs.readFileSync(_confLocation, 'utf8'); |
||||
|
|
||||
|
// any coind
|
||||
|
if (shepherd.nativeCoindList[chain.toLowerCase()]) { |
||||
|
_port = shepherd.nativeCoindList[chain.toLowerCase()].port; |
||||
|
} |
||||
|
|
||||
|
if (_rpcConf.length) { |
||||
|
let _match; |
||||
|
let parsedRpcConfig = { |
||||
|
user: '', |
||||
|
pass: '', |
||||
|
port: _port, |
||||
|
}; |
||||
|
|
||||
|
if (_match = _rpcConf.match(/rpcuser=\s*(.*)/)) { |
||||
|
parsedRpcConfig.user = _match[1]; |
||||
|
} |
||||
|
|
||||
|
if ((_match = _rpcConf.match(/rpcpass=\s*(.*)/)) || |
||||
|
(_match = _rpcConf.match(/rpcpassword=\s*(.*)/))) { |
||||
|
parsedRpcConfig.pass = _match[1]; |
||||
|
} |
||||
|
|
||||
|
if (shepherd.nativeCoindList[chain.toLowerCase()]) { |
||||
|
shepherd.rpcConf[chain] = parsedRpcConfig; |
||||
|
} else { |
||||
|
shepherd.rpcConf[chain === 'komodod' ? 'KMD' : chain] = parsedRpcConfig; |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log(`${_confLocation} is empty`); |
||||
|
} |
||||
|
} else { |
||||
|
shepherd.log(`${_confLocation} doesnt exist`); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/* |
||||
|
* type: POST |
||||
|
* params: payload |
||||
|
*/ |
||||
|
shepherd.post('/cli', (req, res, next) => { |
||||
|
if (!req.body.payload) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'no payload provided', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else if (!req.body.payload.cmd.match(/^[0-9a-zA-Z _\,\.\[\]"'/\\]+$/g)) { |
||||
|
const errorObj = { |
||||
|
msg: 'error', |
||||
|
result: 'wrong cli string format', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(errorObj)); |
||||
|
} else { |
||||
|
const _mode = req.body.payload.mode === 'passthru' ? 'passthru' : 'default'; |
||||
|
const _chain = req.body.payload.chain === 'KMD' ? null : req.body.payload.chain; |
||||
|
let _cmd = req.body.payload.cmd; |
||||
|
const _params = req.body.payload.params ? ` ${req.body.payload.params}` : ''; |
||||
|
|
||||
|
if (!shepherd.rpcConf[_chain]) { |
||||
|
shepherd.getConf(req.body.payload.chain === 'KMD' || !req.body.payload.chain && shepherd.kmdMainPassiveMode ? 'komodod' : req.body.payload.chain); |
||||
|
} |
||||
|
|
||||
|
if (_mode === 'default') { |
||||
|
/*let _body = { |
||||
|
agent: 'bitcoinrpc', |
||||
|
method: _cmd, |
||||
|
}; |
||||
|
|
||||
|
if (req.body.payload.params) { |
||||
|
_body = { |
||||
|
agent: 'bitcoinrpc', |
||||
|
method: _cmd, |
||||
|
params: req.body.payload.params === ' ' ? [''] : req.body.payload.params, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
const options = { |
||||
|
url: `http://localhost:${rpcConf[req.body.payload.chain].port}`, |
||||
|
method: 'POST', |
||||
|
auth: { |
||||
|
user: rpcConf[req.body.payload.chain].user, |
||||
|
pass: rpcConf[req.body.payload.chain].pass, |
||||
|
}, |
||||
|
body: JSON.stringify(_body) |
||||
|
}; |
||||
|
|
||||
|
// send back body on both success and error
|
||||
|
// this bit replicates iguana core's behaviour
|
||||
|
shepherd.request(options, function(error, response, body) { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
res.end(body); |
||||
|
} else { |
||||
|
res.end(body); |
||||
|
} |
||||
|
});*/ |
||||
|
if (_cmd === 'debug' && |
||||
|
_chain !== 'CHIPS') { |
||||
|
if (shepherd.nativeCoindList[_chain.toLowerCase()]) { |
||||
|
const _osHome = shepherd.os.platform === 'win32' ? process.env.APPDATA : process.env.HOME; |
||||
|
let coindDebugLogLocation; |
||||
|
|
||||
|
if (_chain === 'CHIPS') { |
||||
|
coindDebugLogLocation = `${shepherd.chipsDir}/debug.log`; |
||||
|
} else { |
||||
|
coindDebugLogLocation = `${_osHome}/.${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}/debug.log`; |
||||
|
} |
||||
|
|
||||
|
shepherd.readDebugLog(coindDebugLogLocation, 1) |
||||
|
.then((result) => { |
||||
|
const _obj = { |
||||
|
'msg': 'success', |
||||
|
'result': result, |
||||
|
}; |
||||
|
|
||||
|
// shepherd.log('bitcoinrpc debug ====>');
|
||||
|
// console.log(result);
|
||||
|
|
||||
|
res.end(JSON.stringify(_obj)); |
||||
|
}, (result) => { |
||||
|
const _obj = { |
||||
|
error: result, |
||||
|
result: 'error', |
||||
|
}; |
||||
|
|
||||
|
res.end(JSON.stringify(_obj)); |
||||
|
}); |
||||
|
} else { |
||||
|
res.end({ |
||||
|
error: 'bitcoinrpc debug error', |
||||
|
result: 'error', |
||||
|
}); |
||||
|
// console.log('bitcoinrpc debug error');
|
||||
|
} |
||||
|
} else { |
||||
|
if (_chain === 'CHIPS' && |
||||
|
_cmd === 'debug') { |
||||
|
_cmd = 'getblockchaininfo'; |
||||
|
} |
||||
|
|
||||
|
let _body = { |
||||
|
'agent': 'bitcoinrpc', |
||||
|
'method': _cmd, |
||||
|
}; |
||||
|
|
||||
|
if (req.body.payload.params) { |
||||
|
_body = { |
||||
|
'agent': 'bitcoinrpc', |
||||
|
'method': _cmd, |
||||
|
'params': req.body.payload.params === ' ' ? [''] : req.body.payload.params, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
if (req.body.payload.chain) { |
||||
|
const options = { |
||||
|
url: `http://localhost:${shepherd.rpcConf[req.body.payload.chain].port}`, |
||||
|
method: 'POST', |
||||
|
auth: { |
||||
|
'user': shepherd.rpcConf[req.body.payload.chain].user, |
||||
|
'pass': shepherd.rpcConf[req.body.payload.chain].pass |
||||
|
}, |
||||
|
body: JSON.stringify(_body) |
||||
|
}; |
||||
|
|
||||
|
// send back body on both success and error
|
||||
|
// this bit replicates iguana core's behaviour
|
||||
|
shepherd.request(options, (error, response, body) => { |
||||
|
if (response && |
||||
|
response.statusCode && |
||||
|
response.statusCode === 200) { |
||||
|
res.end(body); |
||||
|
} else { |
||||
|
res.end(body); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
} else { |
||||
|
let _coindCliBin = shepherd.komodocliBin; |
||||
|
|
||||
|
if (shepherd.nativeCoindList && |
||||
|
_chain && |
||||
|
shepherd.nativeCoindList[_chain.toLowerCase()]) { |
||||
|
_coindCliBin = `${shepherd.coindRootDir}/${_chain.toLowerCase()}/${shepherd.nativeCoindList[_chain.toLowerCase()].bin.toLowerCase()}-cli`; |
||||
|
} |
||||
|
|
||||
|
let _arg = (_chain ? ' -ac_name=' + _chain : '') + ' ' + _cmd + _params; |
||||
|
|
||||
|
if (shepherd.appConfig.dataDir.length) { |
||||
|
_arg = `${_arg} -datadir=${shepherd.appConfig.dataDir + (_chain ? '/' + key : '')}`; |
||||
|
} |
||||
|
|
||||
|
_arg = _arg.trim().split(' '); |
||||
|
shepherd.execFile(_coindCliBin, _arg, (error, stdout, stderr) => { |
||||
|
shepherd.log(`stdout: ${stdout}`); |
||||
|
shepherd.log(`stderr: ${stderr}`); |
||||
|
|
||||
|
if (error !== null) { |
||||
|
shepherd.log(`exec error: ${error}`); |
||||
|
} |
||||
|
|
||||
|
let responseObj; |
||||
|
|
||||
|
if (stderr) { |
||||
|
responseObj = { |
||||
|
msg: 'error', |
||||
|
result: stderr, |
||||
|
}; |
||||
|
} else { |
||||
|
responseObj = { |
||||
|
msg: 'success', |
||||
|
result: stdout, |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
res.end(JSON.stringify(responseObj)); |
||||
|
shepherd.killRogueProcess('komodo-cli'); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}); |
||||
|
|
||||
|
return shepherd; |
||||
|
}; |
Loading…
Reference in new issue