diff --git a/README.md b/README.md
index 224a5f0..ff1d4a5 100644
--- a/README.md
+++ b/README.md
@@ -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
diff --git a/api_auth_docker/api-sample.properties b/api_auth_docker/api-sample.properties
index 4d59d81..95e3596 100644
--- a/api_auth_docker/api-sample.properties
+++ b/api_auth_docker/api-sample.properties
@@ -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_getbalancebyxpub=spender
action_getbalancebyxpublabel=spender
diff --git a/cyphernodeconf_docker/help.json b/cyphernodeconf_docker/help.json
index c321031..fe9a314 100644
--- a/cyphernodeconf_docker/help.json
+++ b/cyphernodeconf_docker/help.json
@@ -35,7 +35,7 @@
"lightning_nodecolor": "LN nodes have colors. Choose the color you want for yours in RGB format (RRGGBB). For example, pure red would be ff0000.",
"lightning_datapath": "Path name to where LN's data files are stored. This directory will be mounted into the LN node's container. If running on OSX, check mountable directories in Docker's File Sharing configs.",
"lightning_datapath_custom": " ",
- "lightning_expose": "By default, LN node port will be published 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 published outside of Docker. Do you want to expose it so that your node can be accessed from outside of the Docker network?",
"otsclient_datapath": "Full path where the OTS files will be stored. This path will be mounted into the otsclient container which will create the OTS files when stamping and update them when upgrading stamps. It will also be mounted to the proxy container so that it can serve the ots_getfile and send the OTS files to clients. If running on OSX, check mountable directories in Docker's File Sharing configs.",
"otsclient_datapath_custom": " ",
"installer_mode": "Only one installation mode is supported, right now: local docker (self-hosted). Choose wisely ;-)",
diff --git a/cyphernodeconf_docker/lib/app.js b/cyphernodeconf_docker/lib/app.js
index e9cb672..2e96123 100644
--- a/cyphernodeconf_docker/lib/app.js
+++ b/cyphernodeconf_docker/lib/app.js
@@ -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']
}
diff --git a/cyphernodeconf_docker/package-lock.json b/cyphernodeconf_docker/package-lock.json
index 42a79a4..e5ee0a4 100644
--- a/cyphernodeconf_docker/package-lock.json
+++ b/cyphernodeconf_docker/package-lock.json
@@ -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",
diff --git a/cyphernodeconf_docker/templates/gatekeeper/api.properties b/cyphernodeconf_docker/templates/gatekeeper/api.properties
index f2f4739..0a85497 100644
--- a/cyphernodeconf_docker/templates/gatekeeper/api.properties
+++ b/cyphernodeconf_docker/templates/gatekeeper/api.properties
@@ -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_getbalancebyxpub=spender
action_getbalancebyxpublabel=spender
@@ -57,4 +61,4 @@ action_ln_connectfund=spender
action_conf=internal
action_newblock=internal
action_executecallbacks=internal
-action_ots_backoffice=internal
\ No newline at end of file
+action_ots_backoffice=internal
diff --git a/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
index 7971a87..6e73a9d 100644
--- a/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
+++ b/cyphernodeconf_docker/templates/installer/docker/docker-compose.yaml
@@ -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
diff --git a/cyphernodeconf_docker/templates/installer/testdeployment.sh b/cyphernodeconf_docker/templates/installer/testdeployment.sh
index 94dde14..d08ea2e 100644
--- a/cyphernodeconf_docker/templates/installer/testdeployment.sh
+++ b/cyphernodeconf_docker/templates/installer/testdeployment.sh
@@ -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 , 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
diff --git a/dist/setup.sh b/dist/setup.sh
index dee2b03..a9f8ecc 100755
--- a/dist/setup.sh
+++ b/dist/setup.sh
@@ -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 " [31mBlockchain data is not compatible, due to misconfigured nets.[0m"
- echo " [31mYour cyphernode installation is most likely broken.[0m"
- echo " [31mPlease check bitcoin.conf.cyphernode on how to repair it manually.[0m"
else
if [[ $cmpStatus == 'reindex' ]]; then
echo " [33mWarning[0m 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 " [32mclone[0m $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
}
diff --git a/doc/API.v0.md b/doc/API.v0.md
index d62a1a2..03fa6de 100644
--- a/doc/API.v0.md
+++ b/doc/API.v0.md
@@ -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
diff --git a/doc/CN-Arch.jpg b/doc/CN-Arch.jpg
index 3bb4e42..ff2f190 100644
Binary files a/doc/CN-Arch.jpg and b/doc/CN-Arch.jpg differ
diff --git a/doc/openapi/v0/cyphernode-api.yaml b/doc/openapi/v0/cyphernode-api.yaml
index c71a4fb..63f1053 100644
--- a/doc/openapi/v0/cyphernode-api.yaml
+++ b/doc/openapi/v0/cyphernode-api.yaml
@@ -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:
diff --git a/proxy_docker/app/data/cyphernode.sql b/proxy_docker/app/data/cyphernode.sql
index 97aeef0..743ce18 100644
--- a/proxy_docker/app/data/cyphernode.sql
+++ b/proxy_docker/app/data/cyphernode.sql
@@ -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
);
diff --git a/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sh b/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sh
new file mode 100644
index 0000000..83e21bf
--- /dev/null
+++ b/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sh
@@ -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
diff --git a/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sql b/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sql
new file mode 100644
index 0000000..2a95bee
--- /dev/null
+++ b/proxy_docker/app/data/sqlmigrate20191127_0.2.4-0.3.0.sql
@@ -0,0 +1 @@
+ALTER TABLE watching ADD COLUMN event_message TEXT;
diff --git a/proxy_docker/app/script/call_lightningd.sh b/proxy_docker/app/script/call_lightningd.sh
index 8811d23..dd34281 100644
--- a/proxy_docker/app/script/call_lightningd.sh
+++ b/proxy_docker/app/script/call_lightningd.sh
@@ -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}"
diff --git a/proxy_docker/app/script/callbacks_job.sh b/proxy_docker/app/script/callbacks_job.sh
index 294880a..62ff7a4 100644
--- a/proxy_docker/app/script/callbacks_job.sh
+++ b/proxy_docker/app/script/callbacks_job.sh
@@ -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}"
diff --git a/proxy_docker/app/script/confirmation.sh b/proxy_docker/app/script/confirmation.sh
index 48bbfa1..9664d62 100644
--- a/proxy_docker/app/script/confirmation.sh
+++ b/proxy_docker/app/script/confirmation.sh
@@ -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
diff --git a/proxy_docker/app/script/getactivewatches.sh b/proxy_docker/app/script/getactivewatches.sh
index e639945..305abda 100644
--- a/proxy_docker/app/script/getactivewatches.sh
+++ b/proxy_docker/app/script/getactivewatches.sh
@@ -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()..."
diff --git a/proxy_docker/app/script/newblock.sh b/proxy_docker/app/script/newblock.sh
index c266eeb..1ae3d05 100644
--- a/proxy_docker/app/script/newblock.sh
+++ b/proxy_docker/app/script/newblock.sh
@@ -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
}
diff --git a/proxy_docker/app/script/requesthandler.sh b/proxy_docker/app/script/requesthandler.sh
index dad4ca2..4478a87 100644
--- a/proxy_docker/app/script/requesthandler.sh
+++ b/proxy_docker/app/script/requesthandler.sh
@@ -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/