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