Compare commits
No commits in common. "40131da8bd158296970d604d0a7d046dafe9821d" and "b1c45a4ab37716ca736f69320aa9bfebc4e13656" have entirely different histories.
40131da8bd
...
b1c45a4ab3
@ -137,12 +137,6 @@ STACKER_LND_GRPC_PORT=10010
|
|||||||
STACKER_LND_ADDR=bcrt1qfqau4ug9e6rtrvxrgclg58e0r93wshucumm9vu
|
STACKER_LND_ADDR=bcrt1qfqau4ug9e6rtrvxrgclg58e0r93wshucumm9vu
|
||||||
STACKER_LND_PUBKEY=028093ae52e011d45b3e67f2e0f2cb6c3a1d7f88d2920d408f3ac6db3a56dc4b35
|
STACKER_LND_PUBKEY=028093ae52e011d45b3e67f2e0f2cb6c3a1d7f88d2920d408f3ac6db3a56dc4b35
|
||||||
|
|
||||||
# stacker cln container stuff
|
|
||||||
STACKER_CLN_REST_PORT=9092
|
|
||||||
# docker exec -u clightning stacker_cln lightning-cli newaddr bech32
|
|
||||||
STACKER_CLN_ADDR=bcrt1q02sqd74l4pxedy24fg0qtjz4y2jq7x4lxlgzrx
|
|
||||||
STACKER_CLN_PUBKEY=03ca7acec181dbf5e427c682c4261a46a0dd9ea5f35d97acb094e399f727835b90
|
|
||||||
|
|
||||||
LNCLI_NETWORK=regtest
|
LNCLI_NETWORK=regtest
|
||||||
|
|
||||||
# localstack container stuff
|
# localstack container stuff
|
||||||
|
44
README.md
44
README.md
@ -70,7 +70,6 @@ COMMANDS
|
|||||||
stop stop env
|
stop stop env
|
||||||
restart restart env
|
restart restart env
|
||||||
status status of env
|
status status of env
|
||||||
logs logs from env
|
|
||||||
delete delete env
|
delete delete env
|
||||||
|
|
||||||
sn:
|
sn:
|
||||||
@ -80,10 +79,6 @@ COMMANDS
|
|||||||
fund pay a bolt11 for funding
|
fund pay a bolt11 for funding
|
||||||
withdraw create a bolt11 for withdrawal
|
withdraw create a bolt11 for withdrawal
|
||||||
|
|
||||||
cln:
|
|
||||||
cln_fund pay a bolt11 for funding with CLN
|
|
||||||
cln_withdraw create a bolt11 for withdrawal with CLN
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
psql open psql on db
|
psql open psql on db
|
||||||
prisma run prisma commands
|
prisma run prisma commands
|
||||||
@ -93,10 +88,9 @@ COMMANDS
|
|||||||
lint run linters
|
lint run linters
|
||||||
|
|
||||||
other:
|
other:
|
||||||
compose docker compose passthrough
|
compose docker compose passthrough
|
||||||
sn_lndcli lncli passthrough on sn_lnd
|
sn_lncli lncli passthrough on sn_lnd
|
||||||
stacker_lndcli lncli passthrough on stacker_lnd
|
stacker_lncli lncli passthrough on stacker_lnd
|
||||||
stacker_clncli lightning-cli passthrough on stacker_cln
|
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -104,7 +98,7 @@ COMMANDS
|
|||||||
|
|
||||||
#### Running specific services
|
#### Running specific services
|
||||||
|
|
||||||
By default all services will be run. If you want to exclude specific services from running, set `COMPOSE_PROFILES` to use one or more of `minimal|images|search|payments|email|capture`. To only run mininal services without images, search, or payments:
|
By default all services will be run. If you want to exclude specific services from running, set `COMPOSE_PROFILES` to use one or more of `minimal|images|search|payments|email`. To only run mininal services without images, search, or payments:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
$ COMPOSE_PROFILES=minimal ./sndev start
|
$ COMPOSE_PROFILES=minimal ./sndev start
|
||||||
@ -130,6 +124,7 @@ By default `sndev start` will merge `docker-compose.yml` with `docker-compose.ov
|
|||||||
For example, if you want to replace the db seed with a custom seed file located in `docker/db/another.sql`, you'd create a `docker-compose.override.yml` file with the following:
|
For example, if you want to replace the db seed with a custom seed file located in `docker/db/another.sql`, you'd create a `docker-compose.override.yml` file with the following:
|
||||||
|
|
||||||
```yml
|
```yml
|
||||||
|
version: "3"
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
volumes:
|
volumes:
|
||||||
@ -157,11 +152,6 @@ You can read more about [docker compose override files](https://docs.docker.com/
|
|||||||
- [Responsible disclosure of security or privacy vulnerability awards](#responsible-disclosure-of-security-or-privacy-vulnerability-awards)
|
- [Responsible disclosure of security or privacy vulnerability awards](#responsible-disclosure-of-security-or-privacy-vulnerability-awards)
|
||||||
- [Development documentation awards](#development-documentation-awards)
|
- [Development documentation awards](#development-documentation-awards)
|
||||||
- [Helpfulness awards](#helpfulness-awards)
|
- [Helpfulness awards](#helpfulness-awards)
|
||||||
- [Contribution extras](#contribution-extras)
|
|
||||||
- [Dev chat](#dev-chat)
|
|
||||||
- [Triage permissions](#triage-permissions)
|
|
||||||
- [Contributor badges on SN profiles](#contributor-badges-on-sn-profiles)
|
|
||||||
- [What else you got](#what-else-you-got)
|
|
||||||
- [Development Tips](#development-tips)
|
- [Development Tips](#development-tips)
|
||||||
- [Linting](#linting)
|
- [Linting](#linting)
|
||||||
- [Database migrations](#database-migrations)
|
- [Database migrations](#database-migrations)
|
||||||
@ -237,9 +227,6 @@ _Due to Rule 3, make sure that you mark your PR as a draft when you create it an
|
|||||||
| `priority:high` | 2 |
|
| `priority:high` | 2 |
|
||||||
| `priority:urgent` | 3 |
|
| `priority:urgent` | 3 |
|
||||||
|
|
||||||
### Requesting modifications to reward amounts
|
|
||||||
We try to assign difficulty and priority tags to issues accurately, but we're not perfect. If you believe an issue is mis-tagged, you can request a change to the issue's tags.
|
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
## Code review awards
|
## Code review awards
|
||||||
@ -317,25 +304,6 @@ Like issue specification awards, helping fellow contributors substantially in a
|
|||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
# Contribution extras
|
|
||||||
We want to make contributing to SN as rewarding as possible, so we offer a few extras to contributors.
|
|
||||||
|
|
||||||
## Dev chat
|
|
||||||
We self-host a private chat server for contributors to SN. If you'd like to join, please respond in this [discussion](https://github.com/stackernews/stacker.news/discussions/1059).
|
|
||||||
|
|
||||||
## Triage permissions
|
|
||||||
We offer triage permissions to contributors after they've made a few contributions. I'll usually add them as I notice people contributing, but if I missed you and you'd like to be added, let me know!
|
|
||||||
|
|
||||||
## Contributor badges on SN profiles
|
|
||||||
Contributors can get badges on their SN profiles by opening a pull request adding their SN nym to the [contributors.txt](/contributors.txt) file.
|
|
||||||
|
|
||||||
## What else you got
|
|
||||||
In the future we plan to offer more, like gratis github copilot subscriptions, reverse tunnels, codespaces, and merch.
|
|
||||||
|
|
||||||
If you'd like to see something added, please make a suggestion.
|
|
||||||
|
|
||||||
<br>
|
|
||||||
|
|
||||||
# Development Tips
|
# Development Tips
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
@ -464,7 +432,7 @@ To ensure stackers balances are kept sane, all wallet updates are run in [serial
|
|||||||
<br>
|
<br>
|
||||||
|
|
||||||
# Need help?
|
# Need help?
|
||||||
Open a [discussion](http://github.com/stackernews/stacker.news/discussions) or [issue](http://github.com/stackernews/stacker.news/issues/new) or [email us](mailto:kk@stacker.news) or request joining the [dev chat](#dev-chat).
|
Open a [discussion](http://github.com/stackernews/stacker.news/discussions) or [issue](http://github.com/stackernews/stacker.news/issues/new) or [email us](mailto:kk@stacker.news) or [chat with us on telegram](https://t.me/stackernews).
|
||||||
|
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
@ -7,12 +7,11 @@ import lnpr from 'bolt11'
|
|||||||
import { SELECT } from './item'
|
import { SELECT } from './item'
|
||||||
import { lnAddrOptions } from '@/lib/lnurl'
|
import { lnAddrOptions } from '@/lib/lnurl'
|
||||||
import { msatsToSats, msatsToSatsDecimal, ensureB64 } from '@/lib/format'
|
import { msatsToSats, msatsToSatsDecimal, ensureB64 } from '@/lib/format'
|
||||||
import { CLNAutowithdrawSchema, LNDAutowithdrawSchema, amountSchema, lnAddrAutowithdrawSchema, lnAddrSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
|
import { LNDAutowithdrawSchema, amountSchema, lnAddrAutowithdrawSchema, lnAddrSchema, ssValidate, withdrawlSchema } from '@/lib/validate'
|
||||||
import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, ANON_USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT } from '@/lib/constants'
|
import { ANON_BALANCE_LIMIT_MSATS, ANON_INV_PENDING_LIMIT, ANON_USER_ID, BALANCE_LIMIT_MSATS, INVOICE_RETENTION_DAYS, INV_PENDING_LIMIT, USER_IDS_BALANCE_NO_LIMIT } from '@/lib/constants'
|
||||||
import { datePivot } from '@/lib/time'
|
import { datePivot } from '@/lib/time'
|
||||||
import assertGofacYourself from './ofac'
|
import assertGofacYourself from './ofac'
|
||||||
import assertApiKeyNotPermitted from './apiKey'
|
import assertApiKeyNotPermitted from './apiKey'
|
||||||
import { createInvoice as createInvoiceCLN } from '@/lib/cln'
|
|
||||||
|
|
||||||
export async function getInvoice (parent, { id }, { me, models, lnd }) {
|
export async function getInvoice (parent, { id }, { me, models, lnd }) {
|
||||||
const inv = await models.invoice.findUnique({
|
const inv = await models.invoice.findUnique({
|
||||||
@ -324,7 +323,7 @@ export default {
|
|||||||
},
|
},
|
||||||
WalletDetails: {
|
WalletDetails: {
|
||||||
__resolveType (wallet) {
|
__resolveType (wallet) {
|
||||||
return wallet.address ? 'WalletLNAddr' : wallet.macaroon ? 'WalletLND' : 'WalletCLN'
|
return wallet.address ? 'WalletLNAddr' : 'WalletLND'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Mutation: {
|
Mutation: {
|
||||||
@ -467,36 +466,6 @@ export default {
|
|||||||
},
|
},
|
||||||
{ settings, data }, { me, models })
|
{ settings, data }, { me, models })
|
||||||
},
|
},
|
||||||
upsertWalletCLN: async (parent, { settings, ...data }, { me, models }) => {
|
|
||||||
data.cert = ensureB64(data.cert)
|
|
||||||
|
|
||||||
const wallet = 'walletCLN'
|
|
||||||
return await upsertWallet(
|
|
||||||
{
|
|
||||||
schema: CLNAutowithdrawSchema,
|
|
||||||
walletName: wallet,
|
|
||||||
walletType: 'CLN',
|
|
||||||
testConnect: async ({ socket, rune, cert }) => {
|
|
||||||
try {
|
|
||||||
const inv = await createInvoiceCLN({
|
|
||||||
socket,
|
|
||||||
rune,
|
|
||||||
cert,
|
|
||||||
description: 'SN connection test',
|
|
||||||
msats: 'any',
|
|
||||||
expiry: 0
|
|
||||||
})
|
|
||||||
await addWalletLog({ wallet, level: 'SUCCESS', message: 'connected to CLN' }, { me, models })
|
|
||||||
return inv
|
|
||||||
} catch (err) {
|
|
||||||
const details = err.details || err.message || err.toString?.()
|
|
||||||
await addWalletLog({ wallet, level: 'ERROR', message: `could not connect to CLN: ${details}` }, { me, models })
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ settings, data }, { me, models })
|
|
||||||
},
|
|
||||||
upsertWalletLNAddr: async (parent, { settings, ...data }, { me, models }) => {
|
upsertWalletLNAddr: async (parent, { settings, ...data }, { me, models }) => {
|
||||||
const wallet = 'walletLightningAddress'
|
const wallet = 'walletLightningAddress'
|
||||||
return await upsertWallet(
|
return await upsertWallet(
|
||||||
@ -526,8 +495,6 @@ export default {
|
|||||||
let walletName = ''
|
let walletName = ''
|
||||||
if (wallet.type === 'LND') {
|
if (wallet.type === 'LND') {
|
||||||
walletName = 'walletLND'
|
walletName = 'walletLND'
|
||||||
} else if (wallet.type === 'CLN') {
|
|
||||||
walletName = 'walletCLN'
|
|
||||||
} else if (wallet.type === 'LIGHTNING_ADDRESS') {
|
} else if (wallet.type === 'LIGHTNING_ADDRESS') {
|
||||||
walletName = 'walletLightningAddress'
|
walletName = 'walletLightningAddress'
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ export default gql`
|
|||||||
cancelInvoice(hash: String!, hmac: String!): Invoice!
|
cancelInvoice(hash: String!, hmac: String!): Invoice!
|
||||||
dropBolt11(id: ID): Withdrawl
|
dropBolt11(id: ID): Withdrawl
|
||||||
upsertWalletLND(id: ID, socket: String!, macaroon: String!, cert: String, settings: AutowithdrawSettings!): Boolean
|
upsertWalletLND(id: ID, socket: String!, macaroon: String!, cert: String, settings: AutowithdrawSettings!): Boolean
|
||||||
upsertWalletCLN(id: ID, socket: String!, rune: String!, cert: String, settings: AutowithdrawSettings!): Boolean
|
|
||||||
upsertWalletLNAddr(id: ID, address: String!, settings: AutowithdrawSettings!): Boolean
|
upsertWalletLNAddr(id: ID, address: String!, settings: AutowithdrawSettings!): Boolean
|
||||||
removeWallet(id: ID!): Boolean
|
removeWallet(id: ID!): Boolean
|
||||||
}
|
}
|
||||||
@ -43,13 +42,7 @@ export default gql`
|
|||||||
cert: String
|
cert: String
|
||||||
}
|
}
|
||||||
|
|
||||||
type WalletCLN {
|
union WalletDetails = WalletLNAddr | WalletLND
|
||||||
socket: String!
|
|
||||||
rune: String!
|
|
||||||
cert: String
|
|
||||||
}
|
|
||||||
|
|
||||||
union WalletDetails = WalletLNAddr | WalletLND | WalletCLN
|
|
||||||
|
|
||||||
input AutowithdrawSettings {
|
input AutowithdrawSettings {
|
||||||
autoWithdrawThreshold: Int!
|
autoWithdrawThreshold: Int!
|
||||||
|
@ -50,6 +50,3 @@ benalleng,pr,#1050,,good-first-issue,,,,20k,???,???
|
|||||||
jp30566347,pr,#1055,#771,medium,,,extra mile,300k,jpmelanson@getalby.com,2024-04-12
|
jp30566347,pr,#1055,#771,medium,,,extra mile,300k,jpmelanson@getalby.com,2024-04-12
|
||||||
benalleng,helpfulness,#1063,202,medium,,,did much of the legwork in another pr,100k,???,???
|
benalleng,helpfulness,#1063,202,medium,,,did much of the legwork in another pr,100k,???,???
|
||||||
benalleng,code review,#1063,202,medium,,,,25k,???,???
|
benalleng,code review,#1063,202,medium,,,,25k,???,???
|
||||||
benalleng,pr,#1066,#1060,good-first-issue,,,,20k,???,???
|
|
||||||
benalleng,pr,#1068,#1067,good-first-issue,,,,20k,???,???
|
|
||||||
abhiShandy,helpfulness,#1068,#1067,good-first-issue,,,,2k,abhishandy@stacker.news,2024-04-14
|
|
||||||
|
|
@ -8,7 +8,7 @@ export default function LogMessage ({ wallet, level, message, ts }) {
|
|||||||
<tr className={styles.line}>
|
<tr className={styles.line}>
|
||||||
<td className={styles.timestamp}>{timeSince(new Date(ts))}</td>
|
<td className={styles.timestamp}>{timeSince(new Date(ts))}</td>
|
||||||
<td className={styles.wallet}>[{wallet}]</td>
|
<td className={styles.wallet}>[{wallet}]</td>
|
||||||
<td className={`${styles.level} ${levelClassName}`}>{level === 'success' ? 'ok' : level}</td>
|
<td className={`${styles.level} ${levelClassName}`}>{level}</td>
|
||||||
<td>{message}</td>
|
<td>{message}</td>
|
||||||
</tr>
|
</tr>
|
||||||
)
|
)
|
||||||
|
@ -160,7 +160,6 @@ const initIndexedDB = async (storeName) => {
|
|||||||
const renameWallet = (wallet) => {
|
const renameWallet = (wallet) => {
|
||||||
if (wallet === 'walletLightningAddress') return 'lnAddr'
|
if (wallet === 'walletLightningAddress') return 'lnAddr'
|
||||||
if (wallet === 'walletLND') return 'lnd'
|
if (wallet === 'walletLND') return 'lnd'
|
||||||
if (wallet === 'walletCLN') return 'cln'
|
|
||||||
return wallet
|
return wallet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
version: "3"
|
||||||
services:
|
services:
|
||||||
db:
|
db:
|
||||||
container_name: db
|
container_name: db
|
||||||
@ -252,17 +253,14 @@ services:
|
|||||||
bash -c '
|
bash -c '
|
||||||
blockcount=$$(bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} getblockcount 2>/dev/null)
|
blockcount=$$(bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} getblockcount 2>/dev/null)
|
||||||
if (( blockcount <= 0 )); then
|
if (( blockcount <= 0 )); then
|
||||||
echo "Mining 10 blocks to sn_lnd, stacker_lnd, stacker_cln..."
|
echo "Mining 10 blocks to sn_lnd and stacker_lnd..."
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 100 ${LND_ADDR}
|
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 100 ${LND_ADDR}
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 100 ${STACKER_LND_ADDR}
|
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 100 ${STACKER_LND_ADDR}
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 100 ${STACKER_CLN_ADDR}
|
|
||||||
else
|
else
|
||||||
echo "Mining a block to sn_lnd... ${LND_ADDR}"
|
echo "Mining a block to sn_lnd... ${LND_ADDR}"
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 1 ${LND_ADDR}
|
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 1 ${LND_ADDR}
|
||||||
echo "Mining a block to stacker_lnd... ${STACKER_LND_ADDR}"
|
echo "Mining a block to stacker_lnd... ${STACKER_LND_ADDR}"
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 1 ${STACKER_LND_ADDR}
|
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 1 ${STACKER_LND_ADDR}
|
||||||
echo "Mining a block to stacker_cln... ${STACKER_CLN_ADDR}"
|
|
||||||
bitcoin-cli -chain=regtest -rpcport=${RPC_PORT} -rpcuser=${RPC_USER} -rpcpassword=${RPC_PASS} generatetoaddress 1 ${STACKER_CLN_ADDR}
|
|
||||||
fi
|
fi
|
||||||
'
|
'
|
||||||
sn_lnd:
|
sn_lnd:
|
||||||
@ -383,8 +381,8 @@ services:
|
|||||||
- stacker_lnd:/home/lnd/.lnd
|
- stacker_lnd:/home/lnd/.lnd
|
||||||
labels:
|
labels:
|
||||||
ofelia.enabled: "true"
|
ofelia.enabled: "true"
|
||||||
ofelia.job-exec.stacker_lnd_channel_cron.schedule: "@every 1m"
|
ofelia.job-exec.stacker_channel_cron.schedule: "@every 1m"
|
||||||
ofelia.job-exec.stacker_lnd_channel_cron.command: >
|
ofelia.job-exec.stacker_channel_cron.command: >
|
||||||
su lnd -c bash -c "
|
su lnd -c bash -c "
|
||||||
if [ $$(lncli getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
|
if [ $$(lncli getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
|
||||||
exit 0
|
exit 0
|
||||||
@ -393,55 +391,6 @@ services:
|
|||||||
--min_confs 0 --local_amt=1000000000 --push_amt=500000000
|
--min_confs 0 --local_amt=1000000000 --push_amt=500000000
|
||||||
fi
|
fi
|
||||||
"
|
"
|
||||||
stacker_cln:
|
|
||||||
build:
|
|
||||||
context: ./docker/cln
|
|
||||||
container_name: stacker_cln
|
|
||||||
restart: unless-stopped
|
|
||||||
profiles:
|
|
||||||
- payments
|
|
||||||
healthcheck:
|
|
||||||
test: ["CMD-SHELL", "su clightning -c 'lightning-cli --network=regtest getinfo'"]
|
|
||||||
interval: 10s
|
|
||||||
timeout: 10s
|
|
||||||
retries: 10
|
|
||||||
start_period: 1m
|
|
||||||
depends_on:
|
|
||||||
bitcoin:
|
|
||||||
condition: service_healthy
|
|
||||||
restart: true
|
|
||||||
env_file:
|
|
||||||
- .env.development
|
|
||||||
command:
|
|
||||||
- 'lightningd'
|
|
||||||
- '--network=regtest'
|
|
||||||
- '--alias=stacker_cln'
|
|
||||||
- '--bitcoin-rpcconnect=bitcoin'
|
|
||||||
- '--bitcoin-rpcuser=${RPC_USER}'
|
|
||||||
- '--bitcoin-rpcpassword=${RPC_PASS}'
|
|
||||||
- '--large-channels'
|
|
||||||
- '--rest-port=3010'
|
|
||||||
- '--rest-host=0.0.0.0'
|
|
||||||
- '--log-file=/home/clightning/.lightning/debug.log'
|
|
||||||
expose:
|
|
||||||
- "9735"
|
|
||||||
ports:
|
|
||||||
- "${STACKER_CLN_REST_PORT}:3010"
|
|
||||||
volumes:
|
|
||||||
- stacker_cln:/home/clightning/.lightning
|
|
||||||
labels:
|
|
||||||
ofelia.enabled: "true"
|
|
||||||
ofelia.job-exec.stacker_cln_channel_cron.schedule: "@every 1m"
|
|
||||||
ofelia.job-exec.stacker_cln_channel_cron.command: >
|
|
||||||
su clightning -c bash -c "
|
|
||||||
if [ $$(lightning-cli --regtest getinfo | jq '.num_active_channels + .num_pending_channels') -ge 3 ]; then
|
|
||||||
exit 0
|
|
||||||
else
|
|
||||||
lightning-cli --regtest connect $LND_PUBKEY@sn_lnd:9735
|
|
||||||
lightning-cli --regtest fundchannel id=$LND_PUBKEY feerate=1000perkb \\
|
|
||||||
amount=1000000000 push_msat=500000000000 minconf=0
|
|
||||||
fi
|
|
||||||
"
|
|
||||||
channdler:
|
channdler:
|
||||||
image: mcuadros/ofelia:latest
|
image: mcuadros/ofelia:latest
|
||||||
container_name: channdler
|
container_name: channdler
|
||||||
@ -478,5 +427,4 @@ volumes:
|
|||||||
bitcoin:
|
bitcoin:
|
||||||
sn_lnd:
|
sn_lnd:
|
||||||
stacker_lnd:
|
stacker_lnd:
|
||||||
stacker_cln:
|
|
||||||
s3:
|
s3:
|
||||||
|
@ -1,16 +0,0 @@
|
|||||||
FROM polarlightning/clightning:23.08
|
|
||||||
|
|
||||||
RUN apt-get update -y \
|
|
||||||
&& apt-get install -y jq wget \
|
|
||||||
&& apt-get clean \
|
|
||||||
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
|
|
||||||
|
|
||||||
RUN wget https://raw.githubusercontent.com/ElementsProject/lightning/v23.08/plugins/clnrest/requirements.txt \
|
|
||||||
&& pip install -r requirements.txt
|
|
||||||
|
|
||||||
# make sure that wallet and identity is persisted across rebuilds.
|
|
||||||
# server certificates contain stacker_lnd as a custom domain.
|
|
||||||
# see https://docs.corelightning.org/docs/grpc#generating-custom-certificates-optional
|
|
||||||
# since CLNRest in CLNv23.08 seems to use client certificates, they also contain stacker_lnd as a custom domain.
|
|
||||||
# see https://github.com/ElementsProject/lightning/tree/v23.08/plugins/clnrest#configuration
|
|
||||||
COPY ["./hsm_secret", "./ca-key.pem", "./ca.pem", "./server-key.pem", "./server.pem", "./client-key.pem", "./client.pem", "/home/clightning/.lightning/regtest/"]
|
|
@ -1,5 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgXhrU/UORcVCRnDbo
|
|
||||||
KF35C+BM3FIlCvBX0Y5J2J9piE+hRANCAAQY5mh7rpXIMEhjp5SwB2EJT0kMBlwz
|
|
||||||
SVsn81a1fYUxWgUXUJNWLnLo00PQKq5xCKxXc9fzs/tl1w+oANsTp2/u
|
|
||||||
-----END PRIVATE KEY-----
|
|
@ -1,10 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIIBcjCCARigAwIBAgIJANrSvPZ/Y3KEMAoGCCqGSM49BAMCMBYxFDASBgNVBAMM
|
|
||||||
C2NsbiBSb290IENBMCAXDTc1MDEwMTAwMDAwMFoYDzQwOTYwMTAxMDAwMDAwWjAW
|
|
||||||
MRQwEgYDVQQDDAtjbG4gUm9vdCBDQTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IA
|
|
||||||
BBjmaHuulcgwSGOnlLAHYQlPSQwGXDNJWyfzVrV9hTFaBRdQk1YucujTQ9AqrnEI
|
|
||||||
rFdz1/Oz+2XXD6gA2xOnb+6jTTBLMBkGA1UdEQQSMBCCA2NsboIJbG9jYWxob3N0
|
|
||||||
MB0GA1UdDgQWBBSEcmN/9rzS2hR6G7EIgsX+51N0CjAPBgNVHRMBAf8EBTADAQH/
|
|
||||||
MAoGCCqGSM49BAMCA0gAMEUCIHCePvNSvyiBYawqKgQquw9J2WVyJxn2MIYIqz9S
|
|
||||||
QL18AiEAh8BVDz8pX7Nsll8sb0bO0RZ49cvqQocCgVabqJuSuik=
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -1 +0,0 @@
|
|||||||
70A2D30FE991B24B5A6BF85421BE5EF083665E7E
|
|
@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDeBF5jfabczcaz
|
|
||||||
VnYFEB9botC3TevcCpyucbLmCPFU3dcXjUdDJFfnQDlbIAm9UqbUwIF0GIRRJ1Il
|
|
||||||
H5mZ3MxXVGMNHlLuLdfVMfJFk72WczPecUPh5jJea+ujdW4I5Q+kgx/CjVatPHz5
|
|
||||||
WYgKC+6d10tawYLR47RRZhOgzAdrCD//L3AE+WPcdWicRSJoYDxdmyQiF9X9Vz6X
|
|
||||||
NQuOExYk3etHzwEf8alnMZbzS7j10//QxaxR/Ujp4tgt+ACwxNLd7TdoYZ8FP4AY
|
|
||||||
1ijabYxLGUA+Mk0rC6Al36EE78dUg1fTDCh+ee1YAnoYgnT7jrUQ+6pbiwmfjrO0
|
|
||||||
lHVDJ+CpAgMBAAECggEABFgj35KezaNjKbEWljkb0U6bO7L/zRxK8AVa+od0KB9b
|
|
||||||
XvGNfSVY9zYrOuyGELWX7nOz33CF0Wh8LJ4fZkKgZvxk5x6lLAC5Tp7vhYR1LNxs
|
|
||||||
VpJzJN+gbMxz6/6CFYaVeQ2RWLHzpmhCUf/dxVJVc0Dk8xTX8jLngN4/972SwLKJ
|
|
||||||
krM2bdAh9crNwi4IKiwnNZKIZA9mZRwSBkpvScV0WSE7JdBT/j4JgTRJY8ya3svo
|
|
||||||
rc7Lb2b/IeUfJDRbGKW5+5ST3FOUpfXtkHKv+Pg0QYAvdnYLbaI/xaGJkerqE9I8
|
|
||||||
gXXm5kc39N/PE3LCCLrst7mk/xbJAR9BVGylU81gRwKBgQD1rW1qBce0M1VuuI+i
|
|
||||||
yoDAYDP7qL4KW/1YnQP377mRThCPu8vKt6EqOUa3JuMWk7o2pYSgUnawWMuVkmDl
|
|
||||||
1szCHojw1YNblRoO5Qodw8duYE81wXAgAZq3NRE1EBffOAMTo61K63MLq2AxepjW
|
|
||||||
visVx4F/5bKZ/lD2kkiQpQwpUwKBgQDnWHGgHX3sL1gHLdiKvnLYFX+b1DvtQ/zm
|
|
||||||
jz5vmDZtWtyyGUD4Hul77RGxrbB3Yhbfulvkgp3yCFb3o1T32XYBHYxhGVQ54YfP
|
|
||||||
JtwYjd5sGNCofEIJpeVBjgdgDeZWF6AKqgQEw+D1/uIE5E2lZT7HeI+0HL6LIWIo
|
|
||||||
gj6W+RCCkwKBgHLqd1Zzc7FPnbOXsuAjtsvFdCtQB+ySkNOlRljwEi3shQSmhDHD
|
|
||||||
aSh1+CTtlKVX3m93Rq0zRX9BWaESAi8gJVDbtZRpWvM4sCKtcejwTdXMSODNJaRi
|
|
||||||
+7qcoPrgFzp7Wb0S/5kevwaDWBBs1xcDhuW+F0365GrxsW9Uh4rZGPIvAoGAewYi
|
|
||||||
bnYgd4/5rN+pbqa2ZciQ8qobMCJeg7EbD7cPAno2MJOTZB70JM2+AhGObP4BkfoF
|
|
||||||
UfBP09yxesEltyOySAeRljUlAB653OQaWQhghnVvyJlDeOP6lTDVJTRfD9tCZUli
|
|
||||||
F7Kel9JyGQ3baJ/9kY/AQ5Shk1UuYMJaTGioafcCgYEA4DgJWHRbih/KGq+KxCmI
|
|
||||||
ebFabuGr/XRKaQ5NlnUtPnSOPnfFxSKU+7egKmM4fA4i+wiKwWrsfxIXsgkDGKDc
|
|
||||||
6T0s9Zf6uGwS+be3g494WoaPKuiwAuutkkjhWQtXAMLsoLl8PAyZsyet4bsyTmPv
|
|
||||||
9BnJIn6TN2lFZl6yNePe5S0=
|
|
||||||
-----END PRIVATE KEY-----
|
|
@ -1,17 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE REQUEST-----
|
|
||||||
MIICoDCCAYgCAQAwWzELMAkGA1UEBhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
|
||||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLc3Rh
|
|
||||||
Y2tlcl9jbG4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDeBF5jfabc
|
|
||||||
zcazVnYFEB9botC3TevcCpyucbLmCPFU3dcXjUdDJFfnQDlbIAm9UqbUwIF0GIRR
|
|
||||||
J1IlH5mZ3MxXVGMNHlLuLdfVMfJFk72WczPecUPh5jJea+ujdW4I5Q+kgx/CjVat
|
|
||||||
PHz5WYgKC+6d10tawYLR47RRZhOgzAdrCD//L3AE+WPcdWicRSJoYDxdmyQiF9X9
|
|
||||||
Vz6XNQuOExYk3etHzwEf8alnMZbzS7j10//QxaxR/Ujp4tgt+ACwxNLd7TdoYZ8F
|
|
||||||
P4AY1ijabYxLGUA+Mk0rC6Al36EE78dUg1fTDCh+ee1YAnoYgnT7jrUQ+6pbiwmf
|
|
||||||
jrO0lHVDJ+CpAgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAs3mdbEeLipHKeGZO
|
|
||||||
BbZ9vz7Ye0hRMX3q4liSBwGbHm/ByUqtzcXKf+R2T0+5dfc2PJNd7uRxWlRPSDdV
|
|
||||||
ebHm5ctZaDIGUS2XQrj67KoRRtZTLM4IGM2g5ppbgnm9NM6NWYpO9t2hKupIqNa+
|
|
||||||
ATYNOzxLHZQJRTvxhC0kpy196huw/vs4d7TVPF7SxJVsXPmjt1OGso52HL0HjDsD
|
|
||||||
BcNW2Qtd9WcrEwM8HmydBJuSru5gX7HHgKMHUmPtyvFXdTjiiqFnU1apTmF8ptY5
|
|
||||||
tvNtz8pGmb3p0lvkyAUaEurtzNChwIdU5rvkkZGC8sphpMECPEmMgpavkkZK3+EJ
|
|
||||||
aUwdqQ==
|
|
||||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICfzCCAiagAwIBAgIUcKLTD+mRsktaa/hUIb5e8INmXn4wCgYIKoZIzj0EAwIw
|
|
||||||
FjEUMBIGA1UEAwwLY2xuIFJvb3QgQ0EwHhcNMjQwNDExMTU1ODQ2WhcNMzQwNDA5
|
|
||||||
MTU1ODQ2WjBbMQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
|
|
||||||
A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtzdGFja2Vy
|
|
||||||
X2NsbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAN4EXmN9ptzNxrNW
|
|
||||||
dgUQH1ui0LdN69wKnK5xsuYI8VTd1xeNR0MkV+dAOVsgCb1SptTAgXQYhFEnUiUf
|
|
||||||
mZnczFdUYw0eUu4t19Ux8kWTvZZzM95xQ+HmMl5r66N1bgjlD6SDH8KNVq08fPlZ
|
|
||||||
iAoL7p3XS1rBgtHjtFFmE6DMB2sIP/8vcAT5Y9x1aJxFImhgPF2bJCIX1f1XPpc1
|
|
||||||
C44TFiTd60fPAR/xqWcxlvNLuPXT/9DFrFH9SOni2C34ALDE0t3tN2hhnwU/gBjW
|
|
||||||
KNptjEsZQD4yTSsLoCXfoQTvx1SDV9MMKH557VgCehiCdPuOtRD7qluLCZ+Os7SU
|
|
||||||
dUMn4KkCAwEAAaNCMEAwHQYDVR0OBBYEFE15SBJ5Z/50fktS1qs9agwhQ40PMB8G
|
|
||||||
A1UdIwQYMBaAFIRyY3/2vNLaFHobsQiCxf7nU3QKMAoGCCqGSM49BAMCA0cAMEQC
|
|
||||||
IGSPak0NLkIDa1Dyw/NNWeBf+PxLysd9tCIPmMF7YmN3AiAZcdXrWldVW/9RLRyT
|
|
||||||
0RU2Uqr/47R+MADhX961ZlfzfQ==
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -1 +0,0 @@
|
|||||||
$ְDMBֶװהמ$«bד†y5ת<35>$E0”<30>°P·§)ּ
|
|
@ -1,28 +0,0 @@
|
|||||||
-----BEGIN PRIVATE KEY-----
|
|
||||||
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGJ+Q2predhnnY
|
|
||||||
roMhBgQNWt3OvY49M6WC2u+P2HWj3+pjjt6oNaM8emYqRXTh4WlAyi8G/XNkADLv
|
|
||||||
Zf4HMzBp5kYtCemtzce6Zt1LE45Xd7xdFDca7PLOTFaaNY99AENBbaJf0s5ztJSz
|
|
||||||
IfBisPfhvDwOhbwJB5KZ0ezaa/RItYcOdkZeggbgoYeDA63L+fHTX5Y/Rt9Aooo9
|
|
||||||
VDix6y1pGMa53jVxdGvj1bb5hkqPtr5zi6hwIum1YDWWwp7wMkft0pnvrnwMQ9mk
|
|
||||||
A+Pxd8pMie5I5Ks+RPjYk2W8faEccwgN0DXhOBTnxwmdKxVnq+5nU6neZ2d1pRel
|
|
||||||
rklppay9AgMBAAECggEATWyyz+POZL9xhoeRdurJ1IoHlssb86/lYL640gSq2pAY
|
|
||||||
HjRprWHf2TaeCrA+3i9cF9OoElwfpRgqzr2URy3qIca27swrwRxhiOS+XKJUgLqp
|
|
||||||
H9lROrUQnijXwcNhwF7E6KC0zCorPqx1WZTOP1GUWWBaOvZoJUMPNgj/Oczqkymh
|
|
||||||
W0w1+8cHAlQLs5756xL7lvwUk6sLUXWEEYHv2cW3Q8n7c1y41GQUuVnwrZJGqXDM
|
|
||||||
er0TGDGeBjahl7RyC+Nd7aA6W1HW8pELI0lAkkiZkRoSw4urG5RE2al9SqtkbiUi
|
|
||||||
s8ntp1LUk43/mnWgcJ9dLknNiYogNYOb8EdI4OF+xQKBgQD9BbeReC5cP4L9ZpF9
|
|
||||||
Bzaf1K2dxhJivkzScNjX6ciMV/U8jppHAz7BSFbO7gPczsdk5zQaqPy/A0uhGKhy
|
|
||||||
74nB+0kguHegASjzqGWpRh6W/CRWFuiktoulS7eSMUqEiJ8OeKpE2Am3I+ltlOyH
|
|
||||||
gRWdHiEOmZMlWGcrgY3yELlRewKBgQDIfOClARHERNBhbPKAAaW2NTQekMhJPPSZ
|
|
||||||
aCY6TiIJ+SSWSEF1ythe7HyoIJgcR10UobXrcIuv1LbgqxwLOmsaE7TU8fOGeQPe
|
|
||||||
HJrr589o3MWmS5K6TvxVcL40dKYjoVGVBVdahkWmoZ6JALlfKTiOHN62EW/CHOJA
|
|
||||||
wlSyADvZJwKBgQDc09aIwalEnbHHU3N6+Ya1LDtyzeJSB+CochDvMHz17/Z7KcKA
|
|
||||||
Y9arfmU1KQp59oaUDC2vbvlYBJpHOWwbE/DZOmVyh0zwetKxBbHkcOxVvi5AbLIS
|
|
||||||
v7dVRqYqk5aD4XFggfOpLhwcmN0r5KQjB4hDnn4fbe281FEG6YVnVS1IbQKBgQCP
|
|
||||||
WVKaSEB20Ckab/aX9hWRSUtBy42ZaB8QDPrAV5tY/C3f0jwTx/ybKoYbBGseVRxF
|
|
||||||
ozZa6DbIetRjoZTEpnlrxMlYNMNF1AMi7dsLb8zKEoiz1XdNBSrAwIMPKJSeBzs4
|
|
||||||
zP/fdwAYG5kqJj1kwCly20uWbLM23MYdPZWnTCl+owKBgFC6Vjw1fPqp4yi8dgZ4
|
|
||||||
FYpxniLQFgj3wjQiIZbGlN0d0oghF6TJzhbLtdsTMaZVF8hQuhsMIPRuEYxku9no
|
|
||||||
McqUqX30q+O98h1yCmfJmTtFJrNWPSeHejNAQP0BWmEMZBYD7gaexwDXWvcyQh9f
|
|
||||||
Y9gQQi/itHYOEBy6L+OC3ETD
|
|
||||||
-----END PRIVATE KEY-----
|
|
@ -1,17 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE REQUEST-----
|
|
||||||
MIICoDCCAYgCAQAwWzELMAkGA1UEBhMCVVMxEzARBgNVBAgMClNvbWUtU3RhdGUx
|
|
||||||
ITAfBgNVBAoMGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLc3Rh
|
|
||||||
Y2tlcl9jbG4wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDGJ+Q2pred
|
|
||||||
hnnYroMhBgQNWt3OvY49M6WC2u+P2HWj3+pjjt6oNaM8emYqRXTh4WlAyi8G/XNk
|
|
||||||
ADLvZf4HMzBp5kYtCemtzce6Zt1LE45Xd7xdFDca7PLOTFaaNY99AENBbaJf0s5z
|
|
||||||
tJSzIfBisPfhvDwOhbwJB5KZ0ezaa/RItYcOdkZeggbgoYeDA63L+fHTX5Y/Rt9A
|
|
||||||
ooo9VDix6y1pGMa53jVxdGvj1bb5hkqPtr5zi6hwIum1YDWWwp7wMkft0pnvrnwM
|
|
||||||
Q9mkA+Pxd8pMie5I5Ks+RPjYk2W8faEccwgN0DXhOBTnxwmdKxVnq+5nU6neZ2d1
|
|
||||||
pRelrklppay9AgMBAAGgADANBgkqhkiG9w0BAQsFAAOCAQEAOoXArh2qS9wxSEKx
|
|
||||||
JqgTCSxrocuY5xdpeNCoReIQazT+bG332THdiy8a3HtuNGnCSPqrKffpv/G95oSu
|
|
||||||
eGGxx+n/3sObgzliOk9kDA8XOebuUDb+jM3joLZikGzeFOaMCcwJrhYIyTSmN9Zh
|
|
||||||
k5rQT6ufRA7LCL5FS4sYG1sly/fYlBqp2kLs2piEgvWdjh2cnfJ2UnyIk/a6sUJI
|
|
||||||
x7zfmzFAeU3M+fR1Q3//4ISe4MAPDzn/rV8bpcl5iRqagyfbXHIeLfX68mCG3ABf
|
|
||||||
SKnzUBnfB3g1GjUXIMFzGk23VuKRd6dBHoIMNknkDCM4r4thUtWH7oywWDiYBMX7
|
|
||||||
oKrKwg==
|
|
||||||
-----END CERTIFICATE REQUEST-----
|
|
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN CERTIFICATE-----
|
|
||||||
MIICfzCCAiagAwIBAgIUcKLTD+mRsktaa/hUIb5e8INmXn0wCgYIKoZIzj0EAwIw
|
|
||||||
FjEUMBIGA1UEAwwLY2xuIFJvb3QgQ0EwHhcNMjQwNDEwMDk1OTA1WhcNMjUwNDEw
|
|
||||||
MDk1OTA1WjBbMQswCQYDVQQGEwJVUzETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8G
|
|
||||||
A1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMRQwEgYDVQQDDAtzdGFja2Vy
|
|
||||||
X2NsbjCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMYn5Damt52Gediu
|
|
||||||
gyEGBA1a3c69jj0zpYLa74/YdaPf6mOO3qg1ozx6ZipFdOHhaUDKLwb9c2QAMu9l
|
|
||||||
/gczMGnmRi0J6a3Nx7pm3UsTjld3vF0UNxrs8s5MVpo1j30AQ0Ftol/SznO0lLMh
|
|
||||||
8GKw9+G8PA6FvAkHkpnR7Npr9Ei1hw52Rl6CBuChh4MDrcv58dNflj9G30Ciij1U
|
|
||||||
OLHrLWkYxrneNXF0a+PVtvmGSo+2vnOLqHAi6bVgNZbCnvAyR+3Sme+ufAxD2aQD
|
|
||||||
4/F3ykyJ7kjkqz5E+NiTZbx9oRxzCA3QNeE4FOfHCZ0rFWer7mdTqd5nZ3WlF6Wu
|
|
||||||
SWmlrL0CAwEAAaNCMEAwHQYDVR0OBBYEFF4+yBxv8b+F7Jwr1dQbstMRYuO9MB8G
|
|
||||||
A1UdIwQYMBaAFIRyY3/2vNLaFHobsQiCxf7nU3QKMAoGCCqGSM49BAMCA0cAMEQC
|
|
||||||
IHDwzjxMRMztT4mN7tRMAHQsMCMbdIeKzDr7g0so19X/AiBZV4kvDbQSbVoJ2UIA
|
|
||||||
gZiiN5TD+ucMjU4wLzdiBRAFqA==
|
|
||||||
-----END CERTIFICATE-----
|
|
@ -89,13 +89,6 @@ mutation upsertWalletLND($id: ID, $socket: String!, $macaroon: String!, $cert: S
|
|||||||
}
|
}
|
||||||
`
|
`
|
||||||
|
|
||||||
export const UPSERT_WALLET_CLN =
|
|
||||||
gql`
|
|
||||||
mutation upsertWalletCLN($id: ID, $socket: String!, $rune: String!, $cert: String, $settings: AutowithdrawSettings!) {
|
|
||||||
upsertWalletCLN(id: $id, socket: $socket, rune: $rune, cert: $cert, settings: $settings)
|
|
||||||
}
|
|
||||||
`
|
|
||||||
|
|
||||||
export const REMOVE_WALLET =
|
export const REMOVE_WALLET =
|
||||||
gql`
|
gql`
|
||||||
mutation removeWallet($id: ID!) {
|
mutation removeWallet($id: ID!) {
|
||||||
@ -120,11 +113,6 @@ export const WALLET = gql`
|
|||||||
macaroon
|
macaroon
|
||||||
cert
|
cert
|
||||||
}
|
}
|
||||||
... on WalletCLN {
|
|
||||||
socket
|
|
||||||
rune
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -147,11 +135,6 @@ export const WALLET_BY_TYPE = gql`
|
|||||||
macaroon
|
macaroon
|
||||||
cert
|
cert
|
||||||
}
|
}
|
||||||
... on WalletCLN {
|
|
||||||
socket
|
|
||||||
rune
|
|
||||||
cert
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
161
lib/cln.js
161
lib/cln.js
@ -1,161 +0,0 @@
|
|||||||
import fetch from 'node-fetch'
|
|
||||||
import https from 'https'
|
|
||||||
|
|
||||||
export const createInvoice = async ({ socket, rune, cert, label, description, msats, expiry }) => {
|
|
||||||
const agent = cert ? new https.Agent({ ca: Buffer.from(cert, 'base64') }) : undefined
|
|
||||||
const url = 'https://' + socket + '/v1/invoice'
|
|
||||||
const randomId = Math.floor(Math.random() * 1000)
|
|
||||||
const res = await fetch(url, {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
Rune: rune,
|
|
||||||
// can be any node id, only required for CLN v23.08 and below
|
|
||||||
// see https://docs.corelightning.org/docs/rest#server
|
|
||||||
nodeId: '02cb2e2d5a6c5b17fa67b1a883e2973c82e328fb9bd08b2b156a9e23820c87a490'
|
|
||||||
},
|
|
||||||
agent,
|
|
||||||
body: JSON.stringify({
|
|
||||||
// why does CLN require a unique label?
|
|
||||||
label: description ? `${description} ${randomId}` : randomId,
|
|
||||||
description,
|
|
||||||
amount_msat: msats,
|
|
||||||
expiry
|
|
||||||
})
|
|
||||||
})
|
|
||||||
const inv = await res.json()
|
|
||||||
if (inv.error) {
|
|
||||||
throw new Error(inv.error.message)
|
|
||||||
}
|
|
||||||
return inv
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://github.com/clams-tech/rune-decoder/blob/57c2e76d1ef9ab7336f565b99de300da1c7b67ce/src/index.ts
|
|
||||||
export const decodeRune = (rune) => {
|
|
||||||
const runeBinary = Base64Binary.decode(rune)
|
|
||||||
const hashBinary = runeBinary.slice(0, 32)
|
|
||||||
const hash = binaryHashToHex(hashBinary)
|
|
||||||
const restBinary = runeBinary.slice(32)
|
|
||||||
|
|
||||||
const [uniqueId, ...restrictionStrings] = new TextDecoder().decode(restBinary).split('&')
|
|
||||||
|
|
||||||
const id = uniqueId.split('=')[1]
|
|
||||||
|
|
||||||
// invalid rune checks
|
|
||||||
if (!id) return null
|
|
||||||
if (restrictionStrings.some(invalidAscii)) return null
|
|
||||||
|
|
||||||
const restrictions = restrictionStrings.map((restriction) => {
|
|
||||||
const alternatives = restriction.split('|')
|
|
||||||
|
|
||||||
const summary = alternatives.reduce((str, alternative) => {
|
|
||||||
const [operator] = alternative.match(runeOperatorRegex) || []
|
|
||||||
if (!operator) return str
|
|
||||||
|
|
||||||
const [name, value] = alternative.split(operator)
|
|
||||||
|
|
||||||
return `${str ? `${str} OR ` : ''}${name} ${operatorToDescription(operator)} ${value}`
|
|
||||||
}, '')
|
|
||||||
|
|
||||||
return {
|
|
||||||
alternatives,
|
|
||||||
summary
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
id,
|
|
||||||
hash,
|
|
||||||
restrictions
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const Base64Binary = {
|
|
||||||
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
|
|
||||||
|
|
||||||
removePaddingChars: function (input) {
|
|
||||||
const lkey = this._keyStr.indexOf(input.charAt(input.length - 1))
|
|
||||||
|
|
||||||
if (lkey === 64) {
|
|
||||||
return input.substring(0, input.length - 1)
|
|
||||||
}
|
|
||||||
|
|
||||||
return input
|
|
||||||
},
|
|
||||||
|
|
||||||
decode: function (input) {
|
|
||||||
// get last chars to see if are valid
|
|
||||||
input = this.removePaddingChars(input)
|
|
||||||
input = this.removePaddingChars(input)
|
|
||||||
|
|
||||||
const bytes = parseInt(((input.length / 4) * 3).toString(), 10)
|
|
||||||
|
|
||||||
let chr1, chr2, chr3
|
|
||||||
let enc1, enc2, enc3, enc4
|
|
||||||
let i = 0
|
|
||||||
let j = 0
|
|
||||||
|
|
||||||
const uarray = new Uint8Array(bytes)
|
|
||||||
|
|
||||||
for (i = 0; i < bytes; i += 3) {
|
|
||||||
// get the 3 octects in 4 ascii chars
|
|
||||||
enc1 = this._keyStr.indexOf(input.charAt(j++))
|
|
||||||
enc2 = this._keyStr.indexOf(input.charAt(j++))
|
|
||||||
enc3 = this._keyStr.indexOf(input.charAt(j++))
|
|
||||||
enc4 = this._keyStr.indexOf(input.charAt(j++))
|
|
||||||
|
|
||||||
chr1 = (enc1 << 2) | (enc2 >> 4)
|
|
||||||
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2)
|
|
||||||
chr3 = ((enc3 & 3) << 6) | enc4
|
|
||||||
|
|
||||||
uarray[i] = chr1
|
|
||||||
if (enc3 !== 64) uarray[i + 1] = chr2
|
|
||||||
if (enc4 !== 64) uarray[i + 2] = chr3
|
|
||||||
}
|
|
||||||
|
|
||||||
return uarray
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function i2hex (i) {
|
|
||||||
return ('0' + i.toString(16)).slice(-2)
|
|
||||||
}
|
|
||||||
|
|
||||||
const binaryHashToHex = (hash) => {
|
|
||||||
return hash.reduce(function (memo, i) {
|
|
||||||
return memo + i2hex(i)
|
|
||||||
}, '')
|
|
||||||
}
|
|
||||||
|
|
||||||
const runeOperatorRegex = /[=^$/~<>{}#!]/g
|
|
||||||
|
|
||||||
const operatorToDescription = (operator) => {
|
|
||||||
switch (operator) {
|
|
||||||
case '=':
|
|
||||||
return 'is equal to'
|
|
||||||
case '^':
|
|
||||||
return 'starts with'
|
|
||||||
case '$':
|
|
||||||
return 'ends with'
|
|
||||||
case '/':
|
|
||||||
return 'is not equal to'
|
|
||||||
case '~':
|
|
||||||
return 'contains'
|
|
||||||
case '<':
|
|
||||||
return 'is less than'
|
|
||||||
case '>':
|
|
||||||
return 'is greater than'
|
|
||||||
case '{':
|
|
||||||
return 'sorts before'
|
|
||||||
case '}':
|
|
||||||
return 'sorts after'
|
|
||||||
case '#':
|
|
||||||
return 'comment'
|
|
||||||
case '!':
|
|
||||||
return 'is missing'
|
|
||||||
default:
|
|
||||||
return ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const invalidAscii = (str) => !![...str].some((char) => char.charCodeAt(0) > 127)
|
|
@ -6,13 +6,12 @@ import {
|
|||||||
} from './constants'
|
} from './constants'
|
||||||
import { SUPPORTED_CURRENCIES } from './currency'
|
import { SUPPORTED_CURRENCIES } from './currency'
|
||||||
import { NOSTR_MAX_RELAY_NUM, NOSTR_PUBKEY_BECH32, NOSTR_PUBKEY_HEX } from './nostr'
|
import { NOSTR_MAX_RELAY_NUM, NOSTR_PUBKEY_BECH32, NOSTR_PUBKEY_HEX } from './nostr'
|
||||||
import { msatsToSats, numWithUnits, abbrNum, ensureB64, B64_URL_REGEX } from './format'
|
import { msatsToSats, numWithUnits, abbrNum, ensureB64 } from './format'
|
||||||
import * as usersFragments from '@/fragments/users'
|
import * as usersFragments from '@/fragments/users'
|
||||||
import * as subsFragments from '@/fragments/subs'
|
import * as subsFragments from '@/fragments/subs'
|
||||||
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
import { isInvoicableMacaroon, isInvoiceMacaroon } from './macaroon'
|
||||||
import { parseNwcUrl } from './url'
|
import { parseNwcUrl } from './url'
|
||||||
import { datePivot } from './time'
|
import { datePivot } from './time'
|
||||||
import { decodeRune } from '@/lib/cln'
|
|
||||||
|
|
||||||
const { SUB } = subsFragments
|
const { SUB } = subsFragments
|
||||||
const { NAME_QUERY } = usersFragments
|
const { NAME_QUERY } = usersFragments
|
||||||
@ -328,32 +327,6 @@ export function LNDAutowithdrawSchema ({ me } = {}) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
export function CLNAutowithdrawSchema ({ me } = {}) {
|
|
||||||
return object({
|
|
||||||
socket: string().socket().required('required'),
|
|
||||||
rune: string().matches(B64_URL_REGEX, { message: 'invalid rune' }).required('required')
|
|
||||||
.test({
|
|
||||||
name: 'rune',
|
|
||||||
test: (v, context) => {
|
|
||||||
const decoded = decodeRune(v)
|
|
||||||
if (!decoded) return context.createError({ message: 'invalid rune' })
|
|
||||||
if (decoded.restrictions.length === 0) {
|
|
||||||
return context.createError({ message: 'rune must be restricted to method=invoice' })
|
|
||||||
}
|
|
||||||
if (decoded.restrictions.length !== 1 || decoded.restrictions[0].alternatives.length !== 1) {
|
|
||||||
return context.createError({ message: 'rune must be restricted to method=invoice only' })
|
|
||||||
}
|
|
||||||
if (decoded.restrictions[0].alternatives[0] !== 'method=invoice') {
|
|
||||||
return context.createError({ message: 'rune must be restricted to method=invoice only' })
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
cert: hexOrBase64Validator,
|
|
||||||
...autowithdrawSchemaMembers({ me })
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
export function autowithdrawSchemaMembers ({ me } = {}) {
|
export function autowithdrawSchemaMembers ({ me } = {}) {
|
||||||
return {
|
return {
|
||||||
priority: boolean(),
|
priority: boolean(),
|
||||||
|
56
package-lock.json
generated
56
package-lock.json
generated
@ -51,7 +51,6 @@
|
|||||||
"next-auth": "^4.23.2",
|
"next-auth": "^4.23.2",
|
||||||
"next-plausible": "^3.11.1",
|
"next-plausible": "^3.11.1",
|
||||||
"next-seo": "^6.1.0",
|
"next-seo": "^6.1.0",
|
||||||
"node-fetch": "^2.6.1",
|
|
||||||
"node-s3-url-encode": "^0.0.4",
|
"node-s3-url-encode": "^0.0.4",
|
||||||
"nodemailer": "^6.9.6",
|
"nodemailer": "^6.9.6",
|
||||||
"nostr": "^0.2.8",
|
"nostr": "^0.2.8",
|
||||||
@ -372,25 +371,6 @@
|
|||||||
"node": ">=12"
|
"node": ">=12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@apollo/server/node_modules/node-fetch": {
|
|
||||||
"version": "2.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
|
||||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": "^5.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "4.x || >=6.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"encoding": "^0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"encoding": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@apollo/server/node_modules/uuid": {
|
"node_modules/@apollo/server/node_modules/uuid": {
|
||||||
"version": "9.0.0",
|
"version": "9.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.0.tgz",
|
||||||
@ -7405,25 +7385,6 @@
|
|||||||
"node-fetch": "^2.6.12"
|
"node-fetch": "^2.6.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/cross-fetch/node_modules/node-fetch": {
|
|
||||||
"version": "2.7.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
|
|
||||||
"integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==",
|
|
||||||
"dependencies": {
|
|
||||||
"whatwg-url": "^5.0.0"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "4.x || >=6.0.0"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"encoding": "^0.1.0"
|
|
||||||
},
|
|
||||||
"peerDependenciesMeta": {
|
|
||||||
"encoding": {
|
|
||||||
"optional": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/cross-spawn": {
|
"node_modules/cross-spawn": {
|
||||||
"version": "7.0.3",
|
"version": "7.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||||
@ -14738,11 +14699,22 @@
|
|||||||
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
|
"integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA=="
|
||||||
},
|
},
|
||||||
"node_modules/node-fetch": {
|
"node_modules/node-fetch": {
|
||||||
"version": "2.6.1",
|
"version": "2.6.12",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.12.tgz",
|
||||||
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==",
|
"integrity": "sha512-C/fGU2E8ToujUivIO0H+tpQ6HWo4eEmchoPIoXtxCrVghxdKq+QOHqEZW7tuP3KlV3bC8FRMO5nMCC7Zm1VP6g==",
|
||||||
|
"dependencies": {
|
||||||
|
"whatwg-url": "^5.0.0"
|
||||||
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "4.x || >=6.0.0"
|
"node": "4.x || >=6.0.0"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"encoding": "^0.1.0"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"encoding": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/node-gyp-build": {
|
"node_modules/node-gyp-build": {
|
||||||
|
@ -56,7 +56,6 @@
|
|||||||
"next-auth": "^4.23.2",
|
"next-auth": "^4.23.2",
|
||||||
"next-plausible": "^3.11.1",
|
"next-plausible": "^3.11.1",
|
||||||
"next-seo": "^6.1.0",
|
"next-seo": "^6.1.0",
|
||||||
"node-fetch": "^2.6.1",
|
|
||||||
"node-s3-url-encode": "^0.0.4",
|
"node-s3-url-encode": "^0.0.4",
|
||||||
"nodemailer": "^6.9.6",
|
"nodemailer": "^6.9.6",
|
||||||
"nostr": "^0.2.8",
|
"nostr": "^0.2.8",
|
||||||
|
@ -130,7 +130,7 @@ export default function Rewards ({ ssrData }) {
|
|||||||
</h4>
|
</h4>
|
||||||
</Link>
|
</Link>
|
||||||
<Row className='pb-3'>
|
<Row className='pb-3'>
|
||||||
<Col lg={leaderboard?.users && 5}>
|
<Col>
|
||||||
<div
|
<div
|
||||||
className='d-flex flex-column sticky-lg-top py-5'
|
className='d-flex flex-column sticky-lg-top py-5'
|
||||||
>
|
>
|
||||||
|
@ -1,136 +0,0 @@
|
|||||||
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 { useApolloClient, useMutation } from '@apollo/client'
|
|
||||||
import { useToast } from '@/components/toast'
|
|
||||||
import { CLNAutowithdrawSchema } from '@/lib/validate'
|
|
||||||
import { useRouter } from 'next/router'
|
|
||||||
import { AutowithdrawSettings, autowithdrawInitial } from '@/components/autowithdraw-shared'
|
|
||||||
import { REMOVE_WALLET, UPSERT_WALLET_CLN, WALLET_BY_TYPE } from '@/fragments/wallet'
|
|
||||||
import WalletLogs from '@/components/wallet-logs'
|
|
||||||
import Info from '@/components/info'
|
|
||||||
import Text from '@/components/text'
|
|
||||||
|
|
||||||
const variables = { type: 'CLN' }
|
|
||||||
export const getServerSideProps = getGetServerSideProps({ query: WALLET_BY_TYPE, variables, authRequired: true })
|
|
||||||
|
|
||||||
export default function CLN ({ ssrData }) {
|
|
||||||
const me = useMe()
|
|
||||||
const toaster = useToast()
|
|
||||||
const router = useRouter()
|
|
||||||
const client = useApolloClient()
|
|
||||||
const [upsertWalletCLN] = useMutation(UPSERT_WALLET_CLN, {
|
|
||||||
refetchQueries: ['WalletLogs'],
|
|
||||||
onError: (err) => {
|
|
||||||
client.refetchQueries({ include: ['WalletLogs'] })
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
const [removeWallet] = useMutation(REMOVE_WALLET, {
|
|
||||||
refetchQueries: ['WalletLogs'],
|
|
||||||
onError: (err) => {
|
|
||||||
client.refetchQueries({ include: ['WalletLogs'] })
|
|
||||||
throw err
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const { walletByType: wallet } = ssrData || {}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<CenterLayout>
|
|
||||||
<h2 className='pb-2'>CLN</h2>
|
|
||||||
<h6 className='text-muted text-center'>autowithdraw to your Core Lightning node via <a href='https://docs.corelightning.org/docs/rest' target='_blank' noreferrer rel='noreferrer'>CLNRest</a></h6>
|
|
||||||
<Form
|
|
||||||
initial={{
|
|
||||||
socket: wallet?.wallet?.socket || '',
|
|
||||||
rune: wallet?.wallet?.rune || '',
|
|
||||||
cert: wallet?.wallet?.cert || '',
|
|
||||||
...autowithdrawInitial({ me, priority: wallet?.priority })
|
|
||||||
}}
|
|
||||||
schema={CLNAutowithdrawSchema({ me })}
|
|
||||||
onSubmit={async ({ socket, rune, cert, ...settings }) => {
|
|
||||||
try {
|
|
||||||
await upsertWalletCLN({
|
|
||||||
variables: {
|
|
||||||
id: wallet?.id,
|
|
||||||
socket,
|
|
||||||
rune,
|
|
||||||
cert,
|
|
||||||
settings: {
|
|
||||||
...settings,
|
|
||||||
autoWithdrawThreshold: Number(settings.autoWithdrawThreshold),
|
|
||||||
autoWithdrawMaxFeePercent: Number(settings.autoWithdrawMaxFeePercent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
toaster.success('saved settings')
|
|
||||||
router.push('/settings/wallets')
|
|
||||||
} catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Input
|
|
||||||
label='rest host and port'
|
|
||||||
name='socket'
|
|
||||||
hint='tor or clearnet'
|
|
||||||
placeholder='55.5.555.55:3010'
|
|
||||||
clear
|
|
||||||
required
|
|
||||||
autoFocus
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label={
|
|
||||||
<div className='d-flex align-items-center'>invoice only rune
|
|
||||||
<Info>
|
|
||||||
<Text>
|
|
||||||
{'We only accept runes that *only* allow `method=invoice`.\nRun this to generate one:\n\n```lightning-cli createrune restrictions=\'["method=invoice"]\'```'}
|
|
||||||
</Text>
|
|
||||||
</Info>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
name='rune'
|
|
||||||
clear
|
|
||||||
hint='must be restricted to method=invoice'
|
|
||||||
placeholder='S34KtUW-6gqS_hD_9cc_PNhfF-NinZyBOCgr1aIrark9NCZtZXRob2Q9aW52b2ljZQ=='
|
|
||||||
required
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
label={<>cert <small className='text-muted ms-2'>optional if from <a href='https://en.wikipedia.org/wiki/Certificate_authority' target='_blank' rel='noreferrer'>CA</a> (e.g. voltage)</small></>}
|
|
||||||
name='cert'
|
|
||||||
clear
|
|
||||||
hint='hex or base64 encoded'
|
|
||||||
placeholder='LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUNNVENDQWRpZ0F3SUJBZ0lRSHBFdFdrcGJwZHV4RVF2eVBPc3NWVEFLQmdncWhrak9QUVFEQWpBdk1SOHcKSFFZRFZRUUtFeFpzYm1RZ1lYVjBiMmRsYm1WeVlYUmxaQ0JqWlhKME1Rd3dDZ1lEVlFRREV3TmliMkl3SGhjTgpNalF3TVRBM01qQXhORE0wV2hjTk1qVXdNekF6TWpBeE5ETTBXakF2TVI4d0hRWURWUVFLRXhac2JtUWdZWFYwCmIyZGxibVZ5WVhSbFpDQmpaWEowTVF3d0NnWURWUVFERXdOaWIySXdXVEFUQmdjcWhrak9QUUlCQmdncWhrak8KUFFNQkJ3TkNBQVJUS3NMVk5oZnhqb1FLVDlkVVdDbzUzSmQwTnBuL1BtYi9LUE02M1JxbU52dFYvdFk4NjJJZwpSbE41cmNHRnBEajhUeFc2OVhIK0pTcHpjWDdlN3N0Um80SFZNSUhTTUE0R0ExVWREd0VCL3dRRUF3SUNwREFUCkJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQVBCZ05WSFJNQkFmOEVCVEFEQVFIL01CMEdBMVVkRGdRV0JCVDAKMnh3V25GeHRUNzI0MWxwZlNoNm9FWi9UMWpCN0JnTlZIUkVFZERCeWdnTmliMktDQ1d4dlkyRnNhRzl6ZElJRApZbTlpZ2d4d2IyeGhjaTF1TVMxaWIyS0NGR2h2YzNRdVpHOWphMlZ5TG1sdWRHVnlibUZzZ2dSMWJtbDRnZ3AxCmJtbDRjR0ZqYTJWMGdnZGlkV1pqYjI1dWh3Ui9BQUFCaHhBQUFBQUFBQUFBQUFBQUFBQUFBQUFCaHdTc0VnQUQKTUFvR0NDcUdTTTQ5QkFNQ0EwY0FNRVFDSUEwUTlkRXdoNXpPRnpwL3hYeHNpemh5SkxNVG5yazU1VWx1NHJPRwo4WW52QWlBVGt4U3p3Y3hZZnFscGx0UlNIbmd0NUJFcDBzcXlHL05nenBzb2pmMGNqQT09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K'
|
|
||||||
/>
|
|
||||||
<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)
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</Form>
|
|
||||||
<div className='mt-3 w-100'>
|
|
||||||
<WalletLogs wallet='cln' embedded />
|
|
||||||
</div>
|
|
||||||
</CenterLayout>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export function CLNCard ({ wallet }) {
|
|
||||||
return (
|
|
||||||
<WalletCard
|
|
||||||
title='CLN'
|
|
||||||
badges={['receive only', 'non-custodial']}
|
|
||||||
provider='cln'
|
|
||||||
enabled={wallet !== undefined || undefined}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}
|
|
@ -6,7 +6,6 @@ import { LightningAddressWalletCard } from './lightning-address'
|
|||||||
import { LNbitsCard } from './lnbits'
|
import { LNbitsCard } from './lnbits'
|
||||||
import { NWCCard } from './nwc'
|
import { NWCCard } from './nwc'
|
||||||
import { LNDCard } from './lnd'
|
import { LNDCard } from './lnd'
|
||||||
import { CLNCard } from './cln'
|
|
||||||
import { WALLETS } from '@/fragments/wallet'
|
import { WALLETS } from '@/fragments/wallet'
|
||||||
import { useQuery } from '@apollo/client'
|
import { useQuery } from '@apollo/client'
|
||||||
import PageLoading from '@/components/page-loading'
|
import PageLoading from '@/components/page-loading'
|
||||||
@ -20,7 +19,6 @@ export default function Wallet ({ ssrData }) {
|
|||||||
const { wallets } = data || ssrData
|
const { wallets } = data || ssrData
|
||||||
const lnd = wallets.find(w => w.type === 'LND')
|
const lnd = wallets.find(w => w.type === 'LND')
|
||||||
const lnaddr = wallets.find(w => w.type === 'LIGHTNING_ADDRESS')
|
const lnaddr = wallets.find(w => w.type === 'LIGHTNING_ADDRESS')
|
||||||
const cln = wallets.find(w => w.type === 'CLN')
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout>
|
<Layout>
|
||||||
@ -30,7 +28,6 @@ export default function Wallet ({ ssrData }) {
|
|||||||
<div className={styles.walletGrid}>
|
<div className={styles.walletGrid}>
|
||||||
<LightningAddressWalletCard wallet={lnaddr} />
|
<LightningAddressWalletCard wallet={lnaddr} />
|
||||||
<LNDCard wallet={lnd} />
|
<LNDCard wallet={lnd} />
|
||||||
<CLNCard wallet={cln} />
|
|
||||||
<LNbitsCard />
|
<LNbitsCard />
|
||||||
<NWCCard />
|
<NWCCard />
|
||||||
<WalletCard title='coming soon' badges={['probably']} />
|
<WalletCard title='coming soon' badges={['probably']} />
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
-- AlterEnum
|
|
||||||
ALTER TYPE "WalletType" ADD VALUE 'CLN';
|
|
||||||
|
|
||||||
-- CreateTable
|
|
||||||
CREATE TABLE "WalletCLN" (
|
|
||||||
"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,
|
|
||||||
"cert" TEXT,
|
|
||||||
|
|
||||||
CONSTRAINT "WalletCLN_pkey" PRIMARY KEY ("id")
|
|
||||||
);
|
|
||||||
|
|
||||||
-- CreateIndex
|
|
||||||
CREATE UNIQUE INDEX "WalletCLN_walletId_key" ON "WalletCLN"("walletId");
|
|
||||||
|
|
||||||
-- AddForeignKey
|
|
||||||
ALTER TABLE "WalletCLN" ADD CONSTRAINT "WalletCLN_walletId_fkey" FOREIGN KEY ("walletId") REFERENCES "Wallet"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
|
||||||
|
|
||||||
CREATE TRIGGER wallet_cln_as_jsonb
|
|
||||||
AFTER INSERT OR UPDATE ON "WalletCLN"
|
|
||||||
FOR EACH ROW EXECUTE PROCEDURE wallet_wallet_type_as_jsonb();
|
|
@ -133,7 +133,6 @@ model User {
|
|||||||
enum WalletType {
|
enum WalletType {
|
||||||
LIGHTNING_ADDRESS
|
LIGHTNING_ADDRESS
|
||||||
LND
|
LND
|
||||||
CLN
|
|
||||||
}
|
}
|
||||||
|
|
||||||
model Wallet {
|
model Wallet {
|
||||||
@ -155,7 +154,6 @@ model Wallet {
|
|||||||
wallet Json? @db.JsonB
|
wallet Json? @db.JsonB
|
||||||
walletLightningAddress WalletLightningAddress?
|
walletLightningAddress WalletLightningAddress?
|
||||||
walletLND WalletLND?
|
walletLND WalletLND?
|
||||||
walletCLN WalletCLN?
|
|
||||||
|
|
||||||
@@index([userId])
|
@@index([userId])
|
||||||
}
|
}
|
||||||
@ -192,17 +190,6 @@ model WalletLND {
|
|||||||
cert String?
|
cert String?
|
||||||
}
|
}
|
||||||
|
|
||||||
model WalletCLN {
|
|
||||||
id Int @id @default(autoincrement())
|
|
||||||
walletId Int @unique
|
|
||||||
wallet Wallet @relation(fields: [walletId], references: [id], onDelete: Cascade)
|
|
||||||
createdAt DateTime @default(now()) @map("created_at")
|
|
||||||
updatedAt DateTime @default(now()) @updatedAt @map("updated_at")
|
|
||||||
socket String
|
|
||||||
rune String
|
|
||||||
cert String?
|
|
||||||
}
|
|
||||||
|
|
||||||
model Mute {
|
model Mute {
|
||||||
muterId Int
|
muterId Int
|
||||||
mutedId Int
|
mutedId Int
|
||||||
|
72
sndev
72
sndev
@ -10,7 +10,7 @@ docker__compose() {
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
if [ -z "$COMPOSE_PROFILES" ]; then
|
if [ -z "$COMPOSE_PROFILES" ]; then
|
||||||
COMPOSE_PROFILES="images,search,payments,email,capture"
|
COMPOSE_PROFILES="images,search,payments,email"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
CURRENT_UID=$(id -u) CURRENT_GID=$(id -g) COMPOSE_PROFILES=$COMPOSE_PROFILES command docker compose --env-file .env.development "$@"
|
CURRENT_UID=$(id -u) CURRENT_GID=$(id -g) COMPOSE_PROFILES=$COMPOSE_PROFILES command docker compose --env-file .env.development "$@"
|
||||||
@ -23,7 +23,7 @@ docker__exec() {
|
|||||||
exit 0
|
exit 0
|
||||||
fi
|
fi
|
||||||
|
|
||||||
DOCKER_CLI_HINTS=false command docker exec -i "$@"
|
command docker exec -i "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
docker__sn_lnd() {
|
docker__sn_lnd() {
|
||||||
@ -48,17 +48,6 @@ docker__stacker_lnd() {
|
|||||||
docker__exec $t -u lnd stacker_lnd lncli "$@"
|
docker__exec $t -u lnd stacker_lnd lncli "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
docker__stacker_cln() {
|
|
||||||
t=$1
|
|
||||||
if [ "$t" = "-t" ]; then
|
|
||||||
shift
|
|
||||||
else
|
|
||||||
t=""
|
|
||||||
fi
|
|
||||||
|
|
||||||
docker__exec $t -u clightning stacker_cln lightning-cli --regtest "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__start() {
|
sndev__start() {
|
||||||
shift
|
shift
|
||||||
|
|
||||||
@ -218,35 +207,6 @@ OPTIONS"
|
|||||||
docker__stacker_lnd payinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+--pay_req value/'
|
docker__stacker_lnd payinvoice -h | awk '/OPTIONS:/{y=1;next}y' | awk '!/^[\t ]+--pay_req value/'
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__cln_fund() {
|
|
||||||
shift
|
|
||||||
docker__stacker_cln -t pay "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__help_cln_fund() {
|
|
||||||
help="
|
|
||||||
pay a bolt11 for funding with CLN
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
$ sndev cln_fund <bolt11>"
|
|
||||||
echo "$help"
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__cln_withdraw() {
|
|
||||||
shift
|
|
||||||
label=$(date +%s)
|
|
||||||
docker__stacker_cln -t invoice "$1" "$label" sndev | jq -r '.bolt11'
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__help_cln_withdraw() {
|
|
||||||
help="
|
|
||||||
create a bolt11 for withdrawal with CLN
|
|
||||||
|
|
||||||
USAGE
|
|
||||||
$ sndev cln_withdraw <amount msats>"
|
|
||||||
echo "$help"
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__withdraw() {
|
sndev__withdraw() {
|
||||||
shift
|
shift
|
||||||
docker__stacker_lnd addinvoice --amt "$@" | jq -r '.payment_request'
|
docker__stacker_lnd addinvoice --amt "$@" | jq -r '.payment_request'
|
||||||
@ -327,33 +287,24 @@ sndev__help_compose() {
|
|||||||
docker__compose --help
|
docker__compose --help
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__sn_lndcli() {
|
sndev__sn_lncli() {
|
||||||
shift
|
shift
|
||||||
docker__sn_lnd -t "$@"
|
docker__sn_lnd -t "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__help_sn_lndcli() {
|
sndev__help_sn_lncli() {
|
||||||
docker__sn_lnd --help
|
docker__sn_lnd --help
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__stacker_lndcli() {
|
sndev__stacker_lncli() {
|
||||||
shift
|
shift
|
||||||
docker__stacker_lnd -t "$@"
|
docker__stacker_lnd -t "$@"
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__help_stacker_lndcli() {
|
sndev__help_stacker_lncli() {
|
||||||
docker__stacker_lnd --help
|
docker__stacker_lnd --help
|
||||||
}
|
}
|
||||||
|
|
||||||
sndev__stacker_clncli() {
|
|
||||||
shift
|
|
||||||
docker__stacker_cln -t "$@"
|
|
||||||
}
|
|
||||||
|
|
||||||
sndev__help_stacker_clncli() {
|
|
||||||
docker__stacker_cln help
|
|
||||||
}
|
|
||||||
|
|
||||||
__sndev__pr_track() {
|
__sndev__pr_track() {
|
||||||
json=$(curl -fsSH "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/stackernews/stacker.news/pulls/$1")
|
json=$(curl -fsSH "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/stackernews/stacker.news/pulls/$1")
|
||||||
case $(git config --get remote.origin.url) in
|
case $(git config --get remote.origin.url) in
|
||||||
@ -490,10 +441,6 @@ COMMANDS
|
|||||||
fund pay a bolt11 for funding
|
fund pay a bolt11 for funding
|
||||||
withdraw create a bolt11 for withdrawal
|
withdraw create a bolt11 for withdrawal
|
||||||
|
|
||||||
cln:
|
|
||||||
cln_fund pay a bolt11 for funding with CLN
|
|
||||||
cln_withdraw create a bolt11 for withdrawal with CLN
|
|
||||||
|
|
||||||
db:
|
db:
|
||||||
psql open psql on db
|
psql open psql on db
|
||||||
prisma run prisma commands
|
prisma run prisma commands
|
||||||
@ -503,10 +450,9 @@ COMMANDS
|
|||||||
lint run linters
|
lint run linters
|
||||||
|
|
||||||
other:
|
other:
|
||||||
compose docker compose passthrough
|
compose docker compose passthrough
|
||||||
sn_lndcli lncli passthrough on sn_lnd
|
sn_lncli lncli passthrough on sn_lnd
|
||||||
stacker_lndcli lncli passthrough on stacker_lnd
|
stacker_lncli lncli passthrough on stacker_lnd
|
||||||
stacker_clncli lightning-cli passthrough on stacker_cln
|
|
||||||
"
|
"
|
||||||
echo "$help"
|
echo "$help"
|
||||||
}
|
}
|
||||||
|
@ -130,8 +130,6 @@ $accordion-button-icon-dark: $accordion-button-icon;
|
|||||||
$accordion-button-active-icon-dark: $accordion-button-icon;
|
$accordion-button-active-icon-dark: $accordion-button-icon;
|
||||||
|
|
||||||
|
|
||||||
$zindex-sticky: 900;
|
|
||||||
|
|
||||||
:root, [data-bs-theme=light] {
|
:root, [data-bs-theme=light] {
|
||||||
--theme-navLink: rgba(0, 0, 0, 0.55);
|
--theme-navLink: rgba(0, 0, 0, 0.55);
|
||||||
--theme-navLinkFocus: rgba(0, 0, 0, 0.7);
|
--theme-navLinkFocus: rgba(0, 0, 0, 0.7);
|
||||||
|
@ -2,7 +2,6 @@ import { authenticatedLndGrpc, createInvoice } from 'ln-service'
|
|||||||
import { msatsToSats, numWithUnits, satsToMsats } from '@/lib/format'
|
import { msatsToSats, numWithUnits, satsToMsats } from '@/lib/format'
|
||||||
import { datePivot } from '@/lib/time'
|
import { datePivot } from '@/lib/time'
|
||||||
import { createWithdrawal, sendToLnAddr, addWalletLog } from '@/api/resolvers/wallet'
|
import { createWithdrawal, sendToLnAddr, addWalletLog } from '@/api/resolvers/wallet'
|
||||||
import { createInvoice as createInvoiceCLN } from '@/lib/cln'
|
|
||||||
|
|
||||||
export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
||||||
const user = await models.user.findUnique({ where: { id } })
|
const user = await models.user.findUnique({ where: { id } })
|
||||||
@ -56,15 +55,6 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
|||||||
level: 'SUCCESS',
|
level: 'SUCCESS',
|
||||||
message
|
message
|
||||||
}, { me: user, models })
|
}, { me: user, models })
|
||||||
} else if (wallet.type === 'CLN') {
|
|
||||||
await autowithdrawCLN(
|
|
||||||
{ amount, maxFee },
|
|
||||||
{ models, me: user, lnd })
|
|
||||||
await addWalletLog({
|
|
||||||
wallet: 'walletCLN',
|
|
||||||
level: 'SUCCESS',
|
|
||||||
message
|
|
||||||
}, { me: user, models })
|
|
||||||
} else if (wallet.type === 'LIGHTNING_ADDRESS') {
|
} else if (wallet.type === 'LIGHTNING_ADDRESS') {
|
||||||
await autowithdrawLNAddr(
|
await autowithdrawLNAddr(
|
||||||
{ amount, maxFee },
|
{ amount, maxFee },
|
||||||
@ -82,9 +72,7 @@ export async function autoWithdraw ({ data: { id }, models, lnd }) {
|
|||||||
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
|
// LND errors are in this shape: [code, type, { err: { code, details, metadata } }]
|
||||||
const details = error[2]?.err?.details || error.message || error.toString?.()
|
const details = error[2]?.err?.details || error.message || error.toString?.()
|
||||||
await addWalletLog({
|
await addWalletLog({
|
||||||
wallet: wallet.type === 'LND'
|
wallet: wallet.type === 'LND' ? 'walletLND' : 'walletLightningAddress',
|
||||||
? 'walletLND'
|
|
||||||
: wallet.type === 'CLN' ? 'walletCLN' : 'walletLightningAddress',
|
|
||||||
level: 'ERROR',
|
level: 'ERROR',
|
||||||
message: 'autowithdrawal failed: ' + details
|
message: 'autowithdrawal failed: ' + details
|
||||||
})
|
})
|
||||||
@ -154,36 +142,3 @@ async function autowithdrawLND ({ amount, maxFee }, { me, models, lnd }) {
|
|||||||
|
|
||||||
return await createWithdrawal(null, { invoice: invoice.request, maxFee }, { me, models, lnd, autoWithdraw: true })
|
return await createWithdrawal(null, { invoice: invoice.request, maxFee }, { me, models, lnd, autoWithdraw: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
async function autowithdrawCLN ({ amount, maxFee }, { me, models, lnd }) {
|
|
||||||
if (!me) {
|
|
||||||
throw new Error('me not specified')
|
|
||||||
}
|
|
||||||
|
|
||||||
const wallet = await models.wallet.findFirst({
|
|
||||||
where: {
|
|
||||||
userId: me.id,
|
|
||||||
type: 'CLN'
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
walletCLN: true
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
if (!wallet || !wallet.walletCLN) {
|
|
||||||
throw new Error('no cln wallet found')
|
|
||||||
}
|
|
||||||
|
|
||||||
const { walletCLN: { cert, rune, socket } } = wallet
|
|
||||||
|
|
||||||
const inv = await createInvoiceCLN({
|
|
||||||
socket,
|
|
||||||
rune,
|
|
||||||
cert,
|
|
||||||
description: me.hideInvoiceDesc ? undefined : 'autowithdraw to CLN from SN',
|
|
||||||
msats: amount + 'sat',
|
|
||||||
expiry: 360
|
|
||||||
})
|
|
||||||
|
|
||||||
return await createWithdrawal(null, { invoice: inv.bolt11, maxFee }, { me, models, lnd, autoWithdraw: true })
|
|
||||||
}
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user