Merge pull request #4 from SatoshiPortal/dev

Trying something?
This commit is contained in:
Francis Pouliot
2020-01-17 14:58:12 -05:00
committed by GitHub
25 changed files with 757 additions and 125 deletions

View File

@@ -70,7 +70,9 @@ The core component of cyphernode is a request handler which exposes HTTP endpoin
## Documentation
* Read the API docs here: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/API.md
* Read the API docs here:
* API v0 (Current): https://github.com/SatoshiPortal/cyphernode/blob/master/doc/API.v0.md
* API v1 (RESTful): https://github.com/SatoshiPortal/cyphernode/blob/master/doc/API.v1.md
* Installation documentation: https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL.md
* Step-by-step manual install (deprecated): https://github.com/SatoshiPortal/cyphernode/blob/master/doc/INSTALL-MANUAL-STEPS.md

View File

@@ -18,6 +18,8 @@ action_getactivewatchesbylabel=watcher
action_getactivexpubwatches=watcher
action_watchtxid=watcher
action_getactivewatches=watcher
action_get_txns_by_watchlabel=watcher
action_get_unused_addresses_by_watchlabel=watcher
action_getbestblockhash=watcher
action_getbestblockinfo=watcher
action_getblockinfo=watcher
@@ -28,6 +30,7 @@ action_ln_getconnectionstring=watcher
action_ln_decodebolt11=watcher
# Spender can do what the watcher can do, plus:
action_getxnslist=spender
action_getbalance=spender
action_getbalances=spender
action_getbalancebyxpub=spender

View File

@@ -35,7 +35,7 @@
"lightning_nodecolor": "LN nodes have colors. Choose the <font underline='true'>color you want</font> for yours in RGB format (RRGGBB). For example, pure red would be <font color='#ff0000'>ff0000</font>.",
"lightning_datapath": "<font underline='true'>Path name</font> to where LN's data files are stored. This directory will be mounted into the LN node's container. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"lightning_datapath_custom": " ",
"lightning_expose": "By default, LN node port will be <font underline='true'>published</font> outside of Docker. Do you want to hide it so that your node can't be accessed from outside of the Docker network?",
"lightning_expose": "By default, LN node port will be <font underline='true'>published</font> outside of Docker. Do you want to expose it so that your node can be accessed from outside of the Docker network?",
"otsclient_datapath": "<font underline='true'>Full path</font> where the OTS files will be stored. This path will be mounted into the otsclient container which will create the OTS files when <font color='#00ff00'>stamping</font> and update them when <font color='#00ff00'>upgrading</font> stamps. It will also be mounted to the proxy container so that it can serve the <font color='#00ff00'>ots_getfile</font> and send the OTS files to clients. <font color='#ff0000'>If running on OSX, check mountable directories in Docker's File Sharing configs.</font>",
"otsclient_datapath_custom": " ",
"installer_mode": "Only one <font underline='true'>installation mode</font> is supported, right now: <font color='#0000ff'>local docker (self-hosted)</font>. Choose wisely ;-)",

View File

@@ -149,6 +149,11 @@ module.exports = class App {
} );
if( !fs.existsSync(this.destinationPath(configArchiveFileName)) ) {
if( this.sessionData.noWizard ) {
console.log(chalk.bold.red('Unable to run in no wizard mode without a config.7z')+'\n');
process.exit();
return;
}
let r = {};
process.stdout.write(ansi.clear+ansi.reset);
while( !r.password0 || !r.password1 || r.password0 !== r.password1 ) {
@@ -449,7 +454,7 @@ module.exports = class App {
name: 'MQ broker',
label: 'broker',
host: 'broker',
networks: ['cyphernodenet'],
networks: ['cyphernodenet', 'cyphernodeappsnet'],
docker: 'eclipse-mosquitto:'+this.config.docker_versions['eclipse-mosquitto']
}

View File

@@ -2451,9 +2451,9 @@
"dev": true
},
"handlebars": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.1.2.tgz",
"integrity": "sha512-nvfrjqvt9xQ8Z/w0ijewdD/vvWDTOweBUm96NTr66Wfvo1mJenBLwcYmPs3TIBP5ruzYGD7Hx/DaM9RmhroGPw==",
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.5.3.tgz",
"integrity": "sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA==",
"dev": true,
"requires": {
"neo-async": "^2.6.0",
@@ -3721,9 +3721,9 @@
"dev": true
},
"mixin-deep": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz",
"integrity": "sha512-8ZItLHeEgaqEvd5lYBXfm4EZSFCX29Jb9K+lAHhDKzReKBQKj3R+7NOF6tjqYi9t4oI8VUfaWITJQm86wnXGNQ==",
"version": "1.3.2",
"resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz",
"integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==",
"dev": true,
"requires": {
"for-in": "^1.0.2",

View File

@@ -8,6 +8,7 @@ action_helloworld=stats
action_getblockchaininfo=stats
action_installation_info=stats
action_getmempoolinfo=stats
action_getblockhash=stats
# Watcher can:
action_watch=watcher
@@ -18,6 +19,8 @@ action_unwatchxpubbylabel=watcher
action_getactivewatchesbyxpub=watcher
action_getactivewatchesbylabel=watcher
action_getactivexpubwatches=watcher
action_get_txns_by_watchlabel=watcher
action_get_unused_addresses_by_watchlabel=watcher
action_watchtxid=watcher
action_getactivewatches=watcher
action_getbestblockhash=watcher
@@ -32,6 +35,7 @@ action_ln_getconnectionstring=watcher
action_ln_decodebolt11=watcher
# Spender can do what the watcher can do, plus:
action_get_txns_spending=spender
action_getbalance=spender
action_getbalances=spender
action_getbalancebyxpub=spender

View File

@@ -106,6 +106,7 @@ services:
image: eclipse-mosquitto:1.6
networks:
- cyphernodenet
- cyphernodeappsnet
restart: always
# deploy:
# placement:
@@ -165,7 +166,7 @@ services:
<% } %>
volumes:
- "<%= otsclient_datapath %>:/otsfiles"
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf"
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf:ro"
command: $USER /script/startotsclient.sh
networks:
- cyphernodenet
@@ -190,8 +191,8 @@ services:
- "<%= gatekeeper_port %>:<%= gatekeeper_port %>"
<% } %>
volumes:
- "<%= gatekeeper_datapath %>/certs:/etc/ssl/certs"
- "<%= gatekeeper_datapath %>/private:/etc/ssl/private"
- "<%= gatekeeper_datapath %>/certs:/etc/ssl/certs:ro"
- "<%= gatekeeper_datapath %>/private:/etc/ssl/private:ro"
- "<%= gatekeeper_datapath %>/keys.properties:/etc/nginx/conf.d/keys.properties"
- "<%= gatekeeper_datapath %>/api.properties:/etc/nginx/conf.d/api.properties"
- "<%= gatekeeper_datapath %>/default.conf:/etc/nginx/conf.d/default.conf"
@@ -220,9 +221,9 @@ services:
- 443:443
volumes:
- "/var/run/docker.sock:/var/run/docker.sock"
- "<%= traefik_datapath%>/traefik.toml:/traefik.toml"
- "<%= traefik_datapath%>/traefik.toml:/traefik.toml:ro"
- "<%= traefik_datapath%>/acme.json:/acme.json"
- "<%= traefik_datapath%>/htpasswd:/htpasswd/htpasswd"
- "<%= traefik_datapath%>/htpasswd:/htpasswd/htpasswd:ro"
networks:
- cyphernodeappsnet
restart: always
@@ -246,7 +247,7 @@ services:
<% } %>
volumes:
- "<%= lightning_datapath %>:/.lightning"
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf"
- "<%= bitcoin_datapath %>/bitcoin-client.conf:/.bitcoin/bitcoin.conf:ro"
- bitcoin_monitor:/bitcoin_monitor:ro
networks:
- cyphernodenet

View File

@@ -78,7 +78,7 @@ EXIT_STATUS=$(($? | ${EXIT_STATUS}))
printf "\r\n\e[1;32mTests finished.\e[0m\n"
if [ "$EXIT_STATUS" -ne "0" ]; then
printf "\r\n\033[1;31mThere was an error during cyphernode installation. Please see Docker's logs for more information. Run ./testdeployment.sh to rerun the tests. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m"
printf "\r\n\033[1;31mThere was an error during cyphernode installation. full logs: docker ps -q | xargs -L 1 docker logs , Containers logs: docker logs <containerid> , list containers: docker ps .Please see Docker's logs for more information. Run ./testdeployment.sh to rerun the tests. Run ./stop.sh to stop cyphernode.\r\n\r\n\033[0m"
exit 1
fi

8
dist/setup.sh vendored
View File

@@ -437,12 +437,6 @@ install_docker() {
esac
done
fi
elif [[ $cmpStatus == 'incompatible' ]]; then
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin.conf $BITCOIN_DATAPATH/bitcoin.conf.cyphernode 0 $SUDO_REQUIRED
copy_file $cyphernodeconf_filepath/bitcoin/bitcoin-client.conf $BITCOIN_DATAPATH/bitcoin-client.conf.cyphernode 0 $SUDO_REQUIRED
echo " Blockchain data is not compatible, due to misconfigured nets."
echo " Your cyphernode installation is most likely broken."
echo " Please check bitcoin.conf.cyphernode on how to repair it manually."
else
if [[ $cmpStatus == 'reindex' ]]; then
echo " Warning Reindexing will take some time."
@@ -698,9 +692,11 @@ sanity_checks_pre_install() {
install_apps() {
if [ ! -d "$current_path/apps" ]; then
local user=$(id -u $RUN_AS_USER):$(id -g $RUN_AS_USER)
local apps_repo="https://github.com/SatoshiPortal/cypherapps.git"
echo " clone $apps_repo into apps"
docker run --rm -v "$current_path":/git --entrypoint git cyphernode/cyphernodeconf:$CONF_VERSION clone --single-branch -b ${CYPHERAPPS_VERSION} "$apps_repo" /git/apps > /dev/null 2>&1
sudo_if_required chown -R $user $current_path/apps
fi
}

View File

@@ -4,12 +4,12 @@
### Watch a Bitcoin Address (called by application)
Inserts the address and callbacks in the DB and imports the address to the Watching wallet.
Inserts the address and callbacks in the DB and imports the address to the Watching wallet. The callback URLs and event message are optional. If eventMessage is not supplied, tx_confirmation for that watch will not be published. Event message should be in base64 format to avoid dealing with escaping special characters.
```http
POST http://cyphernode:8888/watch
with body...
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K"}
```
Proxy response:
@@ -26,7 +26,8 @@ Proxy response:
"estimatesmartfee2blocks": "0.000010",
"estimatesmartfee6blocks": "0.000010",
"estimatesmartfee36blocks": "0.000010",
"estimatesmartfee144blocks": "0.000010"
"estimatesmartfee144blocks": "0.000010",
"eventMessage": "eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K"
}
```
@@ -66,7 +67,8 @@ Proxy response:
"imported":"1",
"unconfirmedCallbackURL":"192.168.133.233:1111/callback0conf",
"confirmedCallbackURL":"192.168.133.233:1111/callback1conf",
"watching_since":"2018-09-06 21:14:03"}
"watching_since":"2018-09-06 21:14:03",
"eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K"}
]
}
```
@@ -619,12 +621,14 @@ Proxy response:
### Spend coins from spending wallet (called by application)
Calls sendtoaddress RPC on the spending wallet with supplied info.
Calls sendtoaddress RPC on the spending wallet with supplied info. Can supply an eventMessage to be published on successful spending. eventMessage should be base64 encoded to avoid dealing with escaping special characters.
```http
POST http://cyphernode:8888/spend
with body...
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
or
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233,"eventMessage":"eyJ3aGF0ZXZlciI6MTIzfQo="}
```
Proxy response:
@@ -782,12 +786,14 @@ Proxy response:
### Create a Lightning Network invoice (called by application)
Returns a LN invoice. Label must be unique. Description will be used by your user for payment. Expiry is in seconds.
Returns a LN invoice. Label must be unique. Description will be used by your user for payment. Expiry is in seconds and optional. If msatoshi is not supplied, will use "any" (ie donation invoice). callbackUrl is optional.
```http
POST http://cyphernode:8888/ln_create_invoice
with body...
{"msatoshi":10000,"label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":900}
{"msatoshi":10000,"label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":900,"callbackUrl":"https://thesite/lnwebhook/9d8sa98yd"}
or
{"label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":900}
```
Proxy response:
@@ -802,7 +808,7 @@ Proxy response:
### Pay a Lightning Network invoice (called by application)
Make a LN payment. expected_msatoshi and expected_description are respectively the amount and description you gave your user for her to create the invoice; they must match the given bolt11 invoice supplied by your user.
Make a LN payment. expected_msatoshi and expected_description are respectively the amount and description you gave your user for her to create the invoice; they must match the given bolt11 invoice supplied by your user. If the bolt11 invoice doesn't contain an amount, then the expected_msatoshi supplied here will be used as the paid amount.
```http
POST http://cyphernode:8888/ln_pay

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 76 KiB

View File

@@ -50,8 +50,6 @@ paths:
type: "object"
required:
- "address"
- "confirmedCallbackURL"
- "unconfirmedCallbackURL"
properties:
address:
$ref: '#/components/schemas/TypeAddressString'
@@ -61,6 +59,9 @@ paths:
confirmedCallbackURL:
type: "string"
format: "url"
eventMessage:
description: "Will be part of the published message on confirmations"
type: "string"
responses:
'200':
description: "successfully created"
@@ -350,6 +351,276 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_txns_by_watchlabel/{label}:
get:
parameters:
- in: "path"
name: "label"
description: "Xpub label"
required: true
schema:
type: "string"
tags:
- "transactions"
- "core features"
summary: "Get list of upto 10 transactions observed for addreses belonging to this label"
description: "Get list of transactions observed for addreses belonging to this label"
operationId: "get_txns_by_watchlabel"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "label_txns"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/WatchXPubTxn'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_txns_by_watchlabel/{label}/{count}:
get:
parameters:
- in: "path"
name: "label"
description: "Xpub label"
required: true
schema:
type: "string"
- in: "path"
name: "count"
description: "Number of transactions to return"
required: true
schema:
type: "number"
tags:
- "transactions"
- "core features"
summary: "Get list of transactions observed for addreses belonging to this label"
description: "Get list of upto count number of transactions observed for addreses belonging to this label"
operationId: "get_txns_by_watchlabel_count"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "label_txns"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/WatchXPubTxn'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_txns_spending:
get:
tags:
- "transactions"
- "core features"
summary: "Get list of upto 10 spending wallet transactions"
description: "Get list of upto 10 spending wallet transactions"
operationId: "get_txns_spending"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "txns"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/TransactionsSpending'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_txns_spending/{count}/:
get:
parameters:
- in: "path"
name: "count"
description: "Number of txns to return"
required: true
schema:
type: "integer"
tags:
- "transactions"
- "core features"
summary: "Get list of upto count spending wallet transactions"
description: "Get list of upto count wallets transactions"
operationId: "get_txns_spending_count"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "txns"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/TransactionsSpending'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_txns_spending/{count}/{skip}:
get:
parameters:
- in: "path"
name: "count"
description: "Number of txns to return"
required: true
schema:
type: "integer"
- in: "path"
name: "skip"
description: "Number of txns to skip (start from)"
required: true
schema:
type: "integer"
tags:
- "transactions"
- "core features"
summary: "Get list of upto count spending wallet transactions, skipping over the first {skip} number of transactions"
description: "Get list of upto count spending wallet transactions, skipping over the first {skip} number of transactions"
operationId: "get_txns_spending_count_skip"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "txns"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/TransactionsSpending'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_unused_addresses_by_watchlabel/{label}/:
get:
parameters:
- in: "path"
name: "label"
description: "Xpub label"
required: true
schema:
type: "string"
tags:
- "watching addresses"
- "core features"
summary: "Get list of upto 10 unused derived addreses belonging to this label"
description: "Gets an unused subset of addreses from the set of derived addresses belonging to this label"
operationId: "get_unused_addresses_by_watchlabel"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "label_unused_addresses"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/UnusedWatchXPubAddress'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/get_unused_addresses_by_watchlabel/{label}/{count}:
get:
parameters:
- in: "path"
name: "label"
description: "Xpub label"
required: true
schema:
type: "string"
- in: "path"
name: "count"
description: "Number of addresses to return"
required: true
schema:
type: "integer"
tags:
- "watching addresses"
- "core features"
summary: "Get list of unused upto to {count} derived addreses belonging to this label"
description: "Get list of unused upto to {count} derived addreses belonging to this label"
operationId: "get_unused_addresses_by_watchlabel_count"
responses:
'200':
description: "successful operation"
content:
application/json:
schema:
type: "object"
required:
- "label_unused_addresses"
properties:
watches:
type: "array"
items:
$ref: '#/components/schemas/UnusedWatchXPubAddress'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/getactivexpubwatches:
get:
tags:
@@ -796,6 +1067,9 @@ paths:
$ref: '#/components/schemas/TypeAddressString'
amount:
type: "number"
eventMessage:
description: "Will be part of the published message on spend"
type: "string"
responses:
'200':
description: "operation successful"
@@ -1113,10 +1387,8 @@ paths:
schema:
type: "object"
required:
- "msatoshi"
- "label"
- "description"
- "expiry"
properties:
msatoshi:
type: "integer"
@@ -1126,6 +1398,9 @@ paths:
type: "string"
expiry:
type: "integer"
callbackUrl:
type: "string"
format: "url"
responses:
'200':
description: "successful operation"
@@ -1166,7 +1441,6 @@ paths:
required:
- "bolt11"
- "expected_msatoshi"
- "expected_description"
properties:
bolt11:
type: "string"
@@ -1543,6 +1817,7 @@ components:
- "address"
- "unconfirmedCallbackURL"
- "confirmedCallbackURL"
- "eventMessage"
properties:
id:
type: "string"
@@ -1574,6 +1849,8 @@ components:
type: "string"
watching_since:
type: "string"
eventMessage:
type: "string"
WatchedByXpubAddress:
type: "object"
required:
@@ -1607,6 +1884,109 @@ components:
type: "string"
pub32_index:
type: "string"
UnusedWatchXPubAddress:
type: "object"
required:
- "pub32_watch_id"
- "pub32_label"
- "pub32"
- "address_pub32_index"
- "address"
properties:
pub32_watch_id:
type: "string"
address:
$ref: '#/components/schemas/TypeAddressString'
pub32:
$ref: '#/components/schemas/TypeXpubString'
pub32_label:
type: "string"
address_pub32_index:
type: "string"
TransactionsSpending:
type: "object"
required:
- "address"
- "category"
- "amount"
- "label"
- "vout"
- "fee"
- "confirmations"
- "blockhash"
- "blockindex"
- "blocktime"
- "txid"
- "time"
- "timereceived"
- "comment"
- "bip125-replaceable"
- "abandoned"
properties:
address:
$ref: '#/components/schemas/TypeAddressString'
category:
$ref: 'string'
amount:
type: "number"
label:
type: "string"
vout:
type: "integer"
fee:
type: "fee"
confirmations:
type: "integer"
blockhash:
$ref: '#/components/schemas/TypeHashString'
blockindex:
$ref: 'number'
blocktime:
type: "number"
txid:
$ref: '#/components/schemas/TypeHashString'
time:
type: "number"
timereceived:
type: "number"
comment:
type: "string"
bip125-replaceable:
type: "string"
abandoned:
type: "boolean"
WatchXPubTxn:
type: "object"
required:
- "label"
- "address"
- "txid"
- "confirmations"
- "blockheight"
- "v_out"
- "amount"
- "blockhash"
- "blocktime"
- "timereceived"
properties:
label:
type: "string"
address:
$ref: '#/components/schemas/TypeAddressString'
txid:
$ref: '#/components/schemas/TypeHashString'
confirmations:
type: "integer"
v_out:
type: "integer"
amount:
type: "number"
blockhash:
$ref: '#/components/schemas/TypeHashString'
blocktime:
type: "number"
timereceived:
type: "number"
WatchedXpub:
type: "object"
required:

View File

@@ -23,6 +23,7 @@ CREATE TABLE watching (
imported INTEGER DEFAULT FALSE,
watching_by_pub32_id INTEGER REFERENCES watching_by_pub32,
pub32_index INTEGER,
event_message TEXT,
inserted_ts INTEGER DEFAULT CURRENT_TIMESTAMP
);

View File

@@ -0,0 +1,14 @@
#!/bin/sh
echo "Checking for watching event support in DB..."
count=$(sqlite3 $DB_FILE "select count(*) from pragma_table_info('watching') where name='event_message'")
if [ "${count}" -eq "0" ]; then
# event_message not there, we have to migrate
echo "Migrating database for event triggered on watch notif..."
echo "Backing up current DB..."
cp $DB_FILE $DB_FILE-sqlmigrate20191127_0.2.4-0.3.0
echo "Altering DB..."
cat sqlmigrate20191127_0.2.4-0.3.0.sql | sqlite3 $DB_FILE
else
echo "Database watching event support migration already done, skipping!"
fi

View File

@@ -0,0 +1 @@
ALTER TABLE watching ADD COLUMN event_message TEXT;

View File

@@ -20,6 +20,11 @@ ln_create_invoice() {
trace "[ln_create_invoice] expiry=${expiry}"
local callback_url=$(echo "${request}" | jq -r ".callbackUrl")
trace "[ln_create_invoice] callback_url=${callback_url}"
if [ "${callback_url}" != "null" ]; then
# If not null, let's add double-quotes so we don't need to add the double-quotes in the sql insert,
# so if it's null, it will insert the actual sql NULL value.
callback_url="\"${callback_url}\""
fi
#/proxy $ ./lightning-cli invoice 10000 "t1" "t1d" 60
#{
@@ -28,8 +33,13 @@ ln_create_invoice() {
# "bolt11": "lnbc100n1pwzllqgpp55a8xen9sdcntehwr93pkwnuu8nmtqx9yew0flalcxhx9nvy34crqdq9wsckgxqzpucqp2rzjqt04ll5ft3mcuy8hws4xcku2pnhma9r9mavtjtadawyrw5kgzp7g7zr745qq3mcqqyqqqqlgqqqqqzsqpcr85k33shzaxscpj29fadmjmfej6y2p380x9w4kxydqpxq87l6lshy69fry9q2yrtu037nt44x77uhzkdyn8043n5yj8tqgluvmcl69cquaxr68"
#}
trace "[ln_create_invoice] ./lightning-cli invoice ${msatoshi} \"${label}\" \"${description}\" ${expiry}"
result=$(./lightning-cli invoice ${msatoshi} "${label}" "${description}" ${expiry})
if [ "${msatoshi}" = "null" ]; then
trace "[ln_create_invoice] ./lightning-cli invoice \"any\" \"${label}\" \"${description}\" ${expiry}"
result=$(./lightning-cli invoice "any" "${label}" "${description}" ${expiry})
else
trace "[ln_create_invoice] ./lightning-cli invoice ${msatoshi} \"${label}\" \"${description}\" ${expiry}"
result=$(./lightning-cli invoice ${msatoshi} "${label}" "${description}" ${expiry})
fi
returncode=$?
trace_rc ${returncode}
trace "[ln_create_invoice] result=${result}"
@@ -47,7 +57,11 @@ ln_create_invoice() {
# Let's get the connect string if provided in configuration
local connectstring=$(get_connection_string)
sql "INSERT OR IGNORE INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, msatoshi, description, status) VALUES (\"${label}\", \"${bolt11}\", \"${callback_url}\", \"${payment_hash}\", ${expires_at}, ${msatoshi}, \"${description}\", \"unpaid\")"
if [ "${msatoshi}" = "null" ]; then
sql "INSERT OR IGNORE INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, description, status) VALUES (\"${label}\", \"${bolt11}\", ${callback_url}, \"${payment_hash}\", ${expires_at}, \"${description}\", \"unpaid\")"
else
sql "INSERT OR IGNORE INTO ln_invoice (label, bolt11, callback_url, payment_hash, expires_at, msatoshi, description, status) VALUES (\"${label}\", \"${bolt11}\", ${callback_url}, \"${payment_hash}\", ${expires_at}, ${msatoshi}, \"${description}\", \"unpaid\")"
fi
trace_rc $?
id=$(sql "SELECT id FROM ln_invoice WHERE bolt11=\"${bolt11}\"")
trace_rc $?
@@ -58,9 +72,13 @@ ln_create_invoice() {
if [ -n "${connectstring}" ]; then
data="${data}\"connectstring\":\"${connectstring}\","
fi
data="${data}\"callback_url\":\"${callback_url}\","
if [ "${callback_url}" != "null" ]; then
data="${data}\"callbackUrl\":${callback_url},"
fi
data="${data}\"payment_hash\":\"${payment_hash}\","
data="${data}\"msatoshi\":${msatoshi},"
if [ "${msatoshi}" != "null" ]; then
data="${data}\"msatoshi\":${msatoshi},"
fi
data="${data}\"status\":\"unpaid\","
data="${data}\"description\":\"${description}\","
data="${data}\"expires_at\":${expires_at}}"
@@ -306,21 +324,31 @@ ln_pay() {
local invoice_description=$(echo "${result}" | jq ".description")
trace "[ln_pay] invoice_description=${invoice_description}"
# The amount must match
if [ "${expected_msatoshi}" != "${invoice_msatoshi}" ]; then
# The amount must match if not "any"
# If the amount is not in the invoice and not supplied as expected_msatoshi, then both will be null, that's ok!
# Same thing goes for the description.
if [ "${expected_msatoshi}" != "${invoice_msatoshi}" ] && [ "${invoice_msatoshi}" != "null" ]; then
# If invoice_msatoshi is null, that means "any" was supplied, so the amounts don't have to match!
result="{\"result\":\"error\",\"expected_msatoshi\":${expected_msatoshi},\"invoice_msatoshi\":${invoice_msatoshi}}"
returncode=1
elif [ "${expected_description}" != '""' ] && [ "${expected_description}" != "${invoice_description}" ]; then
# If expected description is empty, we accept any description on the invoice. Amount is the important thing.
elif [ -n "${expected_description}" ] && [ "${expected_description}" != "null" ] && [ "${expected_description}" != "${invoice_description}" ]; then
# If expected description is not empty but doesn't correspond to invoice_description, there'a problem.
# (we don't care about the description if expected description is empty. Amount is the most important thing)
result="{\"result\":\"error\",\"expected_description\":${expected_description},\"invoice_description\":${invoice_description}}"
returncode=1
else
# Amount and description is as expected, let's pay!
# Amount and description are as expected (or empty description), let's pay!
trace "[ln_pay] Amount and description are as expected, let's try to pay!"
trace "[ln_pay] ./lightning-cli pay -k bolt11=${bolt11} retry_for=15"
result=$(./lightning-cli pay -k bolt11=${bolt11} retry_for=15)
if [ "${invoice_msatoshi}" = "null" ]; then
# "any" amount on the invoice, we force paying the expected_msatoshi provided to ln_pay by the user
trace "[ln_pay] ./lightning-cli pay -k bolt11=${bolt11} msatoshi=${expected_msatoshi} retry_for=15"
result=$(./lightning-cli pay -k bolt11=${bolt11} msatoshi=${expected_msatoshi} retry_for=15)
else
trace "[ln_pay] ./lightning-cli pay -k bolt11=${bolt11} retry_for=15"
result=$(./lightning-cli pay -k bolt11=${bolt11} retry_for=15)
fi
returncode=$?
trace_rc ${returncode}
trace "[ln_pay] result=${result}"

View File

@@ -11,7 +11,7 @@ do_callbacks() {
trace "Entering do_callbacks()..."
# Let's fetch all the watching addresses still being watched but not called back
local callbacks=$(sql 'SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id NOT NULL AND w.callback0conf NOT NULL AND w.watching')
local callbacks=$(sql 'SELECT DISTINCT w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path, event_message FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE NOT calledback0conf AND watching_id NOT NULL AND w.callback0conf NOT NULL AND w.watching')
trace "[do_callbacks] callbacks0conf=${callbacks}"
local returncode
@@ -30,7 +30,7 @@ do_callbacks() {
fi
done
callbacks=$(sql 'SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path FROM watching w, watching_tx wt, tx t LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf AND confirmations>0 AND w.callback1conf NOT NULL AND w.watching')
callbacks=$(sql 'SELECT DISTINCT w.callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable, pub32_index, pub32, label, derivation_path, event_message FROM watching w, watching_tx wt, tx t LEFT JOIN watching_by_pub32 w32 ON watching_by_pub32_id = w32.id WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf AND confirmations>0 AND w.callback1conf NOT NULL AND w.watching')
trace "[do_callbacks] callbacks1conf=${callbacks}"
for row in ${callbacks}
@@ -64,12 +64,21 @@ ln_manage_callback() {
local id=$(echo "${row}" | cut -d '|' -f1)
trace "[ln_manage_callback] id=${id}"
local callback_url=$(echo "${row}" | cut -d '|' -f4)
trace "[ln_manage_callback] callback_url=${callback_url}"
if [ -z "${callback_url}" ]; then
# No callback url provided for that invoice
trace "[ln_manage_callback] No callback url provided for that invoice"
sql "UPDATE ln_invoice SET calledback=1 WHERE id=\"${id}\""
trace_rc $?
return
fi
local label=$(echo "${row}" | cut -d '|' -f2)
trace "[ln_manage_callback] label=${label}"
local bolt11=$(echo "${row}" | cut -d '|' -f3)
trace "[ln_manage_callback] bolt11=${bolt11}"
local callback_url=$(echo "${row}" | cut -d '|' -f4)
trace "[ln_manage_callback] callback_url=${callback_url}"
local payment_hash=$(echo "${row}" | cut -d '|' -f5)
trace "[ln_manage_callback] payment_hash=${payment_hash}"
local msatoshi=$(echo "${row}" | cut -d '|' -f6)
@@ -88,13 +97,6 @@ ln_manage_callback() {
trace "[ln_manage_callback] expires_at=${expires_at}"
local returncode
if [ -z "${callback_url}" ]; then
# No callback url provided for that invoice
sql "UPDATE ln_invoice SET calledback=1 WHERE id=\"${id}\""
trace_rc $?
return
fi
data="{\"id\":\"${id}\","
data="${data}\"label\":\"${label}\","
data="${data}\"bolt11\":\"${bolt11}\","
@@ -149,13 +151,22 @@ build_callback() {
local label
local derivation_path
# callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id
local event_message
# w.callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime,
# w.id, is_replaceable, pub32_index, pub32, label, derivation_path, event_message
url=$(echo "${row}" | cut -d '|' -f1)
trace "[build_callback] url=${url}"
if [ -z "${url}" ]; then
# No callback url provided for that watch
trace "[build_callback] No callback url provided for that watch, skipping webhook call"
return
fi
trace "[build_callback] row=${row}"
id=$(echo "${row}" | cut -d '|' -f14)
trace "[build_callback] id=${id}"
url=$(echo "${row}" | cut -d '|' -f1)
trace "[build_callback] url=${url}"
address=$(echo "${row}" | cut -d '|' -f2)
trace "[build_callback] address=${address}"
txid=$(echo "${row}" | cut -d '|' -f3)
@@ -199,6 +210,8 @@ build_callback() {
derivation_path=$(echo "${row}" | cut -d '|' -f19)
trace "[build_callback] derivation_path=${derivation_path}"
fi
event_message=$(echo "${row}" | cut -d '|' -f20)
trace "[build_callback] event_message=${event_message}"
data="{\"id\":\"${id}\","
data="${data}\"address\":\"${address}\","
@@ -212,19 +225,19 @@ build_callback() {
if [ -n "${fee}" ]; then
data="${data}\"fees\":${fee},"
fi
data="${data}\"is_replaceable\":${is_replaceable}"
data="${data}\"is_replaceable\":${is_replaceable},"
if [ -n "${blocktime}" ]; then
data="${data},\"blockhash\":\"${blockhash}\","
data="${data}\"blockhash\":\"${blockhash}\","
data="${data}\"blocktime\":\"$(date -Is -d @${blocktime})\","
data="${data}\"blockheight\":${blockheight}"
data="${data}\"blockheight\":${blockheight},"
fi
if [ -n "${pub32_index}" ]; then
data="${data}\"pub32\":\"${pub32}\","
data="${data}\"pub32_label\":\"${label}\","
derivation_path=$(echo -e $derivation_path | sed -En "s/n/${pub32_index}/p")
data="${data}\"pub32_derivation_path\":\"${derivation_path}\""
data="${data}\"pub32_derivation_path\":\"${derivation_path}\","
fi
data="${data}}"
data="${data}\"eventMessage\":\"${event_message}\"}"
trace "[build_callback] data=${data}"
curl_callback "${url}" "${data}"

View File

@@ -30,7 +30,7 @@ confirmation() {
local returncode
local txid=${1}
local tx_details
tx_details=$(get_transaction ${txid})
tx_details="$(get_transaction ${txid})"
returncode=$?
trace_rc ${returncode}
trace "[confirmation] tx_details=${tx_details}"
@@ -38,7 +38,6 @@ confirmation() {
trace "[confirmation] Transaction not in watcher, exiting."
return 0
fi
########################################################################################################
# First of all, let's make sure we're working on watched addresses...
local address
@@ -58,7 +57,7 @@ confirmation() {
notfirst=true
fi
done
local rows=$(sql "SELECT id, address, watching_by_pub32_id, pub32_index FROM watching WHERE address IN (${addresseswhere}) AND watching")
local rows=$(sql "SELECT id, address, watching_by_pub32_id, pub32_index, event_message FROM watching WHERE address IN (${addresseswhere}) AND watching")
if [ ${#rows} -eq 0 ]; then
trace "[confirmation] No watched address in this tx!"
return 0
@@ -68,7 +67,7 @@ confirmation() {
local tx=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
local id_inserted
local tx_raw_details=$(get_rawtransaction ${txid})
local tx_nb_conf=$(echo "${tx_details}" | jq '.result.confirmations')
local tx_nb_conf=$(echo "${tx_details}" | jq -r '.result.confirmations // 0')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
@@ -137,45 +136,62 @@ confirmation() {
rm rawtx-${txid}.blob
########################################################################################################
# Let's now insert in the join table if not already done
local event_message
local watching_id
# Let's see if we need to insert tx in the join table
tx=$(sql "SELECT tx_id FROM watching_tx WHERE tx_id=\"${tx}\"")
if [ -z "${tx}" ]; then
trace "[confirmation] For this tx, there's no watching_tx row, let's create it"
local watching_id
# If the tx is batched and pays multiple watched addresses, we have to insert
# those additional addresses in watching_tx!
for row in ${rows}
do
watching_id=$(echo "${row}" | cut -d '|' -f1)
address=$(echo "${row}" | cut -d '|' -f2)
# In the case of us spending to a watched address, the address appears twice in the details,
# once on the spend side (negative amount) and once on the receiving side (positive amount)
tx_vout_n=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .vout")
tx_vout_amount=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .amount | fabs" | awk '{ printf "%.8f", $0 }')
sql "INSERT OR IGNORE INTO watching_tx (watching_id, tx_id, vout, amount) VALUES (${watching_id}, ${id_inserted}, ${tx_vout_n}, ${tx_vout_amount})"
trace_rc $?
done
else
trace "[confirmation] For this tx, there's already watching_tx rows"
fi
########################################################################################################
########################################################################################################
# Let's now grow the watch window in the case of a xpub watcher...
trace "[confirmation] Let's now grow the watch window in the case of a xpub watcher"
for row in ${rows}
do
address=$(echo "${row}" | cut -d '|' -f2)
tx_vout_amount=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .amount | fabs" | awk '{ printf "%.8f", $0 }')
# In the case of us spending to a watched address, the address appears twice in the details,
# once on the spend side (negative amount) and once on the receiving side (positive amount)
tx_vout_n=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .vout")
########################################################################################################
# Let's now insert in the join table if not already done
if [ -z "${tx}" ]; then
trace "[confirmation] For this tx, there's no watching_tx row, let's create it"
# If the tx is batched and pays multiple watched addresses, we have to insert
# those additional addresses in watching_tx!
watching_id=$(echo "${row}" | cut -d '|' -f1)
sql "INSERT OR IGNORE INTO watching_tx (watching_id, tx_id, vout, amount) VALUES (${watching_id}, ${id_inserted}, ${tx_vout_n}, ${tx_vout_amount})"
trace_rc $?
else
trace "[confirmation] For this tx, there's already watching_tx rows"
fi
########################################################################################################
########################################################################################################
# Let's now grow the watch window in the case of a xpub watcher...
watching_by_pub32_id=$(echo "${row}" | cut -d '|' -f3)
pub32_index=$(echo "${row}" | cut -d '|' -f4)
if [ -n "${watching_by_pub32_id}" ]; then
trace "[confirmation] Let's now grow the watch window in the case of a xpub watcher"
pub32_index=$(echo "${row}" | cut -d '|' -f4)
extend_watchers ${watching_by_pub32_id} ${pub32_index}
fi
done
########################################################################################################
########################################################################################################
########################################################################################################
# Let's publish the event if needed
event_message=$(echo "${row}" | cut -d '|' -f5)
if [ -n "${event_message}" ]; then
# There's an event message, let's publish it!
trace "[confirmation] mosquitto_pub -h broker -t tx_confirmation -m \"{\"txid\":\"${txid}\",\"address\":\"${address}\",\"vout_n\":${tx_vout_n},\"amount\":${tx_vout_amount},\"confirmations\":${tx_nb_conf},\"eventMessage\":\"${event_message}\"}\""
response=$(mosquitto_pub -h broker -t tx_confirmation -m "{\"txid\":\"${txid}\",\"address\":\"${address}\",\"vout_n\":${tx_vout_n},\"amount\":${tx_vout_amount},\"confirmations\":${tx_nb_conf},\"eventMessage\":\"${event_message}\"}")
returncode=$?
trace_rc ${returncode}
fi
########################################################################################################
done
) 201>./.confirmation.lock

View File

@@ -3,6 +3,64 @@
. ./trace.sh
. ./sql.sh
get_txns_by_watchlabel(){
trace "Entering get_txns_by_watchlabel() for label ${1}..."
local label_txns
query=$(cat <<-HERE
SELECT w32.label, w.address, tx.txid, tx.confirmations,tx.blockheight, wtxn.vout, wtxn.amount, tx.blockhash, tx.blocktime, tx.timereceived
FROM watching_by_pub32 as w32
INNER JOIN watching AS w ON w32.id = w.watching_by_pub32_id
INNER JOIN watching_tx AS wtxn ON w.id = wtxn.watching_id
INNER JOIN tx AS tx ON wtxn.tx_id = tx.id
WHERE w32.label="$1"
LIMIT 0,${2-10}
HERE
)
label_txns=$(sql "$query")
returncode=$?
trace_rc ${returncode}
label_txns_json=$(echo "$label_txns" | jq -Rcsn '
{"label_txns":
[inputs
| . / "\n"
| (.[] | select(length > 0) | . / "|") as $input
| {"label": $input[0], "address": $input[1], "txid": $input[2], "confirmations": $input[3], "blockheight": $input[4], "v_out": $input[5], "amount": $input[6], "blockhash": $input[7], "blocktime": $input[8], "timereceived": $input[9]}
]
}
')
echo "$label_txns_json"
return ${returncode}
}
get_unused_addresses_by_watchlabel(){
trace "Entering get_unused_addresses_by_watchlabel() for label ${1}..."
local label_unused_addrs
query=$(cat <<-HERE
SELECT w32.id, w32.label, w32.pub32, w.pub32_index, w.address
FROM watching as w
INNER JOIN watching_by_pub32 AS w32 ON w.watching_by_pub32_id = w32.id
WHERE w32.label="$1"
AND NOT EXISTS (
SELECT 1 FROM watching_tx WHERE watching_id = w.id
)
ORDER BY w.pub32_index ASC
LIMIT 0,${2-10}
HERE
)
label_unused_addrs=$(sql "$query")
returncode=$?
trace_rc ${returncode}
label_unused_addrs_json=$(echo "$label_unused_addrs" | jq -Rcsn '
{"label_unused_addresses":
[inputs
| . / "\n"
| (.[] | select(length > 0) | . / "|") as $input
| {"pub32_watch_id": $input[0], "pub32_label": $input[1], "pub32" : $input[2], "address_pub32_index": $input[3], "address": $input[4]}
]
}
')
echo "$label_unused_addrs_json"
return ${returncode}
}
getactivewatches() {
trace "Entering getactivewatches()..."

View File

@@ -2,6 +2,7 @@
. ./trace.sh
. ./callbacks_txid.sh
. ./blockchainrpc.sh
newblock() {
trace "Entering newblock()..."
@@ -9,5 +10,16 @@ newblock() {
local request=${1}
local blockhash=$(echo "${request}" | cut -d ' ' -f2 | cut -d '/' -f3)
local blockinfo
blockinfo=$(get_block_info ${blockhash})
local blockheight
blockheight=$(echo ${blockinfo} | jq -r ".result.height")
trace "[newblock] mosquitto_pub -h broker -t newblock -m \"{\"blockhash\":\"${blockhash}\",\"blockheight\":\"${blockheight}\"}\""
response=$(mosquitto_pub -h broker -t newblock -m "{\"blockhash\":\"${blockhash}\",\"blockheight\":\"${blockheight}\"}")
returncode=$?
trace_rc ${returncode}
do_callbacks_txid
}

View File

@@ -89,6 +89,7 @@ main() {
watch)
# POST http://192.168.111.152:8080/watch
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K"}
response=$(watchrequest "${line}")
response_to_client "${response}" ${?}
@@ -161,6 +162,18 @@ main() {
response_to_client "${response}" ${?}
break
;;
get_txns_by_watchlabel)
# curl (GET) 192.168.111.152:8080/get_txns_by_watchlabel/<label>/<count>
response=$(get_txns_by_watchlabel $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3) $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4))
response_to_client "${response}" ${?}
break
;;
get_unused_addresses_by_watchlabel)
# curl (GET) 192.168.111.152:8080/get_unused_addresses_by_watchlabel/<label>/<count>
response=$(get_unused_addresses_by_watchlabel $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3) $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4))
response_to_client "${response}" ${?}
break
;;
conf)
# curl (GET) 192.168.111.152:8080/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387
@@ -225,6 +238,13 @@ main() {
response_to_client "${response}" ${?}
break
;;
get_txns_spending)
# curl (GET) http://192.168.111.152:8080/get_txns_spending/20/10
response=$(get_txns_spending $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3) $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f4))
response_to_client "${response}" ${?}
break
;;
getbalance)
# curl (GET) http://192.168.111.152:8080/getbalance
@@ -263,7 +283,7 @@ main() {
;;
spend)
# POST http://192.168.111.152:8080/spend
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233,"eventMessage":"eyJ3aGF0ZXZlciI6MTIzfQo="}
response=$(spend "${line}")
response_to_client "${response}" ${?}

View File

@@ -4,18 +4,18 @@
send_to_watcher_node() {
trace "Entering send_to_watcher_node()..."
send_to_bitcoin_node ${WATCHER_NODE_RPC_URL}/${WATCHER_BTC_NODE_DEFAULT_WALLET} ${WATCHER_NODE_RPC_CFG} $@
local node_payload
node_payload="$(send_to_bitcoin_node ${WATCHER_NODE_RPC_URL}/${WATCHER_BTC_NODE_DEFAULT_WALLET} ${WATCHER_NODE_RPC_CFG} $@)"
local returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -ne 0 ]; then
# Ok, since we now have multiple watching wallets, we need to try them all if it fails
# We have 2 right now: watching and watching-for-xpubs
send_to_watcher_node_wallet ${WATCHER_BTC_NODE_XPUB_WALLET} $@
node_payload="$(send_to_watcher_node_wallet ${WATCHER_BTC_NODE_XPUB_WALLET} $@)"
returncode=$?
trace_rc ${returncode}
fi
echo "$node_payload"
return ${returncode}
}

View File

@@ -43,7 +43,7 @@ ln_waitanyinvoice() {
paid_at=$(echo "${result}" | jq -r ".paid_at")
sql "UPDATE ln_invoice SET status=\"${status}\", pay_index=${pay_index}, msatoshi_received=${msatoshi_received}, paid_at=${paid_at} WHERE bolt11=\"${bolt11}\""
row=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE NOT calledback AND bolt11=\"${bolt11}\"")
row=$(sql "SELECT id, label, bolt11, callback_url, payment_hash, msatoshi, status, pay_index, msatoshi_received, paid_at, description, expires_at FROM ln_invoice WHERE callback_url<>\"\" AND NOT calledback AND bolt11=\"${bolt11}\"")
if [ -n "${row}" ]; then
ln_manage_callback ${row}

View File

@@ -10,7 +10,7 @@ spend() {
local request=${1}
local address=$(echo "${request}" | jq -r ".address")
trace "[spend] address=${address}"
local amount=$(echo "${request}" | jq ".amount" | awk '{ printf "%.8f", $0 }')
local amount=$(echo "${request}" | jq -r ".amount" | awk '{ printf "%.8f", $0 }')
trace "[spend] amount=${amount}"
local response
local id_inserted
@@ -39,10 +39,30 @@ spend() {
local tx_replaceable=$(echo "${tx_details}" | jq '.result."bip125-replaceable"')
tx_replaceable=$([ ${tx_replaceable} = "yes" ] && echo 1 || echo 0)
local fees=$(echo "${tx_details}" | jq '.result.fee | fabs' | awk '{ printf "%.8f", $0 }')
local rawtx=$(echo "${tx_details}" | jq '.result.hex')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
echo "${tx_raw_details}" > rawtx-${txid}.blob
########################################################################################################
# Let's publish the event if needed
local event_message
event_message=$(echo "${request}" | jq -er ".eventMessage")
if [ "$?" -ne "0" ]; then
# event_message tag null, so there's no event_message
trace "[spend] event_message="
event_message=
else
# There's an event message, let's publish it!
trace "[spend] mosquitto_pub -h broker -t spend -m \"{\"txid\":\"${txid}\",\"address\":\"${address}\",\"amount\":${tx_amount},\"eventMessage\":\"${event_message}\"}\""
response=$(mosquitto_pub -h broker -t spend -m "{\"txid\":\"${txid}\",\"address\":\"${address}\",\"amount\":${tx_amount},\"eventMessage\":\"${event_message}\"}")
returncode=$?
trace_rc ${returncode}
fi
########################################################################################################
# Let's insert the txid in our little DB -- then we'll already have it when receiving confirmation
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${rawtx})"
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, readfile('rawtx-${txid}.blob'))"
trace_rc $?
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
trace_rc $?
@@ -51,6 +71,9 @@ spend() {
data="{\"status\":\"accepted\""
data="${data},\"hash\":\"${txid}\"}"
# Delete the temp file containing the raw tx (see above)
rm rawtx-${txid}.blob
else
local message=$(echo "${response}" | jq -e ".error.message")
data="{\"message\":${message}}"
@@ -100,6 +123,32 @@ bumpfee() {
return ${returncode}
}
get_txns_spending() {
trace "Entering get_txns_spending()... with count: $1 , skip: $2"
local count="$1"
local skip="$2"
local response
local data="{\"method\":\"listtransactions\",\"params\":[\"*\",${count:-10},${skip:-0}]}"
response=$(send_to_spender_node "${data}")
local returncode=$?
trace_rc ${returncode}
trace "[get_txns_spending] response=${response}"
if [ "${returncode}" -eq 0 ]; then
local txns=$(echo ${response} | jq -rc ".result")
trace "[get_txns_spending] txns=${txns}"
data="{\"txns\":${txns}}"
else
trace "[get_txns_spending] Coudn't get txns!"
data=""
fi
trace "[get_txns_spending] responding=${data}"
echo "${data}"
return ${returncode}
}
getbalance() {
trace "Entering getbalance()..."
@@ -185,24 +234,22 @@ getbalancebyxpub() {
local returncode
# addresses=$(./bitcoin-cli -rpcwallet=xpubwatching01.dat getaddressesbylabel upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb | jq "keys" | tr -d '\n ')
data="{\"method\":\"getaddressesbylabel\",\"params\":[${xpub}]}"
data="{\"method\":\"getaddressesbylabel\",\"params\":[\"${xpub}\"]}"
trace "[getbalancebyxpub] data=${data}"
addresses=$(send_to_xpub_watcher_wallet ${data} | jq "keys" | tr -d '\n ')
addresses=$(send_to_xpub_watcher_wallet ${data} | jq ".result | keys" | tr -d '\n ')
# ./bitcoin-cli -rpcwallet=xpubwatching01.dat listunspent 0 9999999 "$addresses" | jq "[.[].amount] | add"
data="{\"method\":\"listunspent\",\"params\":[0, 9999999, \"${addresses}\"]}"
data="{\"method\":\"listunspent\",\"params\":[0,9999999,${addresses}]}"
trace "[getbalancebyxpub] data=${data}"
balance=$(send_to_xpub_watcher_wallet ${data} | jq "[.[].amount] | add | . * 100000000 | trunc | . / 100000000")
balance=$(send_to_xpub_watcher_wallet ${data} | jq "[.result[].amount // 0 ] | add | . * 100000000 | trunc | . / 100000000")
returncode=$?
trace_rc ${returncode}
trace "[getbalancebyxpub] balance=${balance}"
data="{\"event\":\"${event}\",\"xpub\":\"${xpub}\",\"balance\":${balance}}"
data="{\"event\":\"${event}\",\"xpub\":\"${xpub}\",\"balance\":${balance:-0}}"
echo "${data}"
return ${returncode}
return "${returncode}"
}
getnewaddress() {
@@ -315,10 +362,12 @@ batchspend() {
local tx_replaceable=$(echo "${tx_details}" | jq '.result."bip125-replaceable"')
tx_replaceable=$([ ${tx_replaceable} = "yes" ] && echo 1 || echo 0)
local fees=$(echo "${tx_details}" | jq '.result.fee | fabs' | awk '{ printf "%.8f", $0 }')
local rawtx=$(echo "${tx_details}" | jq '.result.hex')
# Sometimes raw tx are too long to be passed as paramater, so let's write
# it to a temp file for it to be read by sqlite3 and then delete the file
echo "${tx_raw_details}" > rawtx-${txid}.blob
# Let's insert the txid in our little DB -- then we'll already have it when receiving confirmation
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${rawtx})"
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, raw_tx) VALUES (\"${txid}\", ${tx_hash}, 0, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, readfile('rawtx-${txid}.blob'))"
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
@@ -330,6 +379,9 @@ batchspend() {
data="{\"status\":\"accepted\""
data="${data},\"hash\":\"${txid}\"}"
# Delete the temp file containing the raw tx (see above)
rm rawtx-${txid}.blob
else
local message=$(echo "${response}" | jq -e ".error.message")
data="{\"message\":${message}}"

View File

@@ -11,14 +11,33 @@ watchrequest() {
local returncode
local request=${1}
local address=$(echo "${request}" | jq -r ".address")
local cb0conf_url=$(echo "${request}" | jq -r ".unconfirmedCallbackURL")
local cb1conf_url=$(echo "${request}" | jq -r ".confirmedCallbackURL")
local address=$(echo "${request}" | jq -er ".address")
local cb0conf_url
cb0conf_url=$(echo "${request}" | jq -er ".unconfirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# unconfirmedCallbackURL tag null, so there's no unconfirmedCallbackURL
trace "[watchrequest] unconfirmedCallbackURL="
unconfirmedCallbackURL=
fi
local cb1conf_url
cb1conf_url=$(echo "${request}" | jq -er ".confirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# confirmedCallbackURL tag null, so there's no confirmedCallbackURL
trace "[watchrequest] confirmedCallbackURL="
confirmedCallbackURL=
fi
local event_message
event_message=$(echo "${request}" | jq -er ".eventMessage")
if [ "$?" -ne "0" ]; then
# event_message tag null, so there's no event_message
trace "[watchrequest] event_message="
event_message=
fi
local imported
local inserted
local id_inserted
local result
trace "[watchrequest] Watch request on address (${address}), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url})"
trace "[watchrequest] Watch request on address (${address}), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url}) with event_message=${event_message}"
result=$(importaddress_rpc "${address}")
returncode=$?
@@ -29,7 +48,7 @@ watchrequest() {
imported=0
fi
sql "INSERT OR REPLACE INTO watching (address, watching, callback0conf, callback1conf, imported) VALUES (\"${address}\", 1, \"${cb0conf_url}\", \"${cb1conf_url}\", ${imported})"
sql "INSERT OR REPLACE INTO watching (address, watching, callback0conf, callback1conf, imported, event_message) VALUES (\"${address}\", 1, \"${cb0conf_url}\", \"${cb1conf_url}\", ${imported}, '${event_message}')"
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
@@ -63,7 +82,8 @@ watchrequest() {
\"estimatesmartfee2blocks\":\"${fees2blocks}\",
\"estimatesmartfee6blocks\":\"${fees6blocks}\",
\"estimatesmartfee36blocks\":\"${fees36blocks}\",
\"estimatesmartfee144blocks\":\"${fees144blocks}\"}"
\"estimatesmartfee144blocks\":\"${fees144blocks}\",
\"eventMessage\":\"${event_message}\"}"
trace "[watchrequest] responding=${data}"
echo "${data}"