-
-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
#824 Core Lightning Auto Withdraw #865
Changes from 7 commits
ffc1f22
ac857ac
3b3cd6c
e8dfac6
e7d2fca
31a98b5
c080981
4571c23
23fc178
1e05fe6
952dfa3
8b97469
e5f6540
a0bc1a0
d2fd823
0975003
faa2873
8aeb41a
9c9af9d
6ff4bd7
8189259
71de2d8
9687ebf
be06681
33dbbcb
016c929
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
import { getGetServerSideProps } from '../../../api/ssrApollo' | ||
import { Form, Input } from '../../../components/form' | ||
import { CenterLayout } from '../../../components/layout' | ||
import { useMe } from '../../../components/me' | ||
import { WalletButtonBar, WalletCard } from '../../../components/wallet-card' | ||
import { useMutation } from '@apollo/client' | ||
import { useToast } from '../../../components/toast' | ||
import { CoreLightningAutowithdrawSchema } from '../../../lib/validate' | ||
import { useRouter } from 'next/router' | ||
import { AutowithdrawSettings, autowithdrawInitial } from '../../../components/autowithdraw-shared' | ||
import { REMOVE_WALLET, UPSERT_WALLET_CORE_LIGHTNING, WALLET_BY_TYPE } from '../../../fragments/wallet' | ||
import Info from '../../../components/info' | ||
import Text from '../../../components/text' | ||
|
||
const variables = { type: 'CORE_LIGHTNING' } | ||
export const getServerSideProps = getGetServerSideProps({ query: WALLET_BY_TYPE, variables, authRequired: true }) | ||
|
||
export default function CoreLightning ({ ssrData }) { | ||
const me = useMe() | ||
const toaster = useToast() | ||
const router = useRouter() | ||
const [upsertWalletCoreLightning] = useMutation(UPSERT_WALLET_CORE_LIGHTNING) | ||
const [removeWallet] = useMutation(REMOVE_WALLET) | ||
|
||
const { walletByType: wallet } = ssrData || {} | ||
|
||
return ( | ||
<CenterLayout> | ||
<h2 className='pb-2'>Core Lightning</h2> | ||
<h6 className='text-muted text-center pb-3'>autowithdraw to your Core Lightning node</h6> | ||
<h6 className='text-muted text-center pb-3'> You must have CLNRest working on your node. <a href='https://docs.corelightning.org/docs/rest\n\n'>More info here.</a></h6> | ||
Comment on lines
+29
to
+31
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
|
||
<Form | ||
initial={{ | ||
socket: wallet?.wallet?.socket || '', | ||
rune: wallet?.wallet?.rune || '', | ||
...autowithdrawInitial({ me, priority: wallet?.priority }) | ||
}} | ||
schema={CoreLightningAutowithdrawSchema({ me })} | ||
onSubmit={async ({ socket, rune, ...settings }) => { | ||
try { | ||
await upsertWalletCoreLightning({ | ||
variables: { | ||
id: wallet?.id, | ||
socket, | ||
rune, | ||
settings: { | ||
...settings, | ||
autoWithdrawThreshold: Number(settings.autoWithdrawThreshold), | ||
autoWithdrawMaxFeePercent: Number(settings.autoWithdrawMaxFeePercent) | ||
} | ||
} | ||
}) | ||
toaster.success('saved settings') | ||
router.push('/settings/wallets') | ||
} catch (err) { | ||
console.error(err) | ||
toaster.danger('failed to attach: ' + err.message || err.toString?.()) | ||
} | ||
}} | ||
> | ||
<Input | ||
label='grpc host and port' | ||
name='socket' | ||
hint='tor or clearnet' | ||
placeholder='55.5.555.55:10001' | ||
clear | ||
required | ||
autoFocus | ||
/> | ||
<Input | ||
label={ | ||
<div className='d-flex align-items-center'>Invoice Only Rune | ||
<Info label='privacy tip'> | ||
<Text> | ||
{"***invoice only rune*** for your convenience. To gain better privacy, generate a new rune as follows:\n\n```lightning-cli createrune restrictions='[[\"method=invoice\"], [\"rate=10\"]]'```\n\nfor older core lightning versions use ```lightning-cli commando-rune restrictions='[[\"method=invoice\"], [\"rate=10\"]]'```"} | ||
</Text> | ||
</Info> | ||
</div> | ||
} | ||
name='macaroon' | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This won't work with this field named incorrectly. |
||
clear | ||
hint='hex or base64 encoded' | ||
placeholder='AgEDbG5kAlgDChCn7YgfWX7uTkQQgXZ2uahNEgEwGhYKB2FkZHJlc3MSBHJlYWQSBXdyaXRlGhcKCGludm9pY2VzEgRyZWFkEgV3cml0ZRoPCgdvbmNoYWluEgRyZWFkAAAGIJkMBrrDV0npU90JV0TGNJPrqUD8m2QYoTDjolaL6eBs' | ||
required | ||
/> | ||
<AutowithdrawSettings /> | ||
<WalletButtonBar | ||
enabled={!!wallet} onDelete={async () => { | ||
try { | ||
await removeWallet({ variables: { id: wallet?.id } }) | ||
toaster.success('saved settings') | ||
router.push('/settings/wallets') | ||
} catch (err) { | ||
console.error(err) | ||
toaster.danger('failed to unattach:' + err.message || err.toString?.()) | ||
} | ||
}} | ||
/> | ||
</Form> | ||
</CenterLayout> | ||
) | ||
} | ||
|
||
export function CoreLightningCard ({ wallet }) { | ||
return ( | ||
<WalletCard | ||
title='Core Lightning' | ||
badges={['receive only', 'non-custodial']} | ||
provider='core-lightning' | ||
enabled={wallet !== undefined || undefined} | ||
/> | ||
) | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
ALTER TYPE "WalletType" ADD VALUE 'CORE_LIGHTNING'; | ||
|
||
CREATE TABLE "WalletCoreLightning" ( | ||
"id" SERIAL NOT NULL, | ||
"walletId" INTEGER NOT NULL, | ||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
"updated_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, | ||
"socket" TEXT NOT NULL, | ||
"rune" TEXT NOT NULL, | ||
|
||
CONSTRAINT "WalletCoreLightning_pkey" PRIMARY KEY ("id") | ||
); | ||
|
||
CREATE UNIQUE INDEX "WalletCoreLightning_walletId_key" ON "WalletCoreLightning"("walletId"); | ||
|
||
ALTER TABLE "WalletCoreLightning" ADD CONSTRAINT "WalletCoreLightning_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE; | ||
|
||
CREATE TRIGGER wallet_core_lightning_as_jsonb | ||
AFTER INSERT OR UPDATE ON "WalletCoreLightning" | ||
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb(); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -53,6 +53,10 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) { | |
await autowithdrawLNAddr( | ||
{ amount, maxFee }, | ||
{ models, me: user, lnd }) | ||
} else if (wallet.type === 'CORE_LIGHTNING') { | ||
await autowithdrawCoreLightning( | ||
{ amount, maxFee }, | ||
{ models, me: user }) | ||
} | ||
|
||
return | ||
|
@@ -124,3 +128,41 @@ async function autowithdrawLND ({ amount, maxFee }, { me, models, lnd }) { | |
|
||
return await createWithdrawal(null, { invoice: invoice.request, maxFee }, { me, models, lnd, autoWithdraw: true }) | ||
} | ||
|
||
async function autowithdrawCoreLightning ({ amount, maxFee }, { me, models }) { | ||
if (!me) { | ||
throw new Error('me not specified') | ||
} | ||
|
||
const wallet = await models.wallet.findFirst({ | ||
where: { | ||
userId: me.id, | ||
type: 'CORE_LIGHTNING' | ||
}, | ||
include: { | ||
walletCoreLightning: true | ||
} | ||
}) | ||
|
||
if (!wallet || !wallet.walletCoreLightning) { | ||
throw new Error('no lightning address wallet found') | ||
} | ||
|
||
const { walletCoreLightning: { rune, socket } } = wallet | ||
const options = { | ||
method: 'POST', | ||
headers: { | ||
'content-type': 'application/json', | ||
Rune: rune | ||
}, | ||
body: JSON.stringify({ | ||
amount_msat: '20', | ||
label: 'Stacker.News AutoWithdrawal', | ||
description: 'Autowithdraw to Core Lightning from SN' | ||
}) | ||
} | ||
|
||
const invoice = await fetch(`${socket}/v1/invoice`, options) | ||
|
||
return await createWithdrawal(null, { invoice: invoice.payment_hash, maxFee }, { me, models, autoWithdraw: true }) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. haven't looked at all the code but you can't pay an invoice hash |
||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I mentioned in the prior review, we do not want credentials that can spend from a stacker's node to be sent to the server (even if we don't store them).
This check should be done on the client like we do with LND and be part of validation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed and moved it to the validator. I'm also using the demo node url for the rune check to decouple the socket and rune from the validation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we be sending runes to blockstream for all of our users? Or is this a placeholder while you work on something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Originally it was a placeholder to make sure it worked so I didn't have to spin up my own core lightning node again. I had to do that because Polar was giving me grief trying to get the rest api working. But I kind of feel like it would be good to decouple the 2. I just pulled that URL from their rest api example page here. But idk what do you think? Also I am working on the statistics page at the moment. I've been doing these 2 at the same time
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's this library but it hasn't been touched in a couple years and the health score looks pretty low
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user I'd be pissed the application I'm using is sending credentials for my money to other people/companies. Wouldn't you?
The healthscore is low for non-security reasons. It's a really simple package with a single function calling a few utility functions that easy to audit.