Channel status
This commit is contained in:
parent
795bb6a7a6
commit
65c731e1ad
@ -2,7 +2,9 @@
|
|||||||
<div class="mb-2">
|
<div class="mb-2">
|
||||||
<h1 i18n="shared.address" class="mb-0">Channel <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.id }}</a> <app-clipboard [text]="channel.id"></app-clipboard></h1>
|
<h1 i18n="shared.address" class="mb-0">Channel <a [routerLink]="['/lightning/channel' | relativeUrl, channel.id]">{{ channel.id }}</a> <app-clipboard [text]="channel.id"></app-clipboard></h1>
|
||||||
<div class="badges">
|
<div class="badges">
|
||||||
<span class="badge rounded-pill badge-success">Open</span>
|
<span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
|
||||||
|
<span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
|
||||||
|
<span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -46,98 +48,95 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
<h2>Peers</h2>
|
|
||||||
|
|
||||||
<div class="box">
|
<div class="row row-cols-1 row-cols-md-2">
|
||||||
|
<div class="col">
|
||||||
<div class="row">
|
<div class="mb-2">
|
||||||
<div class="col-md">
|
<h2 class="mb-0">{{ channel.alias_left }}</h2>
|
||||||
<table class="table table-borderless table-striped">
|
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]" >
|
||||||
<tbody>
|
{{ channel.node1_public_key | shortenString : 18 }}
|
||||||
<tr>
|
</a>
|
||||||
<td i18n="address.total-sent">Node</td>
|
<app-clipboard [text]="channel.node1_public_key"></app-clipboard>
|
||||||
<td>
|
|
||||||
{{ channel.alias_left }}
|
|
||||||
<br>
|
|
||||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]" >
|
|
||||||
{{ channel.node1_public_key | shortenString : 18 }}
|
|
||||||
</a>
|
|
||||||
<app-clipboard [text]="channel.node1_public_key"></app-clipboard>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-sent">Fee rate</td>
|
|
||||||
<td>
|
|
||||||
{{ channel.node1_fee_rate / 10000 | number }}%
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-sent">Base fee</td>
|
|
||||||
<td>
|
|
||||||
<app-sats [satoshis]="channel.node1_base_fee_mtokens / 1000"></app-sats>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-sent">Min HTLC</td>
|
|
||||||
<td>
|
|
||||||
<app-sats [satoshis]="channel.node1_min_htlc_mtokens / 1000"></app-sats>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td i18n="address.total-sent">Max HTLC</td>
|
|
||||||
<td>
|
|
||||||
<app-sats [satoshis]="channel.node1_max_htlc_mtokens / 1000"></app-sats>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="w-100 d-block d-md-none"></div>
|
<div class="box">
|
||||||
<div class="col-md">
|
|
||||||
<table class="table table-borderless table-striped">
|
<div class="row">
|
||||||
<tbody>
|
<div class="col-md">
|
||||||
<tr>
|
<table class="table table-borderless table-striped">
|
||||||
<td i18n="address.total-sent">Node</td>
|
<tbody>
|
||||||
<td>
|
<tr>
|
||||||
{{ channel.alias_right }}
|
<td i18n="address.total-sent">Fee rate</td>
|
||||||
<br>
|
<td>
|
||||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]" >
|
{{ channel.node1_fee_rate / 10000 | number }}%
|
||||||
{{ channel.node2_public_key | shortenString : 18 }}
|
</td>
|
||||||
</a>
|
</tr>
|
||||||
<app-clipboard [text]="channel.node1_public_key"></app-clipboard>
|
<tr>
|
||||||
</td>
|
<td i18n="address.total-sent">Base fee</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<app-sats [satoshis]="channel.node1_base_fee_mtokens / 1000"></app-sats>
|
||||||
<td i18n="address.total-sent">Fee rate</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
{{ channel.node2_fee_rate / 10000 | number }}%
|
<tr>
|
||||||
</td>
|
<td i18n="address.total-sent">Min HTLC</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<app-sats [satoshis]="channel.node1_min_htlc_mtokens / 1000"></app-sats>
|
||||||
<td i18n="address.total-sent">Base fee</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
<app-sats [satoshis]="channel.node2_base_fee_mtokens / 1000"></app-sats>
|
<tr>
|
||||||
</td>
|
<td i18n="address.total-sent">Max HTLC</td>
|
||||||
</tr>
|
<td>
|
||||||
<tr>
|
<app-sats [satoshis]="channel.node1_max_htlc_mtokens / 1000"></app-sats>
|
||||||
<td i18n="address.total-sent">Min HTLC</td>
|
</td>
|
||||||
<td>
|
</tr>
|
||||||
<app-sats [satoshis]="channel.node2_min_htlc_mtokens / 1000"></app-sats>
|
</tbody>
|
||||||
</td>
|
</table>
|
||||||
</tr>
|
</div>
|
||||||
<tr>
|
</div>
|
||||||
<td i18n="address.total-sent">Max HTLC</td>
|
|
||||||
<td>
|
|
||||||
<app-sats [satoshis]="channel.node2_max_htlc_mtokens / 1000"></app-sats>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<div class="mb-2">
|
||||||
|
<h2 class="mb-0">{{ channel.alias_right }}</h2>
|
||||||
|
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]" >
|
||||||
|
{{ channel.node2_public_key | shortenString : 18 }}
|
||||||
|
</a>
|
||||||
|
<app-clipboard [text]="channel.node1_public_key"></app-clipboard>
|
||||||
|
</div>
|
||||||
|
<div class="box">
|
||||||
|
|
||||||
|
<div class="col-md">
|
||||||
|
<table class="table table-borderless table-striped">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.total-sent">Fee rate</td>
|
||||||
|
<td>
|
||||||
|
{{ channel.node2_fee_rate / 10000 | number }}%
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.total-sent">Base fee</td>
|
||||||
|
<td>
|
||||||
|
<app-sats [satoshis]="channel.node2_base_fee_mtokens / 1000"></app-sats>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.total-sent">Min HTLC</td>
|
||||||
|
<td>
|
||||||
|
<app-sats [satoshis]="channel.node2_min_htlc_mtokens / 1000"></app-sats>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td i18n="address.total-sent">Max HTLC</td>
|
||||||
|
<td>
|
||||||
|
<app-sats [satoshis]="channel.node2_max_htlc_mtokens / 1000"></app-sats>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,3 +1,3 @@
|
|||||||
.badges {
|
.badges {
|
||||||
font-size: 18px;
|
font-size: 20px;
|
||||||
}
|
}
|
@ -3,6 +3,7 @@
|
|||||||
<table class="table table-borderless">
|
<table class="table table-borderless">
|
||||||
<thead>
|
<thead>
|
||||||
<th class="alias text-left" i18n="nodes.alias">Node Alias</th>
|
<th class="alias text-left" i18n="nodes.alias">Node Alias</th>
|
||||||
|
<th class="alias text-left" i18n="nodes.alias">Status</th>
|
||||||
<th class="channels text-right" i18n="channels.rate">Fee Rate</th>
|
<th class="channels text-right" i18n="channels.rate">Fee Rate</th>
|
||||||
<th class="capacity text-right" i18n="channels.id">Channel ID</th>
|
<th class="capacity text-right" i18n="channels.id">Channel ID</th>
|
||||||
<th class="capacity text-right" i18n="nodes.capacity">Capacity</th>
|
<th class="capacity text-right" i18n="nodes.capacity">Capacity</th>
|
||||||
@ -14,6 +15,11 @@
|
|||||||
<td class="alias text-left">
|
<td class="alias text-left">
|
||||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]">{{ channel.alias_left }}</a>
|
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node1_public_key]">{{ channel.alias_left }}</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
|
||||||
|
<span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
|
||||||
|
<span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
|
||||||
|
</td>
|
||||||
<td class="capacity text-right">
|
<td class="capacity text-right">
|
||||||
{{ channel.node1_fee_rate / 10000 | number }}%
|
{{ channel.node1_fee_rate / 10000 | number }}%
|
||||||
</td>
|
</td>
|
||||||
@ -22,6 +28,11 @@
|
|||||||
<td class="alias text-left" *ngIf="channel.node1_public_key === publicKey">
|
<td class="alias text-left" *ngIf="channel.node1_public_key === publicKey">
|
||||||
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]">{{ channel.alias_right }}</a>
|
<a [routerLink]="['/lightning/node' | relativeUrl, channel.node2_public_key]">{{ channel.alias_right }}</a>
|
||||||
</td>
|
</td>
|
||||||
|
<td>
|
||||||
|
<span class="badge rounded-pill badge-secondary" *ngIf="channel.status === 0">Inactive</span>
|
||||||
|
<span class="badge rounded-pill badge-success" *ngIf="channel.status === 1">Active</span>
|
||||||
|
<span class="badge rounded-pill badge-danger" *ngIf="channel.status === 2">Closed</span>
|
||||||
|
</td>
|
||||||
<td class="capacity text-right">
|
<td class="capacity text-right">
|
||||||
{{ channel.node2_fee_rate / 10000 | number }}%
|
{{ channel.node2_fee_rate / 10000 | number }}%
|
||||||
</td>
|
</td>
|
||||||
|
@ -17,6 +17,12 @@
|
|||||||
"TSL_CERT_PATH": "",
|
"TSL_CERT_PATH": "",
|
||||||
"MACAROON_PATH": ""
|
"MACAROON_PATH": ""
|
||||||
},
|
},
|
||||||
|
"CORE_RPC": {
|
||||||
|
"HOST": "127.0.0.1",
|
||||||
|
"PORT": 8332,
|
||||||
|
"USERNAME": "mempool",
|
||||||
|
"PASSWORD": "mempool"
|
||||||
|
},
|
||||||
"DATABASE": {
|
"DATABASE": {
|
||||||
"HOST": "127.0.0.1",
|
"HOST": "127.0.0.1",
|
||||||
"PORT": 3306,
|
"PORT": 3306,
|
||||||
|
12
lightning-backend/src/api/bitcoin/bitcoin-client.ts
Normal file
12
lightning-backend/src/api/bitcoin/bitcoin-client.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import config from '../../config';
|
||||||
|
const bitcoin = require('./rpc-api/index');
|
||||||
|
|
||||||
|
const nodeRpcCredentials: any = {
|
||||||
|
host: config.CORE_RPC.HOST,
|
||||||
|
port: config.CORE_RPC.PORT,
|
||||||
|
user: config.CORE_RPC.USERNAME,
|
||||||
|
pass: config.CORE_RPC.PASSWORD,
|
||||||
|
timeout: 60000,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new bitcoin.Client(nodeRpcCredentials);
|
92
lightning-backend/src/api/bitcoin/rpc-api/commands.ts
Normal file
92
lightning-backend/src/api/bitcoin/rpc-api/commands.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
module.exports = {
|
||||||
|
addMultiSigAddress: 'addmultisigaddress',
|
||||||
|
addNode: 'addnode', // bitcoind v0.8.0+
|
||||||
|
backupWallet: 'backupwallet',
|
||||||
|
createMultiSig: 'createmultisig',
|
||||||
|
createRawTransaction: 'createrawtransaction', // bitcoind v0.7.0+
|
||||||
|
decodeRawTransaction: 'decoderawtransaction', // bitcoind v0.7.0+
|
||||||
|
decodeScript: 'decodescript',
|
||||||
|
dumpPrivKey: 'dumpprivkey',
|
||||||
|
dumpWallet: 'dumpwallet', // bitcoind v0.9.0+
|
||||||
|
encryptWallet: 'encryptwallet',
|
||||||
|
estimateFee: 'estimatefee', // bitcoind v0.10.0x
|
||||||
|
estimatePriority: 'estimatepriority', // bitcoind v0.10.0+
|
||||||
|
generate: 'generate', // bitcoind v0.11.0+
|
||||||
|
getAccount: 'getaccount',
|
||||||
|
getAccountAddress: 'getaccountaddress',
|
||||||
|
getAddedNodeInfo: 'getaddednodeinfo', // bitcoind v0.8.0+
|
||||||
|
getAddressesByAccount: 'getaddressesbyaccount',
|
||||||
|
getBalance: 'getbalance',
|
||||||
|
getBestBlockHash: 'getbestblockhash', // bitcoind v0.9.0+
|
||||||
|
getBlock: 'getblock',
|
||||||
|
getBlockStats: 'getblockstats',
|
||||||
|
getBlockFilter: 'getblockfilter',
|
||||||
|
getBlockchainInfo: 'getblockchaininfo', // bitcoind v0.9.2+
|
||||||
|
getBlockCount: 'getblockcount',
|
||||||
|
getBlockHash: 'getblockhash',
|
||||||
|
getBlockHeader: 'getblockheader',
|
||||||
|
getBlockTemplate: 'getblocktemplate', // bitcoind v0.7.0+
|
||||||
|
getChainTips: 'getchaintips', // bitcoind v0.10.0+
|
||||||
|
getChainTxStats: 'getchaintxstats',
|
||||||
|
getConnectionCount: 'getconnectioncount',
|
||||||
|
getDifficulty: 'getdifficulty',
|
||||||
|
getGenerate: 'getgenerate',
|
||||||
|
getInfo: 'getinfo',
|
||||||
|
getMempoolAncestors: 'getmempoolancestors',
|
||||||
|
getMempoolDescendants: 'getmempooldescendants',
|
||||||
|
getMempoolEntry: 'getmempoolentry',
|
||||||
|
getMempoolInfo: 'getmempoolinfo', // bitcoind v0.10+
|
||||||
|
getMiningInfo: 'getmininginfo',
|
||||||
|
getNetTotals: 'getnettotals',
|
||||||
|
getNetworkInfo: 'getnetworkinfo', // bitcoind v0.9.2+
|
||||||
|
getNetworkHashPs: 'getnetworkhashps', // bitcoind v0.9.0+
|
||||||
|
getNewAddress: 'getnewaddress',
|
||||||
|
getPeerInfo: 'getpeerinfo', // bitcoind v0.7.0+
|
||||||
|
getRawChangeAddress: 'getrawchangeaddress', // bitcoin v0.9+
|
||||||
|
getRawMemPool: 'getrawmempool', // bitcoind v0.7.0+
|
||||||
|
getRawTransaction: 'getrawtransaction', // bitcoind v0.7.0+
|
||||||
|
getReceivedByAccount: 'getreceivedbyaccount',
|
||||||
|
getReceivedByAddress: 'getreceivedbyaddress',
|
||||||
|
getTransaction: 'gettransaction',
|
||||||
|
getTxOut: 'gettxout', // bitcoind v0.7.0+
|
||||||
|
getTxOutProof: 'gettxoutproof', // bitcoind v0.11.0+
|
||||||
|
getTxOutSetInfo: 'gettxoutsetinfo', // bitcoind v0.7.0+
|
||||||
|
getUnconfirmedBalance: 'getunconfirmedbalance', // bitcoind v0.9.0+
|
||||||
|
getWalletInfo: 'getwalletinfo', // bitcoind v0.9.2+
|
||||||
|
help: 'help',
|
||||||
|
importAddress: 'importaddress', // bitcoind v0.10.0+
|
||||||
|
importPrivKey: 'importprivkey',
|
||||||
|
importWallet: 'importwallet', // bitcoind v0.9.0+
|
||||||
|
keypoolRefill: 'keypoolrefill',
|
||||||
|
keyPoolRefill: 'keypoolrefill',
|
||||||
|
listAccounts: 'listaccounts',
|
||||||
|
listAddressGroupings: 'listaddressgroupings', // bitcoind v0.7.0+
|
||||||
|
listLockUnspent: 'listlockunspent', // bitcoind v0.8.0+
|
||||||
|
listReceivedByAccount: 'listreceivedbyaccount',
|
||||||
|
listReceivedByAddress: 'listreceivedbyaddress',
|
||||||
|
listSinceBlock: 'listsinceblock',
|
||||||
|
listTransactions: 'listtransactions',
|
||||||
|
listUnspent: 'listunspent', // bitcoind v0.7.0+
|
||||||
|
lockUnspent: 'lockunspent', // bitcoind v0.8.0+
|
||||||
|
move: 'move',
|
||||||
|
ping: 'ping', // bitcoind v0.9.0+
|
||||||
|
prioritiseTransaction: 'prioritisetransaction', // bitcoind v0.10.0+
|
||||||
|
sendFrom: 'sendfrom',
|
||||||
|
sendMany: 'sendmany',
|
||||||
|
sendRawTransaction: 'sendrawtransaction', // bitcoind v0.7.0+
|
||||||
|
sendToAddress: 'sendtoaddress',
|
||||||
|
setAccount: 'setaccount',
|
||||||
|
setGenerate: 'setgenerate',
|
||||||
|
setTxFee: 'settxfee',
|
||||||
|
signMessage: 'signmessage',
|
||||||
|
signRawTransaction: 'signrawtransaction', // bitcoind v0.7.0+
|
||||||
|
stop: 'stop',
|
||||||
|
submitBlock: 'submitblock', // bitcoind v0.7.0+
|
||||||
|
validateAddress: 'validateaddress',
|
||||||
|
verifyChain: 'verifychain', // bitcoind v0.9.0+
|
||||||
|
verifyMessage: 'verifymessage',
|
||||||
|
verifyTxOutProof: 'verifytxoutproof', // bitcoind v0.11.0+
|
||||||
|
walletLock: 'walletlock',
|
||||||
|
walletPassphrase: 'walletpassphrase',
|
||||||
|
walletPassphraseChange: 'walletpassphrasechange'
|
||||||
|
}
|
61
lightning-backend/src/api/bitcoin/rpc-api/index.ts
Normal file
61
lightning-backend/src/api/bitcoin/rpc-api/index.ts
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
var commands = require('./commands')
|
||||||
|
var rpc = require('./jsonrpc')
|
||||||
|
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
// JsonRPC
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
function Client (opts) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.rpc = new rpc.JsonRPC(opts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
// cmd
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
Client.prototype.cmd = function () {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
var cmd = args.shift()
|
||||||
|
|
||||||
|
callRpc(cmd, args, this.rpc)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
// callRpc
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
function callRpc (cmd, args, rpc) {
|
||||||
|
var fn = args[args.length - 1]
|
||||||
|
|
||||||
|
// If the last argument is a callback, pop it from the args list
|
||||||
|
if (typeof fn === 'function') {
|
||||||
|
args.pop()
|
||||||
|
} else {
|
||||||
|
fn = function () {}
|
||||||
|
}
|
||||||
|
|
||||||
|
return rpc.call(cmd, args, function () {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
// @ts-ignore
|
||||||
|
args.unshift(null)
|
||||||
|
// @ts-ignore
|
||||||
|
fn.apply(this, args)
|
||||||
|
}, function (err) {
|
||||||
|
fn(err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
// Initialize wrappers
|
||||||
|
// ===----------------------------------------------------------------------===//
|
||||||
|
(function () {
|
||||||
|
for (var protoFn in commands) {
|
||||||
|
(function (protoFn) {
|
||||||
|
Client.prototype[protoFn] = function () {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
return callRpc(commands[protoFn], args, this.rpc)
|
||||||
|
}
|
||||||
|
})(protoFn)
|
||||||
|
}
|
||||||
|
})()
|
||||||
|
|
||||||
|
// Export!
|
||||||
|
module.exports.Client = Client;
|
162
lightning-backend/src/api/bitcoin/rpc-api/jsonrpc.ts
Normal file
162
lightning-backend/src/api/bitcoin/rpc-api/jsonrpc.ts
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
var http = require('http')
|
||||||
|
var https = require('https')
|
||||||
|
|
||||||
|
var JsonRPC = function (opts) {
|
||||||
|
// @ts-ignore
|
||||||
|
this.opts = opts || {}
|
||||||
|
// @ts-ignore
|
||||||
|
this.http = this.opts.ssl ? https : http
|
||||||
|
}
|
||||||
|
|
||||||
|
JsonRPC.prototype.call = function (method, params) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
var time = Date.now()
|
||||||
|
var requestJSON
|
||||||
|
|
||||||
|
if (Array.isArray(method)) {
|
||||||
|
// multiple rpc batch call
|
||||||
|
requestJSON = []
|
||||||
|
method.forEach(function (batchCall, i) {
|
||||||
|
requestJSON.push({
|
||||||
|
id: time + '-' + i,
|
||||||
|
method: batchCall.method,
|
||||||
|
params: batchCall.params
|
||||||
|
})
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// single rpc call
|
||||||
|
requestJSON = {
|
||||||
|
id: time,
|
||||||
|
method: method,
|
||||||
|
params: params
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// First we encode the request into JSON
|
||||||
|
requestJSON = JSON.stringify(requestJSON)
|
||||||
|
|
||||||
|
// prepare request options
|
||||||
|
var requestOptions = {
|
||||||
|
host: this.opts.host || 'localhost',
|
||||||
|
port: this.opts.port || 8332,
|
||||||
|
method: 'POST',
|
||||||
|
path: '/',
|
||||||
|
headers: {
|
||||||
|
'Host': this.opts.host || 'localhost',
|
||||||
|
'Content-Length': requestJSON.length
|
||||||
|
},
|
||||||
|
agent: false,
|
||||||
|
rejectUnauthorized: this.opts.ssl && this.opts.sslStrict !== false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.opts.ssl && this.opts.sslCa) {
|
||||||
|
// @ts-ignore
|
||||||
|
requestOptions.ca = this.opts.sslCa
|
||||||
|
}
|
||||||
|
|
||||||
|
// use HTTP auth if user and password set
|
||||||
|
if (this.opts.user && this.opts.pass) {
|
||||||
|
// @ts-ignore
|
||||||
|
requestOptions.auth = this.opts.user + ':' + this.opts.pass
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we'll make a request to the server
|
||||||
|
var cbCalled = false
|
||||||
|
var request = this.http.request(requestOptions)
|
||||||
|
|
||||||
|
// start request timeout timer
|
||||||
|
var reqTimeout = setTimeout(function () {
|
||||||
|
if (cbCalled) return
|
||||||
|
cbCalled = true
|
||||||
|
request.abort()
|
||||||
|
var err = new Error('ETIMEDOUT')
|
||||||
|
// @ts-ignore
|
||||||
|
err.code = 'ETIMEDOUT'
|
||||||
|
reject(err)
|
||||||
|
}, this.opts.timeout || 30000)
|
||||||
|
|
||||||
|
// set additional timeout on socket in case of remote freeze after sending headers
|
||||||
|
request.setTimeout(this.opts.timeout || 30000, function () {
|
||||||
|
if (cbCalled) return
|
||||||
|
cbCalled = true
|
||||||
|
request.abort()
|
||||||
|
var err = new Error('ESOCKETTIMEDOUT')
|
||||||
|
// @ts-ignore
|
||||||
|
err.code = 'ESOCKETTIMEDOUT'
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
request.on('error', function (err) {
|
||||||
|
if (cbCalled) return
|
||||||
|
cbCalled = true
|
||||||
|
clearTimeout(reqTimeout)
|
||||||
|
reject(err)
|
||||||
|
})
|
||||||
|
|
||||||
|
request.on('response', function (response) {
|
||||||
|
clearTimeout(reqTimeout)
|
||||||
|
|
||||||
|
// We need to buffer the response chunks in a nonblocking way.
|
||||||
|
var buffer = ''
|
||||||
|
response.on('data', function (chunk) {
|
||||||
|
buffer = buffer + chunk
|
||||||
|
})
|
||||||
|
// When all the responses are finished, we decode the JSON and
|
||||||
|
// depending on whether it's got a result or an error, we call
|
||||||
|
// emitSuccess or emitError on the promise.
|
||||||
|
response.on('end', function () {
|
||||||
|
var err
|
||||||
|
|
||||||
|
if (cbCalled) return
|
||||||
|
cbCalled = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
var decoded = JSON.parse(buffer)
|
||||||
|
} catch (e) {
|
||||||
|
if (response.statusCode !== 200) {
|
||||||
|
err = new Error('Invalid params, response status code: ' + response.statusCode)
|
||||||
|
err.code = -32602
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
err = new Error('Problem parsing JSON response from server')
|
||||||
|
err.code = -32603
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Array.isArray(decoded)) {
|
||||||
|
decoded = [decoded]
|
||||||
|
}
|
||||||
|
|
||||||
|
// iterate over each response, normally there will be just one
|
||||||
|
// unless a batch rpc call response is being processed
|
||||||
|
decoded.forEach(function (decodedResponse, i) {
|
||||||
|
if (decodedResponse.hasOwnProperty('error') && decodedResponse.error != null) {
|
||||||
|
if (reject) {
|
||||||
|
err = new Error(decodedResponse.error.message || '')
|
||||||
|
if (decodedResponse.error.code) {
|
||||||
|
err.code = decodedResponse.error.code
|
||||||
|
}
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
} else if (decodedResponse.hasOwnProperty('result')) {
|
||||||
|
// @ts-ignore
|
||||||
|
resolve(decodedResponse.result, response.headers)
|
||||||
|
} else {
|
||||||
|
if (reject) {
|
||||||
|
err = new Error(decodedResponse.error.message || '')
|
||||||
|
if (decodedResponse.error.code) {
|
||||||
|
err.code = decodedResponse.error.code
|
||||||
|
}
|
||||||
|
reject(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
request.end(requestJSON);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports.JsonRPC = JsonRPC
|
@ -2,6 +2,28 @@ import logger from '../../logger';
|
|||||||
import DB from '../../database';
|
import DB from '../../database';
|
||||||
|
|
||||||
class ChannelsApi {
|
class ChannelsApi {
|
||||||
|
public async $getAllChannels(): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const query = `SELECT * FROM channels`;
|
||||||
|
const [rows]: any = await DB.query(query);
|
||||||
|
return rows;
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getChannel error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async $getChannelsByStatus(status: number): Promise<any[]> {
|
||||||
|
try {
|
||||||
|
const query = `SELECT * FROM channels WHERE status = ?`;
|
||||||
|
const [rows]: any = await DB.query(query, [status]);
|
||||||
|
return rows;
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$getChannel error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public async $getChannel(shortId: string): Promise<any> {
|
public async $getChannel(shortId: string): Promise<any> {
|
||||||
try {
|
try {
|
||||||
const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE channels.id = ?`;
|
const query = `SELECT n1.alias AS alias_left, n2.alias AS alias_right, channels.* FROM channels LEFT JOIN nodes AS n1 ON n1.public_key = channels.node1_public_key LEFT JOIN nodes AS n2 ON n2.public_key = channels.node2_public_key WHERE channels.id = ?`;
|
||||||
|
@ -19,6 +19,12 @@ interface IConfig {
|
|||||||
TSL_CERT_PATH: string;
|
TSL_CERT_PATH: string;
|
||||||
MACAROON_PATH: string;
|
MACAROON_PATH: string;
|
||||||
};
|
};
|
||||||
|
CORE_RPC: {
|
||||||
|
HOST: string;
|
||||||
|
PORT: number;
|
||||||
|
USERNAME: string;
|
||||||
|
PASSWORD: string;
|
||||||
|
};
|
||||||
DATABASE: {
|
DATABASE: {
|
||||||
HOST: string,
|
HOST: string,
|
||||||
SOCKET: string,
|
SOCKET: string,
|
||||||
@ -48,6 +54,12 @@ const defaults: IConfig = {
|
|||||||
'TSL_CERT_PATH': '',
|
'TSL_CERT_PATH': '',
|
||||||
'MACAROON_PATH': '',
|
'MACAROON_PATH': '',
|
||||||
},
|
},
|
||||||
|
'CORE_RPC': {
|
||||||
|
'HOST': '127.0.0.1',
|
||||||
|
'PORT': 8332,
|
||||||
|
'USERNAME': 'mempool',
|
||||||
|
'PASSWORD': 'mempool'
|
||||||
|
},
|
||||||
'DATABASE': {
|
'DATABASE': {
|
||||||
'HOST': '127.0.0.1',
|
'HOST': '127.0.0.1',
|
||||||
'SOCKET': '',
|
'SOCKET': '',
|
||||||
@ -62,6 +74,7 @@ class Config implements IConfig {
|
|||||||
MEMPOOL: IConfig['MEMPOOL'];
|
MEMPOOL: IConfig['MEMPOOL'];
|
||||||
SYSLOG: IConfig['SYSLOG'];
|
SYSLOG: IConfig['SYSLOG'];
|
||||||
LN_NODE_AUTH: IConfig['LN_NODE_AUTH'];
|
LN_NODE_AUTH: IConfig['LN_NODE_AUTH'];
|
||||||
|
CORE_RPC: IConfig['CORE_RPC'];
|
||||||
DATABASE: IConfig['DATABASE'];
|
DATABASE: IConfig['DATABASE'];
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -69,6 +82,7 @@ class Config implements IConfig {
|
|||||||
this.MEMPOOL = configs.MEMPOOL;
|
this.MEMPOOL = configs.MEMPOOL;
|
||||||
this.SYSLOG = configs.SYSLOG;
|
this.SYSLOG = configs.SYSLOG;
|
||||||
this.LN_NODE_AUTH = configs.LN_NODE_AUTH;
|
this.LN_NODE_AUTH = configs.LN_NODE_AUTH;
|
||||||
|
this.CORE_RPC = configs.CORE_RPC;
|
||||||
this.DATABASE = configs.DATABASE;
|
this.DATABASE = configs.DATABASE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,6 +3,8 @@ import DB from '../database';
|
|||||||
import logger from '../logger';
|
import logger from '../logger';
|
||||||
import lightningApi from '../api/lightning/lightning-api-factory';
|
import lightningApi from '../api/lightning/lightning-api-factory';
|
||||||
import { ILightningApi } from '../api/lightning/lightning-api.interface';
|
import { ILightningApi } from '../api/lightning/lightning-api.interface';
|
||||||
|
import channelsApi from '../api/nodes/channels.api';
|
||||||
|
import bitcoinClient from '../api/bitcoin/bitcoin-client';
|
||||||
|
|
||||||
class NodeSyncService {
|
class NodeSyncService {
|
||||||
constructor() {}
|
constructor() {}
|
||||||
@ -25,15 +27,35 @@ class NodeSyncService {
|
|||||||
await this.$saveNode(node);
|
await this.$saveNode(node);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.$setChannelsInactive();
|
||||||
|
|
||||||
for (const channel of networkGraph.channels) {
|
for (const channel of networkGraph.channels) {
|
||||||
await this.$saveChannel(channel);
|
await this.$saveChannel(channel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
await this.$scanForClosedChannels();
|
||||||
|
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$updateNodes() error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $scanForClosedChannels() {
|
||||||
|
try {
|
||||||
|
const channels = await channelsApi.$getChannelsByStatus(0);
|
||||||
|
for (const channel of channels) {
|
||||||
|
const outspends = await bitcoinClient.getTxOut(channel.transaction_id, channel.transaction_vout);
|
||||||
|
if (outspends === null) {
|
||||||
|
logger.debug('Marking channel: ' + channel.id + ' as closed.');
|
||||||
|
await DB.query(`UPDATE channels SET status = 2 WHERE id = ?`, [channel.id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.err('$updateNodes() error: ' + (e instanceof Error ? e.message : e));
|
logger.err('$updateNodes() error: ' + (e instanceof Error ? e.message : e));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async $saveChannel(channel: ILightningApi.Channel) {
|
private async $saveChannel(channel: ILightningApi.Channel): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const d = new Date(Date.parse(channel.updated_at));
|
const d = new Date(Date.parse(channel.updated_at));
|
||||||
const query = `INSERT INTO channels
|
const query = `INSERT INTO channels
|
||||||
@ -43,6 +65,7 @@ class NodeSyncService {
|
|||||||
transaction_id,
|
transaction_id,
|
||||||
transaction_vout,
|
transaction_vout,
|
||||||
updated_at,
|
updated_at,
|
||||||
|
status,
|
||||||
node1_public_key,
|
node1_public_key,
|
||||||
node1_base_fee_mtokens,
|
node1_base_fee_mtokens,
|
||||||
node1_cltv_delta,
|
node1_cltv_delta,
|
||||||
@ -60,10 +83,11 @@ class NodeSyncService {
|
|||||||
node2_min_htlc_mtokens,
|
node2_min_htlc_mtokens,
|
||||||
node2_updated_at
|
node2_updated_at
|
||||||
)
|
)
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
VALUES (?, ?, ?, ?, ?, 1, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
ON DUPLICATE KEY UPDATE
|
ON DUPLICATE KEY UPDATE
|
||||||
capacity = ?,
|
capacity = ?,
|
||||||
updated_at = ?,
|
updated_at = ?,
|
||||||
|
status = 1,
|
||||||
node1_public_key = ?,
|
node1_public_key = ?,
|
||||||
node1_base_fee_mtokens = ?,
|
node1_base_fee_mtokens = ?,
|
||||||
node1_cltv_delta = ?,
|
node1_cltv_delta = ?,
|
||||||
@ -128,7 +152,15 @@ class NodeSyncService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async $saveNode(node: ILightningApi.Node) {
|
private async $setChannelsInactive(): Promise<void> {
|
||||||
|
try {
|
||||||
|
await DB.query(`UPDATE channels SET status = 0 WHERE status = 1`);
|
||||||
|
} catch (e) {
|
||||||
|
logger.err('$setChannelsInactive() error: ' + (e instanceof Error ? e.message : e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async $saveNode(node: ILightningApi.Node): Promise<void> {
|
||||||
try {
|
try {
|
||||||
const updatedAt = this.utcDateToMysql(node.updated_at);
|
const updatedAt = this.utcDateToMysql(node.updated_at);
|
||||||
const query = `INSERT INTO nodes(
|
const query = `INSERT INTO nodes(
|
||||||
|
Loading…
x
Reference in New Issue
Block a user