Skip to content

Commit

Permalink
new interfaces thing: lightningd, eclair, ptarmigan.
Browse files Browse the repository at this point in the history
  • Loading branch information
fiatjaf committed May 1, 2019
1 parent 7db4481 commit 488e29c
Show file tree
Hide file tree
Showing 11 changed files with 465 additions and 120 deletions.
12 changes: 4 additions & 8 deletions src/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,10 @@ import {
MENUITEM_BLOCK,
MENUITEM_GENERATE
} from './constants'
import {rpcCall, sprint, msatsFormat, notify} from './utils'
import {sprint, msatsFormat, notify} from './utils'
import {getBehavior} from './predefined-behaviors'
import * as current from './current-action'
import handleRPC from './interfaces'

// logger service
browser.runtime.onMessage.addListener((message, sender) => {
Expand Down Expand Up @@ -76,16 +77,11 @@ browser.runtime.onMessage.addListener(({setAction, tab}, sender) => {

// do an rpc call on behalf of anyone who wants that -- normally the popup
browser.runtime.onMessage.addListener(
({rpc, method, params, behaviors = {}, extra = {}, tab}, sender) => {
({rpc, behaviors = {}, extra = {}, tab}, sender) => {
if (!rpc) return

tab = sender.tab || tab
let resPromise = rpcCall(method, params).then(res => {
if (res.code) {
throw new Error(res.message || res.code)
}
return res
})
let resPromise = handleRPC(rpc)

resPromise.then(res => {
;(behaviors.success || [])
Expand Down
79 changes: 19 additions & 60 deletions src/components/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,61 +10,14 @@ import {msatsFormat} from '../utils'
export default function Home() {
let {tab} = useContext(CurrentContext)

let [invoices, setInvoices] = useState([])
let [payments, setPayments] = useState([])
let [nodeInfo, setNodeInfo] = useState({})
let [balance, setBalance] = useState(0)
let [summary, setSummary] = useState({})
let [blocked, setBlocked] = useState({})

useEffect(() => {
browser.runtime
.sendMessage({tab, rpc: true, method: 'getinfo'})
.then(({blockheight, id, alias, color, address}) => {
address =
address.length === 0
? null
: `${address[0].address}:${address[0].port}`
setNodeInfo({blockheight, id, alias, color, address})
})
browser.runtime
.sendMessage({tab, rpc: true, method: 'listfunds'})
.then(({channels}) => {
let balance = channels.reduce((acc, ch) => acc + ch.channel_sat, 0)
setBalance(balance)
})
browser.runtime
.sendMessage({tab, rpc: true, method: 'listinvoices'})
.then(resp => {
setInvoices(resp.invoices.filter(inv => inv.status === 'paid'))
})
browser.runtime
.sendMessage({tab, rpc: true, method: 'listpayments'})
.then(resp => {
setPayments(resp.payments.filter(pay => pay.status === 'complete'))
})
browser.runtime.sendMessage({tab, rpc: {summary: []}}).then(setSummary)
browser.runtime.sendMessage({tab, getBlocked: true}).then(setBlocked)
}, [])

let transactions = invoices
.map(({paid_at, expires_at, msatoshi, description = ''}) => ({
date: paid_at || expires_at,
amount: msatoshi,
description
}))
.slice(-15)
.concat(
payments
.map(({created_at, msatoshi, description, payment_preimage}) => ({
date: created_at,
amount: -msatoshi,
description: description || payment_preimage
}))
.slice(-15)
)
.sort((a, b) => a.date - b.date)
.slice(-15)
.reverse()

function unblock(e) {
e.preventDefault()
let domain = e.target.dataset.domain
Expand All @@ -79,12 +32,12 @@ export default function Home() {
return (
<div>
<h1 className="f6 ma3 tc">Balance</h1>
<div className="f5 tc dark-pink b">{balance} satoshis</div>
<div className="f5 tc dark-pink b">{summary.balance} satoshi</div>
<h1 className="f6 ma3 tc">Latest transactions</h1>
<div className="flex justify-center">
<table className="f">
<tbody>
{transactions.map((tx, i) => (
{summary.transactions.map((tx, i) => (
<tr key={i} className="bg-light-yellow hover-bg-light-pink">
<td className="pa1 f7">{formatDate(tx.date)}</td>
<td
Expand All @@ -105,17 +58,23 @@ export default function Home() {
</table>
</div>
<h1 className="f6 ma3 tc">Node</h1>
<div style={{border: `5px solid #${nodeInfo.color}`}} className="pa1 ma2">
<div
style={{border: `5px solid #${summary.info.color}`}}
className="pa1 ma2"
>
<table>
<tbody>
{['alias', 'id', 'address', 'blockheight'].map(attr => (
<tr key={attr}>
<td className="lh-title b tr dark-pink">{attr}</td>
<td className="wrap code lh-copy measure-narrow">
{nodeInfo[attr]}
</td>
</tr>
))}
{['alias', 'id', 'address', 'blockheight']
.map(attr => [attr, summary.info[attr]])
.filter(([_, v]) => v)
.map(([attr, val]) => (
<tr key={attr}>
<td className="lh-title b tr dark-pink">{attr}</td>
<td className="wrap code lh-copy measure-narrow">
{summary.info[attr]}
</td>
</tr>
))}
</tbody>
</table>
</div>
Expand Down
12 changes: 3 additions & 9 deletions src/components/Invoice.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,26 +45,20 @@ export default function Invoice() {
function makeInvoice(e) {
e.preventDefault()

let label = `KwH.${cuid.slug()}`

browser.runtime
.sendMessage({
tab,
rpc: true,
method: 'invoice',
params: [satoshis * 1000, label, desc.replace(/&nbsp;/g, '').trim()],
rpc: {
makeInvoice: [satoshis * 1000, desc.replace(/&nbsp;/g, '').trim()]
},
behaviors: {
success: [
'paste-invoice',
'return-invoice',
'wait-for-invoice',
'cleanup-browser-action',
'save-invoice-to-current-action'
],
failure: ['notify-invoice-error', 'cleanup-browser-action']
},
extra: {
newInvoiceLabel: label
}
})
.then(({bolt11}) => {
Expand Down
16 changes: 8 additions & 8 deletions src/components/Payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default function Payment() {
if (bolt11 === '' || doneTyping === false || paymentPending) return

browser.runtime
.sendMessage({tab, rpc: true, method: 'decodepay', params: [bolt11]})
.sendMessage({tab, rpc: {decode: [bolt11]}})
.then(data => {
setInvoiceData(data)
})
Expand Down Expand Up @@ -56,12 +56,12 @@ export default function Payment() {
browser.runtime
.sendMessage({
tab,
rpc: true,
method: 'pay',
params: {
bolt11,
msatoshi: satoshiActual ? satoshiActual * 1000 : undefined,
label: invoiceData.description
rpc: {
pay: [
bolt11,
satoshiActual ? satoshiActual * 1000 : undefined,
invoiceData.description
]
},
behaviors: {
success: [
Expand Down Expand Up @@ -152,7 +152,7 @@ export default function Payment() {
)}{' '}
to{' '}
<span className="dark-pink hover-gold code b f6">
{invoiceData.payee.slice(0, 4)}{invoiceData.payee.slice(-4)}
{invoiceData.nodeid.slice(0, 4)}{invoiceData.nodeid.slice(-4)}
</span>
{invoiceData.description ? (
<>
Expand Down
3 changes: 1 addition & 2 deletions src/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -130,8 +130,7 @@ if (document) {
switch (type) {
case REQUEST_GETINFO:
return browser.runtime.sendMessage({
rpc: true,
method: 'getinfo'
rpc: {getInfo: []}
})
default:
return null
Expand Down
154 changes: 154 additions & 0 deletions src/interfaces/eclair.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/** @format */

export function summary() {
return Promise.all([
rpcCall('getinfo').then(
({nodeId, alias, blockHeight, publicAddresses}) => ({
blockheight: blockheight,
id: nodeId,
alias,
address: publicAddresses.length === 0 ? null : publicAddresses[0]
})
),
rpcCall('listinvoices'),
rpcCall('listpendinginvoices'),
rpcCall('listpayments'),
rpcCall('channels').then(
channels =>
channels.reduce(
(acc, ch) => acc + ch.data.commitments.localCommit.spec.toLocalMsat,
0
) / 1000
)
]).then(([info, pendingInvoices, allInvoices, balance]) => {
let received = allInvoices
.filter(inv => {
for (let i = 0; i < pendingInvoices.length; i++) {
let pinv = pendingInvoices[i]
if (inv.paymentHash === pinv.paymentHash) {
return false
}
}
return true
})
.slice(0, 15)

return {
info,
balance,
transactions: received.map(({timestamp, amount, description}) => ({
date: timestamp,
amount,
description
}))
}
})
}

export function pay(bolt11, msatoshi = undefined, description = undefined) {
return rpcCall('payinvoice', {invoice: bolt11, amountMsat: msatoshi}).then(
callId => {
return new Promise(resolve => {
eventCallbacks[callId] = ({amount, feesPaid, paymentPreimage}) =>
resolve({
msatoshi_paid: amount,
msatoshi_fees: feesPaid,
preimage: paymentPreimage
})
})
}
)
}

export function decode(bolt11) {
return rpcCall('parseinvoice', {}).then(
({description, amount, nodeId, paymentHash, expiry, timestamp}) => ({
description,
msatoshi: amount,
nodeid: nodeId,
hash: paymentHash,
creation: timestamp,
expiry
})
)
}

export function makeInvoice(msatoshi, description) {
return rpcCall('createinvoice', {
amountMsat: msatoshi,
description
}).then(({serialized, paymentHash}) => ({
bolt11: serialized,
hash: paymentHash
}))
}

var eventCallbacks = {}

export function listenForEvents(defaultCallback) {
return getRpcParams().then(({endpoint, password}) => {
const ws = new WebSocket(endpoint.trim().replace('://', '://:' + password))

ws.onmessage = ev => {
var event
try {
event = JSON.parse(ev.data)
} catch (e) {
console.log('failed to parse websocket event', ev)
return
}

// specific callbacks registered for this event
if (eventCallbacks[event.id]) {
eventCallbacks[event.id](event)
delete eventCallbacks[event.id]
}

// here we send normalized data, not the raw event
switch (event.type) {
case 'payment-received':
defaultCallback({})
}
}

ws.onclose = ev => {
console.log('websocket closed', ev)
listenForEvents(defaultCallback)
}

ws.onerror = ev => {
console.log('error on websocket', err)
listenForEvents(defaultCallback)
}
})
}

function rpcCall(method, params = {}) {
return getRpcParams().then(({endpoint, password}) => {
let formData = new FormData()

for (let k in params) {
if (typeof params[k] === 'undefined') continue

formData.append(k, params[k])
}

return fetch(endpoint.trim() + '/' + method, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
Accept: 'application/json',
Authorization: 'Basic ' + window.btoa(':' + password)
},
body: formData
})
.then(r => r.json())
.then(res => {
if (res.error) {
throw new Error(res.error)
}

return res
})
})
}
21 changes: 21 additions & 0 deletions src/interfaces/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/** @format */

import * as lightningd_spark from './lightningd_spark'
import * as eclair from './eclair'
import * as ptarmigan from './ptarmigan'
import {getRpcParams} from '../utils'

const kinds = {
lightningd_spark,
eclair,
ptarmigan
}

export default function handleRPC(rpcField = {}) {
return getRpcParams().then(({kind}) => {
for (let method in rpcField) {
let args = rpcField[method]
return kinds[kind][method].apply(null, args)
}
})
}
Loading

0 comments on commit 488e29c

Please sign in to comment.