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 " 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 } 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/