mirror of
https://github.com/AskDavis/cyphernode.git
synced 2026-01-01 04:25:58 -08:00
First push from Satoshi Portal's own cyphernode
This commit is contained in:
56
proxy_docker/Dockerfile
Normal file
56
proxy_docker/Dockerfile
Normal file
@@ -0,0 +1,56 @@
|
||||
FROM alpine
|
||||
|
||||
ARG USER_ID
|
||||
ARG GROUP_ID
|
||||
ENV USERNAME proxyuser
|
||||
ENV HOME /${USERNAME}
|
||||
ENV USER_ID ${USER_ID:-1000}
|
||||
ENV GROUP_ID ${GROUP_ID:-1000}
|
||||
|
||||
RUN addgroup -g ${GROUP_ID} ${USERNAME} \
|
||||
&& adduser -u ${USER_ID} -G ${USERNAME} -D -s /bin/sh -h ${HOME} ${USERNAME}
|
||||
|
||||
RUN apk add --update --no-cache \
|
||||
sqlite \
|
||||
jq \
|
||||
curl
|
||||
|
||||
COPY --chown=proxyuser app/script/callbacks_job.sh ${HOME}/callbacks_job.sh
|
||||
COPY --chown=proxyuser app/script/blockchainrpc.sh ${HOME}/blockchainrpc.sh
|
||||
COPY --chown=proxyuser app/script/call_lightningd.sh ${HOME}/call_lightningd.sh
|
||||
COPY --chown=proxyuser app/script/bitcoin.sh ${HOME}/bitcoin.sh
|
||||
COPY --chown=proxyuser app/script/requesthandler.sh ${HOME}/requesthandler.sh
|
||||
COPY --chown=proxyuser app/script/watchrequest.sh ${HOME}/watchrequest.sh
|
||||
COPY --chown=proxyuser app/script/walletoperations.sh ${HOME}/walletoperations.sh
|
||||
COPY --chown=proxyuser app/script/confirmation.sh ${HOME}/confirmation.sh
|
||||
COPY --chown=proxyuser app/config/watcher_btcnode_curlcfg.properties ${HOME}/watcher_btcnode_curlcfg.properties
|
||||
COPY --chown=proxyuser app/config/spender_btcnode_curlcfg.properties ${HOME}/spender_btcnode_curlcfg.properties
|
||||
COPY --chown=proxyuser app/config/config.properties ${HOME}/config.properties
|
||||
COPY --chown=proxyuser app/script/startproxy.sh ${HOME}/startproxy.sh
|
||||
COPY --chown=proxyuser app/script/trace.sh ${HOME}/trace.sh
|
||||
COPY --chown=proxyuser app/script/sendtobitcoinnode.sh ${HOME}/sendtobitcoinnode.sh
|
||||
COPY --chown=proxyuser app/script/responsetoclient.sh ${HOME}/responsetoclient.sh
|
||||
COPY --chown=proxyuser app/script/importaddress.sh ${HOME}/importaddress.sh
|
||||
COPY --chown=proxyuser app/script/sql.sh ${HOME}/sql.sh
|
||||
COPY --chown=proxyuser app/data/watching.sql ${HOME}/watching.sql
|
||||
COPY --chown=proxyuser app/script/computefees.sh ${HOME}/computefees.sh
|
||||
COPY --chown=proxyuser app/script/unwatchrequest.sh ${HOME}/unwatchrequest.sh
|
||||
COPY --chown=proxyuser app/script/getactivewatches.sh ${HOME}/getactivewatches.sh
|
||||
COPY --chown=proxyuser app/script/utils.sh ${HOME}/utils.sh
|
||||
COPY --chown=proxyuser app/script/manage_missed_conf.sh ${HOME}/manage_missed_conf.sh
|
||||
COPY --chown=proxyuser app/script/tests.sh ${HOME}/tests.sh
|
||||
COPY --chown=proxyuser app/script/tests-cb.sh ${HOME}/tests-cb.sh
|
||||
COPY --chown=proxyuser app/bin/lightning-cli_x86 ${HOME}/lightning-cli
|
||||
|
||||
USER ${USERNAME}
|
||||
WORKDIR ${HOME}
|
||||
|
||||
RUN chmod +x startproxy.sh requesthandler.sh \
|
||||
&& chmod 600 watcher_btcnode_curlcfg.properties \
|
||||
&& chmod 600 spender_btcnode_curlcfg.properties \
|
||||
&& chmod 600 config.properties \
|
||||
&& mkdir db
|
||||
|
||||
VOLUME ["${HOME}/db", "${HOME}/.lightning"]
|
||||
|
||||
ENTRYPOINT ["./startproxy.sh"]
|
||||
520
proxy_docker/README.md
Normal file
520
proxy_docker/README.md
Normal file
@@ -0,0 +1,520 @@
|
||||
# Cyphernode Proxy
|
||||
|
||||
We assume you are the user pi on a Raspberry Pi.
|
||||
|
||||
## Create proxyuser that will run the processes
|
||||
|
||||
Log in your host and:
|
||||
|
||||
```shell
|
||||
sudo useradd proxyuser
|
||||
```
|
||||
|
||||
## Configure your container by modifying `env.properties` file
|
||||
|
||||
```properties
|
||||
TRACING=1
|
||||
WATCHER_BTC_NODE_RPC_URL=btcnode:18332/wallet/watching01.dat
|
||||
SPENDER_BTC_NODE_RPC_URL=btcnode:18332/wallet/spending01.dat
|
||||
PROXY_LISTENING_PORT=8888
|
||||
# Variable substitutions don't work
|
||||
DB_PATH=/proxyuser/db
|
||||
DB_FILE=/proxyuser/db/proxydb
|
||||
# Pycoin container
|
||||
PYCOIN_CONTAINER=pycoinnode:7777
|
||||
# OTS container
|
||||
OTS_CONTAINER=otsnode:6666
|
||||
```
|
||||
|
||||
## Set your Watching Bitcoin node RPC credentials in `app/config/watcher_btcnode_curlcfg.properties`
|
||||
|
||||
```properties
|
||||
user=rpc_username:rpc_password
|
||||
```
|
||||
|
||||
## Set your Spending Bitcoin node RPC credentials in `app/config/spender_btcnode_curlcfg.properties`
|
||||
|
||||
```properties
|
||||
user=rpc_username:rpc_password
|
||||
```
|
||||
|
||||
## Set your address derivation information in `app/config/derivation.properties`
|
||||
|
||||
```properties
|
||||
derivation.xpub=upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb
|
||||
derivation.path=0/n
|
||||
watchingnode.pruned=false
|
||||
```
|
||||
|
||||
## Building docker image
|
||||
|
||||
```shell
|
||||
docker build -t btcproxyimg --build-arg USER_ID=$(id -u proxyuser) --build-arg GROUP_ID=$(id -g proxyuser) .
|
||||
```
|
||||
|
||||
## Create sqlite3 database path and give rights
|
||||
|
||||
```shell
|
||||
mkdir ~/btcproxydb ; sudo chown -R proxyuser:pi ~/btcproxydb ; sudo chmod g+ws ~/btcproxydb
|
||||
```
|
||||
|
||||
## What you MUST have in your Watching Bitcoin node's bitcoin.conf file
|
||||
(or something similar)
|
||||
(*Not in the Spending Bitcoin node!*)
|
||||
|
||||
```properties
|
||||
walletnotify=curl cyphernode:8888/conf/%s
|
||||
```
|
||||
|
||||
## Current API
|
||||
|
||||
### Watch a Bitcoin Address (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/watch
|
||||
with body...
|
||||
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "291",
|
||||
"event": "watch",
|
||||
"imported": "1",
|
||||
"inserted": "1",
|
||||
"address": "2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp",
|
||||
"unconfirmedCallbackURL": "192.168.133.233:1111/callback0conf",
|
||||
"confirmedCallbackURL": "192.168.133.233:1111/callback1conf",
|
||||
"estimatesmartfee2blocks": "0.000010",
|
||||
"estimatesmartfee6blocks": "0.000010",
|
||||
"estimatesmartfee36blocks": "0.000010",
|
||||
"estimatesmartfee144blocks": "0.000010"
|
||||
}
|
||||
```
|
||||
|
||||
### Un-watch a previously watched Bitcoin Address (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"event": "unwatch",
|
||||
"address": "2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp"
|
||||
}
|
||||
```
|
||||
|
||||
### Get a list of Bitcoin addresses being watched (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getactivewatches
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"watches": [
|
||||
{
|
||||
"id":"291",
|
||||
"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp",
|
||||
"imported":"1",
|
||||
"unconfirmedCallbackURL":"192.168.133.233:1111/callback0conf",
|
||||
"confirmedCallbackURL":"192.168.133.233:1111/callback1conf",
|
||||
"watching_since":"2018-09-06 21:14:03"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Confirm a Transaction on Watched Address (called by Bitcoin node on transaction confirmations)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":"confirmed"
|
||||
}
|
||||
```
|
||||
|
||||
### Get the Best Block Hash (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getbestblockhash
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":"00000000000000262588c21afbf9e1da151daf10b11215d501271163f26ea74a",
|
||||
"error":null,
|
||||
"id":null
|
||||
}
|
||||
```
|
||||
|
||||
### Get Block Info (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":
|
||||
{
|
||||
"hash":"000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea",
|
||||
"confirmations":124329,
|
||||
"strippedsize":8067,
|
||||
"size":8067,
|
||||
"weight":32268,
|
||||
"height":1288528,
|
||||
"version":536870912,
|
||||
"versionHex":"20000000",
|
||||
"merkleroot":"f1596255c357713c9827a739b17c8445cdcb81c4d336e24516c66f714a8f7030",
|
||||
"tx":["65759382e7047f89f7e80676026c10401d47ce47ea997138251c59ca58f28a03","4d2d0cdd89061ce4bb8bdf5502aec9799e5065d205e8ab75c6598bd90a0e6e4c",[...],"a6d2fda52467aa7ca1271529ded5510bd12ad58f99f73fe995f50691aea4eb06"],
|
||||
"time":1521404668,
|
||||
"mediantime":1521398617,
|
||||
"nonce":4209349744,
|
||||
"bits":"1d00ffff",
|
||||
"difficulty":1,
|
||||
"chainwork":"000000000000000000000000000000000000000000000037e37554821063611f",
|
||||
"nTx":32,
|
||||
"previousblockhash":"000000005b55cefec377f7f73e656fef835a928b6eeb2060a89e7eb23a573c49",
|
||||
"nextblockhash":"000000000ab13797b6dddd5e28d2ed62b937cd65ecf49b1f9d75108b9ea500f9"
|
||||
},
|
||||
"error":null,
|
||||
"id":null
|
||||
}
|
||||
```
|
||||
|
||||
### Get the Best Block Info (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getbestblockinfo
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":
|
||||
{
|
||||
"hash":"000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea",
|
||||
"confirmations":124329,
|
||||
"strippedsize":8067,
|
||||
"size":8067,
|
||||
"weight":32268,
|
||||
"height":1288528,
|
||||
"version":536870912,
|
||||
"versionHex":"20000000",
|
||||
"merkleroot":"f1596255c357713c9827a739b17c8445cdcb81c4d336e24516c66f714a8f7030",
|
||||
"tx":["65759382e7047f89f7e80676026c10401d47ce47ea997138251c59ca58f28a03","4d2d0cdd89061ce4bb8bdf5502aec9799e5065d205e8ab75c6598bd90a0e6e4c",[...],"a6d2fda52467aa7ca1271529ded5510bd12ad58f99f73fe995f50691aea4eb06"],
|
||||
"time":1521404668,
|
||||
"mediantime":1521398617,
|
||||
"nonce":4209349744,
|
||||
"bits":"1d00ffff",
|
||||
"difficulty":1,
|
||||
"chainwork":"000000000000000000000000000000000000000000000037e37554821063611f",
|
||||
"nTx":32,
|
||||
"previousblockhash":"000000005b55cefec377f7f73e656fef835a928b6eeb2060a89e7eb23a573c49",
|
||||
"nextblockhash":"000000000ab13797b6dddd5e28d2ed62b937cd65ecf49b1f9d75108b9ea500f9"
|
||||
},
|
||||
"error":null,
|
||||
"id":null
|
||||
}
|
||||
```
|
||||
|
||||
### Get a transaction details (node's getrawtransaction) (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"result":
|
||||
{
|
||||
"txid":"af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648",
|
||||
"hash":"af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648",
|
||||
"version":1,
|
||||
"size":223,
|
||||
"vsize":223,
|
||||
"locktime":0,
|
||||
"vin":[
|
||||
{
|
||||
"txid":"53a0e2ffa456b97d3944b652bba771221e60f4d852546bd6b351d33261b3e8b6",
|
||||
"vout":0,
|
||||
"scriptSig":
|
||||
{
|
||||
"asm":"30440220127c4adc1cf985cd884c383e69440ce4d48a0c4fdce6bf9d70faa0ee8092acb80220632cb6c99ded7f261814e602fc8fa8e7fe8cb6a95d45c497846b8624f7d19b3c[ALL] 03df001c8b58ac42b6cbfc2223b8efaa7e9a1911e529bd2c8b7f90140079034e75",
|
||||
"hex":"4730440220127c4adc1cf985cd884c383e69440ce4d48a0c4fdce6bf9d70faa0ee8092acb80220632cb6c99ded7f261814e602fc8fa8e7fe8cb6a95d45c497846b8624f7d19b3c012103df001c8b58ac42b6cbfc2223b8efaa7e9a1911e529bd2c8b7f90140079034e75"
|
||||
},
|
||||
"sequence":4294967295
|
||||
}
|
||||
],
|
||||
"vout":[
|
||||
{
|
||||
"value":0.84000000,
|
||||
"n":0,
|
||||
"scriptPubKey":
|
||||
{
|
||||
"asm":"OP_HASH160 c449a7fafb3b13b2952e064f2c3c58e851bb9430 OP_EQUAL",
|
||||
"hex":"a914c449a7fafb3b13b2952e064f2c3c58e851bb943087",
|
||||
"reqSigs":1,
|
||||
"type":"scripthash",
|
||||
"addresses":[
|
||||
"2NB96fbwy8eoHttuZTtbwvvhEYrBwz494ov"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"value":0.01890000,
|
||||
"n":1,
|
||||
"scriptPubKey":
|
||||
{
|
||||
"asm":"OP_DUP OP_HASH160 b0379374df5eab8be9a21ee96711712bdb781a95 OP_EQUALVERIFY OP_CHECKSIG",
|
||||
"hex":"76a914b0379374df5eab8be9a21ee96711712bdb781a9588ac",
|
||||
"reqSigs":1,
|
||||
"type":"pubkeyhash",
|
||||
"addresses":[
|
||||
"mwahoJcaVuy2TiMtGDZV9PaujFeD9z1a1q"
|
||||
]
|
||||
}
|
||||
}
|
||||
],
|
||||
"hex":"0100000001b6e8b36132d351b3d66b5452d8f4601e2271a7bb52b644397db9[...]4df5eab8be9a21ee96711712bdb781a9588ac00000000",
|
||||
"blockhash":"000000009249e7d725cc087cb781ade1dbfaf2bd777822948d5fccd4044f8299",
|
||||
"confirmations":1106162,
|
||||
"time":1415240575,
|
||||
"blocktime":1415240575
|
||||
},
|
||||
"error":null,
|
||||
"id":null
|
||||
}
|
||||
```
|
||||
|
||||
### Try missed callbacks (called by proxycronnode, a CRON job that retries if something went wrong)
|
||||
[See cron_docker](../cron_docker)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/executecallbacks
|
||||
```
|
||||
|
||||
Proxy response: EMPTY
|
||||
|
||||
### Get spending wallet's balance (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getbalance
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"balance":1.51911837
|
||||
}
|
||||
```
|
||||
|
||||
### Get a new Bitcoin address from spending wallet (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/getnewaddress
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"address":"2NEC972DZpRM7SfuJUG9rYEix2P9A8qsNKF"
|
||||
}
|
||||
```
|
||||
|
||||
### Spend coins from spending wallet (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/spend
|
||||
with body...
|
||||
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"hash": "af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648"
|
||||
}
|
||||
```
|
||||
|
||||
### Add an output to the next batched transaction (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/addtobatch
|
||||
with body...
|
||||
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
```
|
||||
|
||||
Proxy response: EMPTY
|
||||
|
||||
### Spend a batched transaction with outputs added with addtobatch (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/batchspend
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"status": "accepted",
|
||||
"hash": "af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648"
|
||||
}
|
||||
```
|
||||
|
||||
### Get derived address(es) using path in config and provided index (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/deriveindex/25-30
|
||||
GET http://cyphernode:8888/deriveindex/34
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"addresses":[
|
||||
{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},
|
||||
{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},
|
||||
{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},
|
||||
{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},
|
||||
{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},
|
||||
{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get derived address(es) using provided path and index (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/derivepubpath
|
||||
with body...
|
||||
{"pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/25-30"}
|
||||
|
||||
or
|
||||
|
||||
{"pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/34"}
|
||||
|
||||
or
|
||||
|
||||
{"pub32":"vpub5SLqN2bLY4WeZF3kL4VqiWF1itbf3A6oRrq9aPf16AZMVWYCuN9TxpAZwCzVgW94TNzZPNc9XAHD4As6pdnExBtCDGYRmNJrcJ4eV9hNqcv","path":"0/25-30"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"addresses":[
|
||||
{"address":"mz3bWMW3BWGT9YGDjJwS8TfhJMMtZ91Frm"},
|
||||
{"address":"mkjmKEX3KJrVpiqLSSxKB6jjgm3WhPnrv8"},
|
||||
{"address":"mk43Tmf6E5nsmETTaNMTZK9TikaeVJRJ4a"},
|
||||
{"address":"n1SEcVHHKpHyNr695JpXNdH6b9cWQ26qkt"},
|
||||
{"address":"mzWqwZkA31kYVy1kpMoZgvfzSDyGgEi7Yg"},
|
||||
{"address":"mp5jtEDNa88xfSQGs5yYQGk7guGWvaG4ci"}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### Get info from Lightning Network node (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_getinfo
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "03bb990f43e6a6eccb223288d32fcb91209b12370c0a8bf5cdf4ad7bc11e33f253",
|
||||
"alias": "SatoshiPortal01",
|
||||
"color": "008000",
|
||||
"address": [
|
||||
],
|
||||
"binding": [
|
||||
{
|
||||
"type": "ipv6",
|
||||
"address": "::",
|
||||
"port": 9735
|
||||
},
|
||||
{
|
||||
"type": "ipv4",
|
||||
"address": "0.0.0.0",
|
||||
"port": 9735
|
||||
}
|
||||
],
|
||||
"version": "v0.6.1rc1-40-gae61f64",
|
||||
"blockheight": 1412861,
|
||||
"network": "testnet"
|
||||
}
|
||||
```
|
||||
|
||||
### Create a Lightning Network invoice (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ln_create_invoice
|
||||
with body...
|
||||
{"msatoshi":10000,"label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":900}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"payment_hash": "fd27edf261d4b089c3478dece4f2c92c8c68db7be3999e89d452d39c083ad00f",
|
||||
"expires_at": 1536593926,
|
||||
"bolt11": "lntb100n1pdedryzpp5l5n7munp6jcgns683hkwfukf9jxx3kmmuwveazw52tfeczp66q8sdqagfukcmrnyphhyer9wgszxvfsxc6rjxqzuycqp2ak5feh7x7wkkt76uc5ptzcv90jhzhs5swzefv9344hnv74c25dvsstx7l24y46sx5tnkenu480pe06wtly2h5lrj63vszzgrxt4grkcqcltquj"
|
||||
}
|
||||
```
|
||||
|
||||
### Pay a Lightning Network invoice (called by application)
|
||||
|
||||
```http
|
||||
POST http://cyphernode:8888/ln_pay
|
||||
with body...
|
||||
{"bolt11":"lntb1pdca82tpp5gv8mn5jqlj6xztpnt4r472zcyrwf3y2c3cvm4uzg2gqcnj90f83qdp2gf5hgcm0d9hzqnm4w3kx2apqdaexgetjyq3nwvpcxgcqp2g3d86wwdfvyxcz7kce7d3n26d2rw3wf5tzpm2m5fl2z3mm8msa3xk8nv2y32gmzlhwjved980mcmkgq83u9wafq9n4w28amnmwzujgqpmapcr3","expected_msatoshi":10000,"expected_description":"Bitcoin Outlet order #7082"}
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"": ""
|
||||
}
|
||||
```
|
||||
|
||||
### Get a new Bitcoin address from the Lightning Network node (to fund it) (called by application)
|
||||
|
||||
```http
|
||||
GET http://cyphernode:8888/ln_newaddr
|
||||
```
|
||||
|
||||
Proxy response:
|
||||
|
||||
```json
|
||||
{
|
||||
"address": "tb1q9n8jfwe9qvlgczfxa5n4pe7haarqflzerqfhk9"
|
||||
}
|
||||
```
|
||||
5
proxy_docker/app/bin/README.md
Normal file
5
proxy_docker/app/bin/README.md
Normal file
@@ -0,0 +1,5 @@
|
||||
# Nota bene
|
||||
|
||||
lightning-cli binary has been pre-compiled in an Alpine docker container.
|
||||
|
||||
Use lightning-cli_arm for armhf architecture, lightning-cli_x86 for x86_64.
|
||||
BIN
proxy_docker/app/bin/lightning-cli_arm
Normal file
BIN
proxy_docker/app/bin/lightning-cli_arm
Normal file
Binary file not shown.
BIN
proxy_docker/app/bin/lightning-cli_x86
Normal file
BIN
proxy_docker/app/bin/lightning-cli_x86
Normal file
Binary file not shown.
3
proxy_docker/app/config/config.properties
Normal file
3
proxy_docker/app/config/config.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
derivation.pub32=upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb
|
||||
derivation.path=0/n
|
||||
watchingnode.pruned=false
|
||||
@@ -0,0 +1 @@
|
||||
user=rpc_username:rpc_password
|
||||
@@ -0,0 +1 @@
|
||||
user=rpc_username:rpc_password
|
||||
55
proxy_docker/app/data/watching.sql
Normal file
55
proxy_docker/app/data/watching.sql
Normal file
@@ -0,0 +1,55 @@
|
||||
PRAGMA foreign_keys = ON;
|
||||
|
||||
CREATE TABLE watching (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
address TEXT,
|
||||
watching INTEGER DEFAULT FALSE,
|
||||
callback0conf TEXT,
|
||||
calledback0conf INTEGER DEFAULT FALSE,
|
||||
callback1conf TEXT,
|
||||
calledback1conf INTEGER DEFAULT FALSE,
|
||||
imported INTEGER DEFAULT FALSE,
|
||||
inserted_ts INTEGER DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_watching_address ON watching (address);
|
||||
|
||||
CREATE TABLE watching_tx (
|
||||
watching_id INTEGER REFERENCES watching,
|
||||
tx_id INTEGER REFERENCES tx,
|
||||
vout INTEGER,
|
||||
amount REAL
|
||||
);
|
||||
CREATE UNIQUE INDEX idx_watching_tx ON watching_tx (watching_id, tx_id);
|
||||
|
||||
CREATE TABLE tx (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
txid TEXT UNIQUE,
|
||||
hash TEXT UNIQUE,
|
||||
confirmations INTEGER DEFAULT 0,
|
||||
timereceived INTEGER,
|
||||
fee REAL,
|
||||
size INTEGER,
|
||||
vsize INTEGER,
|
||||
is_replaceable INTEGER,
|
||||
blockhash TEXT,
|
||||
blockheight INTEGER,
|
||||
blocktime INTEGER,
|
||||
raw_tx TEXT,
|
||||
inserted_ts INTEGER DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_tx_timereceived ON tx (timereceived);
|
||||
CREATE INDEX idx_tx_fee ON tx (fee);
|
||||
CREATE INDEX idx_tx_size ON tx (size);
|
||||
CREATE INDEX idx_tx_vsize ON tx (vsize);
|
||||
CREATE INDEX idx_tx_blockhash ON tx (blockhash);
|
||||
CREATE INDEX idx_tx_blockheight ON tx (blockheight);
|
||||
CREATE INDEX idx_tx_blocktime ON tx (blocktime);
|
||||
|
||||
CREATE TABLE recipient (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
address TEXT,
|
||||
amount REAL,
|
||||
tx_id INTEGER REFERENCES tx,
|
||||
inserted_ts INTEGER DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
CREATE INDEX idx_recipient_address ON recipient (address);
|
||||
46
proxy_docker/app/script/bitcoin.sh
Normal file
46
proxy_docker/app/script/bitcoin.sh
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./utils.sh
|
||||
|
||||
deriveindex()
|
||||
{
|
||||
trace "Entering deriveindex()..."
|
||||
|
||||
local index=${1}
|
||||
trace "[deriveindex] index=${index}"
|
||||
|
||||
local pub32=$(get_prop "derivation.pub32")
|
||||
local path=$(get_prop "derivation.path" | sed -En "s/n/${index}/p")
|
||||
# pub32=$(grep "derivation.pub32" config.properties | cut -d'=' -f2)
|
||||
# path=$(grep "derivation.path" config.properties | cut -d'=' -f2 | sed -En "s/n/${index}/p")
|
||||
|
||||
local data="{\"pub32\":\"${pub32}\",\"path\":\"${path}\"}"
|
||||
trace "[deriveindex] data=${data}"
|
||||
|
||||
send_to_pycoin "${data}"
|
||||
return $?
|
||||
}
|
||||
|
||||
send_to_pycoin()
|
||||
{
|
||||
trace "Entering send_to_pycoin()..."
|
||||
|
||||
local data=${1}
|
||||
local result
|
||||
local returncode
|
||||
|
||||
trace "[send_to_pycoin] curl -s -H \"Content-Type: application/json\" -d \"${data}\" ${PYCOIN_CONTAINER}/derive"
|
||||
|
||||
result=$(curl -s -H "Content-Type: application/json" -d "${data}" ${PYCOIN_CONTAINER}/derive)
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[send_to_pycoin] result=${result}"
|
||||
|
||||
# Output response to stdout before exiting with return code
|
||||
echo "${result}"
|
||||
|
||||
trace_rc ${returncode}
|
||||
return ${returncode}
|
||||
|
||||
}
|
||||
79
proxy_docker/app/script/blockchainrpc.sh
Normal file
79
proxy_docker/app/script/blockchainrpc.sh
Normal file
@@ -0,0 +1,79 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
|
||||
get_best_block_hash()
|
||||
{
|
||||
trace "Entering get_best_block_hash()..."
|
||||
|
||||
local data='{"method":"getbestblockhash"}'
|
||||
send_to_watcher_node "${data}"
|
||||
return $?
|
||||
}
|
||||
|
||||
getestimatesmartfee()
|
||||
{
|
||||
trace "Entering getestimatesmartfee()..."
|
||||
|
||||
local nb_blocks=${1}
|
||||
trace "[getestimatesmartfee] nb_blocks=${nb_blocks}"
|
||||
send_to_watcher_node "{\"method\":\"estimatesmartfee\",\"params\":[${nb_blocks}]}" | jq ".result.feerate" | awk '{ printf "%.8f", $0 }'
|
||||
return $?
|
||||
}
|
||||
|
||||
get_block_info()
|
||||
{
|
||||
trace "Entering get_block_info()..."
|
||||
|
||||
local block_hash=${1}
|
||||
trace "[get_block_info] block_hash=${block_hash}"
|
||||
local data="{\"method\":\"getblock\",\"params\":[\"${block_hash}\"]}"
|
||||
trace "[get_block_info] data=${data}"
|
||||
send_to_watcher_node "${data}"
|
||||
return $?
|
||||
}
|
||||
|
||||
get_best_block_info()
|
||||
{
|
||||
trace "Entering get_best_block_info()..."
|
||||
|
||||
local block_hash=$(echo "$(get_best_block_hash)" | jq ".result" | tr -d '"')
|
||||
trace "[get_best_block_info] block_hash=${block_hash}"
|
||||
get_block_info ${block_hash}
|
||||
return $?
|
||||
}
|
||||
|
||||
get_rawtransaction()
|
||||
{
|
||||
trace "Entering get_rawtransaction()..."
|
||||
|
||||
local txid=${1}
|
||||
trace "[get_rawtransaction] txid=${txid}"
|
||||
|
||||
local rawtx
|
||||
rawtx=$(sql "SELECT raw_tx FROM tx WHERE txid=\"${txid}\"")
|
||||
if [ -z ${rawtx} ]; then
|
||||
trace "[get_rawtransaction] rawtx not found in DB, let's fetch the Bitcoin node"
|
||||
local data="{\"method\":\"getrawtransaction\",\"params\":[\"${txid}\",true]}"
|
||||
trace "[get_rawtransaction] data=${data}"
|
||||
send_to_watcher_node "${data}"
|
||||
return $?
|
||||
else
|
||||
trace "[get_rawtransaction] rawtx found in DB, no need to fetch the Bitcoin node"
|
||||
echo ${rawtx}
|
||||
return 0
|
||||
fi
|
||||
}
|
||||
|
||||
get_transaction()
|
||||
{
|
||||
trace "Entering get_transaction()..."
|
||||
|
||||
local txid=${1}
|
||||
trace "[get_transaction] txid=${txid}"
|
||||
local data="{\"method\":\"gettransaction\",\"params\":[\"${txid}\",true]}"
|
||||
trace "[get_transaction] data=${data}"
|
||||
send_to_watcher_node "${data}"
|
||||
return $?
|
||||
}
|
||||
102
proxy_docker/app/script/call_lightningd.sh
Normal file
102
proxy_docker/app/script/call_lightningd.sh
Normal file
@@ -0,0 +1,102 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
ln_create_invoice()
|
||||
{
|
||||
trace "Entering ln_create_invoice()..."
|
||||
|
||||
local result
|
||||
|
||||
local request=${1}
|
||||
local msatoshi=$(echo "${request}" | jq ".msatoshi" | tr -d '"')
|
||||
trace "[ln_create_invoice] msatoshi=${msatoshi}"
|
||||
local label=$(echo "${request}" | jq ".label")
|
||||
trace "[ln_create_invoice] label=${label}"
|
||||
local description=$(echo "${request}" | jq ".description")
|
||||
trace "[ln_create_invoice] description=${description}"
|
||||
local expiry=$(echo "${request}" | jq ".expiry" | tr -d '"')
|
||||
trace "[ln_create_invoice] expiry=${expiry}"
|
||||
|
||||
result=$(./lightning-cli invoice ${msatoshi} "${label}" "${description}" ${expiry})
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[ln_create_invoice] result=${result}"
|
||||
|
||||
echo "${result}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
ln_getinfo()
|
||||
{
|
||||
trace "Entering ln_get_info()..."
|
||||
|
||||
local result
|
||||
|
||||
result=$(./lightning-cli getinfo)
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[ln_getinfo] result=${result}"
|
||||
|
||||
echo "${result}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
ln_pay() {
|
||||
trace "Entering ln_pay()..."
|
||||
|
||||
local result
|
||||
|
||||
local request=${1}
|
||||
local bolt11=$(echo "${request}" | jq ".bolt11" | tr -d '"')
|
||||
trace "[ln_pay] bolt11=${bolt11}"
|
||||
local expected_msatoshi=$(echo "${request}" | jq ".expected_msatoshi")
|
||||
trace "[ln_pay] expected_msatoshi=${expected_msatoshi}"
|
||||
local expected_description=$(echo "${request}" | jq ".expected_description")
|
||||
trace "[ln_pay] expected_description=${expected_description}"
|
||||
|
||||
result=$(./lightning-cli decodepay ${bolt11})
|
||||
|
||||
local invoice_msatoshi=$(echo "${result}" | jq ".msatoshi")
|
||||
trace "[ln_pay] invoice_msatoshi=${invoice_msatoshi}"
|
||||
local invoice_description=$(echo "${result}" | jq ".description")
|
||||
trace "[ln_pay] invoice_description=${invoice_description}"
|
||||
|
||||
if [ "${expected_msatoshi}" != "${invoice_msatoshi}" ]; then
|
||||
result="{\"result\":\"error\",\"expected_msatoshi\":${expected_msatoshi},\"invoice_msatoshi\":${invoice_msatoshi}}"
|
||||
returncode=1
|
||||
elif [ "${expected_description}" != "${invoice_description}" ]; then
|
||||
result="{\"result\":\"error\",\"expected_description\":${expected_description},\"invoice_description\":${invoice_description}}"
|
||||
returncode=1
|
||||
else
|
||||
result=$(./lightning-cli pay ${bolt11})
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
fi
|
||||
trace "[ln_pay] result=${result}"
|
||||
|
||||
echo "${result}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
ln_newaddr()
|
||||
{
|
||||
trace "Entering ln_newaddr()..."
|
||||
|
||||
local result
|
||||
|
||||
call_lightningd newaddr
|
||||
result=$(./lightning-cli newaddr)
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[ln_newaddr] result=${result}"
|
||||
|
||||
echo "${result}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *call_lightningd.sh) call_lightningd $@;; esac
|
||||
148
proxy_docker/app/script/callbacks_job.sh
Normal file
148
proxy_docker/app/script/callbacks_job.sh
Normal file
@@ -0,0 +1,148 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sql.sh
|
||||
|
||||
do_callbacks()
|
||||
{
|
||||
(
|
||||
flock -x 200 || return 0
|
||||
|
||||
trace "Entering do_callbacks()..."
|
||||
|
||||
# Let's fetch all the watching addresses still being watched but not called back
|
||||
local callbacks=$(sql 'SELECT DISTINCT callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx ON tx.id = tx_id WHERE NOT calledback0conf and watching_id NOT NULL and callback0conf NOT NULL and watching')
|
||||
trace "[do_callbacks] callbacks0conf=${callbacks}"
|
||||
|
||||
local returncode
|
||||
local address
|
||||
local IFS=$'\n'
|
||||
for row in ${callbacks}
|
||||
do
|
||||
build_callback ${row}
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
address=$(echo "${row}" | cut -d '|' -f2)
|
||||
sql "UPDATE watching SET calledback0conf=1 WHERE address=\"${address}\""
|
||||
trace_rc $?
|
||||
fi
|
||||
done
|
||||
|
||||
callbacks=$(sql 'SELECT DISTINCT callback1conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id, is_replaceable FROM watching w, watching_tx wt, tx t WHERE w.id = watching_id AND tx_id = t.id AND NOT calledback1conf and confirmations>0 and callback1conf NOT NULL and watching')
|
||||
trace "[do_callbacks] callbacks1conf=${callbacks}"
|
||||
|
||||
for row in ${callbacks}
|
||||
do
|
||||
build_callback ${row}
|
||||
returncode=$?
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
address=$(echo "${row}" | cut -d '|' -f2)
|
||||
sql "UPDATE watching SET calledback1conf=1, watching=0 WHERE address=\"${address}\""
|
||||
trace_rc $?
|
||||
fi
|
||||
done
|
||||
) 200>./.callbacks.lock
|
||||
}
|
||||
|
||||
build_callback()
|
||||
{
|
||||
trace "Entering build_callback()..."
|
||||
|
||||
local row=$@
|
||||
local id
|
||||
local url
|
||||
local data
|
||||
local address
|
||||
local txid
|
||||
local vout_n
|
||||
local sent_amount
|
||||
local confirmations
|
||||
local ts_firstseen
|
||||
local fee
|
||||
local size
|
||||
local vsize
|
||||
local blockhash
|
||||
local blocktime
|
||||
local blockheight
|
||||
|
||||
# callback0conf, address, txid, vout, amount, confirmations, timereceived, fee, size, vsize, blockhash, blockheight, blocktime, w.id
|
||||
|
||||
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)
|
||||
trace "[build_callback] txid=${txid}"
|
||||
vout_n=$(echo "${row}" | cut -d '|' -f4)
|
||||
trace "[build_callback] vout_n=${vout_n}"
|
||||
sent_amount=$(echo "${row}" | cut -d '|' -f5)
|
||||
trace "[build_callback] sent_amount=${sent_amount}"
|
||||
confirmations=$(echo "${row}" | cut -d '|' -f6)
|
||||
trace "[build_callback] confirmations=${confirmations}"
|
||||
ts_firstseen=$(echo "${row}" | cut -d '|' -f7)
|
||||
trace "[build_callback] ts_firstseen=${ts_firstseen}"
|
||||
|
||||
# If node in pruned mode, we can't calculate the fees and then we don't want
|
||||
# to send 0.00000000 as fees but empty string to distinguish.
|
||||
fee=$(echo "${row}" | cut -d '|' -f8)
|
||||
if [ -n "${fee}" ]; then
|
||||
fee=$(echo "${fee}" | awk '{ printf "%.8f", $0 }')
|
||||
fi
|
||||
trace "[build_callback] fee=${fee}"
|
||||
size=$(echo "${row}" | cut -d '|' -f9)
|
||||
trace "[build_callback] size=${size}"
|
||||
vsize=$(echo "${row}" | cut -d '|' -f10)
|
||||
trace "[build_callback] vsize=${vsize}"
|
||||
is_replaceable=$(echo "${row}" | cut -d '|' -f15)
|
||||
trace "[build_callback] is_replaceable=${is_replaceable}"
|
||||
blockhash=$(echo "${row}" | cut -d '|' -f11)
|
||||
trace "[build_callback] blockhash=${blockhash}"
|
||||
blockheight=$(echo "${row}" | cut -d '|' -f12)
|
||||
trace "[build_callback] blockheight=${blockheight}"
|
||||
blocktime=$(echo "${row}" | cut -d '|' -f13)
|
||||
trace "[build_callback] blocktime=${blocktime}"
|
||||
|
||||
data="{\"id\":\"${id}\","
|
||||
data="${data}\"address\":\"${address}\","
|
||||
data="${data}\"hash\":\"${txid}\","
|
||||
data="${data}\"vout_n\":${vout_n},"
|
||||
data="${data}\"sent_amount\":${sent_amount},"
|
||||
data="${data}\"confirmations\":${confirmations},"
|
||||
data="${data}\"received\":\"$(date -Is -d @${ts_firstseen})\","
|
||||
data="${data}\"size\":${size},"
|
||||
data="${data}\"vsize\":${vsize},"
|
||||
data="${data}\"fees\":${fee},"
|
||||
data="${data}\"is_replaceable\":${is_replaceable},"
|
||||
data="${data}\"blockhash\":\"${blockhash}\","
|
||||
if [ -z ${blocktime} ]; then
|
||||
data="${data}\"blocktime\":\"\","
|
||||
else
|
||||
data="${data}\"blocktime\":\"$(date -Is -d @${blocktime})\","
|
||||
fi
|
||||
data="${data}\"blockheight\":\"${blockheight}\"}"
|
||||
trace "[build_callback] data=${data}"
|
||||
|
||||
curl_callback "${url}" "${data}"
|
||||
return $?
|
||||
}
|
||||
|
||||
curl_callback()
|
||||
{
|
||||
trace "Entering curl_callback()..."
|
||||
|
||||
local url=${1}
|
||||
local data=${2}
|
||||
|
||||
trace "[curl_callback] curl -H \"Content-Type: application/json\" -d \"${data}\" ${url}"
|
||||
curl -H "Content-Type: application/json" -H "X-Forwarded-Proto: https" -d "${data}" ${url}
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *callbacks_job.sh) do_callbacks $@;; esac
|
||||
112
proxy_docker/app/script/computefees.sh
Normal file
112
proxy_docker/app/script/computefees.sh
Normal file
@@ -0,0 +1,112 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
. ./sql.sh
|
||||
. ./blockchainrpc.sh
|
||||
|
||||
compute_fees()
|
||||
{
|
||||
local pruned=$(get_prop "watchingnode.pruned")
|
||||
if [ "${pruned}" = "true" ]; then
|
||||
trace "[compute_fees] pruned=${pruned}"
|
||||
# We want null instead of 0.00000000 in this case.
|
||||
echo "null"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
local txid=${1}
|
||||
|
||||
local tx_raw_details=$(cat rawtx-${txid}.blob)
|
||||
trace "[compute_fees] tx_raw_details=${tx_raw_details}"
|
||||
local vin_total_amount=$(compute_vin_total_amount "${tx_raw_details}")
|
||||
|
||||
local vout_total_amount=0
|
||||
local vout_value
|
||||
local vout_values=$(echo "${tx_raw_details}" | jq ".result.vout[].value")
|
||||
for vout_value in ${vout_values}
|
||||
do
|
||||
vout_total_amount=$(awk "BEGIN { printf(\"%.8f\", ${vout_total_amount}+${vout_value}); exit }")
|
||||
done
|
||||
|
||||
trace "[compute_fees] vin total amount=${vin_total_amount}"
|
||||
trace "[compute_fees] vout total amount=${vout_total_amount}"
|
||||
|
||||
local fees=$(awk "BEGIN { printf(\"%.8f\", ${vin_total_amount}-${vout_total_amount}); exit }")
|
||||
trace "[compute_fees] fees=${fees}"
|
||||
|
||||
echo ${fees}
|
||||
}
|
||||
|
||||
compute_vin_total_amount()
|
||||
{
|
||||
trace "Entering compute_vin_total_amount()..."
|
||||
|
||||
local main_tx=${1}
|
||||
# local vin_txids=$(echo ${main_tx} | jq '.result.vin[].txid')
|
||||
local vin_txids_vout=$(echo ${main_tx} | jq '.result.vin[] | ((.txid + "-") + (.vout | tostring))')
|
||||
trace "[compute_vin_total_amount] vin_txids_vout=${vin_txids_vout}"
|
||||
local returncode
|
||||
local vin_txid_vout
|
||||
local vin_txid
|
||||
local vin_raw_tx
|
||||
local vin_vout_amount=0
|
||||
local vout
|
||||
local vin_total_amount=0
|
||||
local vin_hash
|
||||
local vin_confirmations
|
||||
local vin_timereceived
|
||||
local vin_vsize
|
||||
local vin_blockhash
|
||||
local vin_blockheight
|
||||
local vin_blocktime
|
||||
local txid_already_inserted=true
|
||||
|
||||
for vin_txid_vout in ${vin_txids_vout}
|
||||
do
|
||||
# vin_txid=$(echo ${vin_txid} | tr -d '"')
|
||||
vin_txid=$(echo ${vin_txid_vout} | tr -d '"' | cut -d '-' -f1)
|
||||
# Check if we already have the tx in our DB
|
||||
vin_raw_tx=$(sql "SELECT raw_tx FROM tx WHERE txid=\"${vin_txid}\"")
|
||||
if [ -z "${vin_raw_tx}" ]; then
|
||||
txid_already_inserted=false
|
||||
vin_raw_tx=$(get_rawtransaction "${vin_txid}")
|
||||
returncode=$?
|
||||
if [ "${returncode}" -ne 0 ]; then
|
||||
return ${returncode}
|
||||
fi
|
||||
fi
|
||||
# vout=$(echo ${main_tx} | jq ".result.vin[] | select(.txid == \"${vin_txid}\") | .vout")
|
||||
vout=$(echo ${vin_txid_vout} | tr -d '"' | cut -d '-' -f2)
|
||||
trace "[compute_vin_total_amount] vout=${vout}"
|
||||
vin_vout_amount=$(echo ${vin_raw_tx} | jq ".result.vout[] | select(.n == ${vout}) | .value" | awk '{ printf "%.8f", $0 }')
|
||||
trace "[compute_vin_total_amount] vin_vout_amount=${vin_vout_amount}"
|
||||
vin_total_amount=$(awk "BEGIN { printf(\"%.8f\", ${vin_total_amount}+${vin_vout_amount}); exit}")
|
||||
trace "[compute_vin_total_amount] vin_total_amount=${vin_total_amount}"
|
||||
vin_hash=$(echo ${vin_raw_tx} | jq ".result.hash")
|
||||
vin_confirmations=$(echo ${vin_raw_tx} | jq ".result.confirmations")
|
||||
vin_timereceived=$(echo ${vin_raw_tx} | jq ".result.time")
|
||||
vin_size=$(echo ${vin_raw_tx} | jq ".result.size")
|
||||
vin_vsize=$(echo ${vin_raw_tx} | jq ".result.vsize")
|
||||
vin_blockhash=$(echo ${vin_raw_tx} | jq ".result.blockhash")
|
||||
vin_blockheight=$(echo ${vin_raw_tx} | jq ".result.blockheight")
|
||||
vin_blocktime=$(echo ${vin_raw_tx} | jq ".result.blocktime")
|
||||
|
||||
# Let's insert the vin tx in the DB just in case it would be useful
|
||||
if ! ${txid_already_inserted}; then
|
||||
# Sometimes raw tx are too long to be passed as paramater, so let's write
|
||||
# it to a temp file for it to be read by sqlite3 and then delete the file
|
||||
echo "${vin_raw_tx}" > rawtx-${vin_txid}.blob
|
||||
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, size, vsize, blockhash, blockheight, blocktime, raw_tx) VALUES (\"${vin_txid}\", ${vin_hash}, ${vin_confirmations}, ${vin_timereceived}, ${vin_size}, ${vin_vsize}, ${vin_blockhash}, ${vin_blockheight}, ${vin_blocktime}, readfile('rawtx-${vin_txid}.blob'))"
|
||||
trace_rc $?
|
||||
rm rawtx-${vin_txid}.blob
|
||||
txid_already_inserted=true
|
||||
fi
|
||||
done
|
||||
|
||||
echo ${vin_total_amount}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
case "${0}" in *computefees.sh) compute_vin_total_amount $@;; esac
|
||||
150
proxy_docker/app/script/confirmation.sh
Normal file
150
proxy_docker/app/script/confirmation.sh
Normal file
@@ -0,0 +1,150 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sql.sh
|
||||
. ./callbacks_job.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
. ./responsetoclient.sh
|
||||
. ./computefees.sh
|
||||
. ./blockchainrpc.sh
|
||||
|
||||
confirmation_request()
|
||||
{
|
||||
# We are receiving a HTTP request, let's find the TXID from it
|
||||
|
||||
trace "Entering confirmation_request()..."
|
||||
|
||||
local request=${1}
|
||||
local txid=$(echo "${request}" | cut -d ' ' -f2 | cut -d '/' -f3)
|
||||
|
||||
confirmation "${txid}"
|
||||
return $?
|
||||
}
|
||||
|
||||
confirmation()
|
||||
{
|
||||
trace "Entering confirmation()..."
|
||||
|
||||
local txid=${1}
|
||||
local tx_details=$(get_transaction ${txid})
|
||||
|
||||
########################################################################################################
|
||||
# First of all, let's make sure we're working on watched addresses...
|
||||
local address
|
||||
local addresseswhere
|
||||
local addresses=$(echo ${tx_details} | jq ".result.details[].address")
|
||||
|
||||
local notfirst=false
|
||||
local IFS=$'\n'
|
||||
for address in ${addresses}
|
||||
do
|
||||
trace "[confirmation] address=${address}"
|
||||
|
||||
if ${notfirst}; then
|
||||
addresseswhere="${addresseswhere},${address}"
|
||||
else
|
||||
addresseswhere="${address}"
|
||||
notfirst=true
|
||||
fi
|
||||
done
|
||||
local rows=$(sql "SELECT id, address FROM watching WHERE address IN (${addresseswhere}) AND watching")
|
||||
if [ ${#rows} -eq 0 ]; then
|
||||
trace "[confirmation] No watched address in this tx!"
|
||||
return 0
|
||||
fi
|
||||
########################################################################################################
|
||||
|
||||
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')
|
||||
|
||||
# Sometimes raw tx are too long to be passed as paramater, so let's write
|
||||
# it to a temp file for it to be read by sqlite3 and then delete the file
|
||||
echo "${tx_raw_details}" > rawtx-${txid}.blob
|
||||
|
||||
if [ -z ${tx} ]; then
|
||||
# TX not found in our DB.
|
||||
# 0-conf or missed conf (managed or while spending) or spending an unconfirmed
|
||||
# (note: spending an unconfirmed TX must be avoided or we'll get here spending an unprocessed watching)
|
||||
|
||||
# Let's first insert the tx in our DB
|
||||
|
||||
local tx_hash=$(echo ${tx_raw_details} | jq '.result.hash')
|
||||
local tx_ts_firstseen=$(echo ${tx_details} | jq '.result.timereceived')
|
||||
local tx_amount=$(echo ${tx_details} | jq '.result.amount')
|
||||
|
||||
local tx_size=$(echo ${tx_raw_details} | jq '.result.size')
|
||||
local tx_vsize=$(echo ${tx_raw_details} | jq '.result.vsize')
|
||||
local tx_replaceable=$(echo ${tx_details} | jq '.result."bip125-replaceable"')
|
||||
tx_replaceable=$([ ${tx_replaceable} = "yes" ] && echo 1 || echo 0)
|
||||
|
||||
local fees=$(compute_fees "${txid}")
|
||||
trace "[confirmation] fees=${fees}"
|
||||
|
||||
# If we missed 0-conf...
|
||||
local tx_blockhash=$(echo ${tx_details} | jq '.result.blockhash')
|
||||
local tx_blockheight=$(echo ${tx_details} | jq '.result.blockheight')
|
||||
local tx_blocktime=$(echo ${tx_details} | jq '.result.blocktime')
|
||||
|
||||
sql "INSERT OR IGNORE INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, blockhash, blockheight, blocktime, raw_tx) VALUES (\"${txid}\", ${tx_hash}, ${tx_nb_conf}, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${tx_blockhash}, ${tx_blockheight}, ${tx_blocktime}, readfile('rawtx-${txid}.blob'))"
|
||||
trace_rc $?
|
||||
|
||||
id_inserted=$(sql "SELECT id FROM tx WHERE txid='${txid}'")
|
||||
trace_rc $?
|
||||
|
||||
else
|
||||
# TX found in our DB.
|
||||
# 1-conf or spending watched address (in this case, we probably missed conf)
|
||||
|
||||
local tx_blockhash=$(echo ${tx_details} | jq '.result.blockhash')
|
||||
local tx_blockheight=$(echo ${tx_details} | jq '.result.blockheight')
|
||||
local tx_blocktime=$(echo ${tx_details} | jq '.result.blocktime')
|
||||
|
||||
sql "UPDATE tx SET
|
||||
confirmations=${tx_nb_conf},
|
||||
blockhash=${tx_blockhash},
|
||||
blockheight=${tx_blockheight},
|
||||
blocktime=${tx_blocktime},
|
||||
raw_tx=readfile('rawtx-${txid}.blob')
|
||||
WHERE txid=\"${txid}\""
|
||||
trace_rc $?
|
||||
|
||||
id_inserted=${tx}
|
||||
|
||||
fi
|
||||
# Delete the temp file containing the raw tx (see above)
|
||||
rm rawtx-${txid}.blob
|
||||
|
||||
########################################################################################################
|
||||
# Let's now insert in the join table if not already done
|
||||
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"
|
||||
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)
|
||||
tx_vout_n=$(echo ${tx_details} | jq ".result.details[] | select(.address==\"${address}\") | .vout")
|
||||
tx_vout_amount=$(echo ${tx_details} | jq ".result.details[] | select(.address==\"${address}\") | .amount")
|
||||
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
|
||||
########################################################################################################
|
||||
|
||||
do_callbacks
|
||||
|
||||
echo '{"result":"confirmed"}'
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
case "${0}" in *confirmation.sh) confirmation $@;; esac
|
||||
64
proxy_docker/app/script/getactivewatches.sh
Normal file
64
proxy_docker/app/script/getactivewatches.sh
Normal file
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sql.sh
|
||||
|
||||
getactivewatches()
|
||||
{
|
||||
trace "Entering getactivewatches()..."
|
||||
|
||||
local watches
|
||||
watches=$(sql "SELECT id, address, imported, callback0conf, callback1conf, inserted_ts FROM watching WHERE watching AND NOT calledback1conf")
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
|
||||
local id
|
||||
local address
|
||||
local imported
|
||||
local inserted
|
||||
local cb0conf_url
|
||||
local cb1conf_url
|
||||
local timestamp
|
||||
local notfirst=false
|
||||
|
||||
echo -n "{\"watches\":["
|
||||
|
||||
local IFS=$'\n'
|
||||
for row in ${watches}
|
||||
do
|
||||
if ${notfirst}; then
|
||||
echo ","
|
||||
else
|
||||
notfirst=true
|
||||
fi
|
||||
trace "[getactivewatches] row=${row}"
|
||||
id=$(echo "${row}" | cut -d '|' -f1)
|
||||
trace "[getactivewatches] id=${id}"
|
||||
address=$(echo "${row}" | cut -d '|' -f2)
|
||||
trace "[getactivewatches] address=${address}"
|
||||
imported=$(echo "${row}" | cut -d '|' -f3)
|
||||
trace "[getactivewatches] imported=${imported}"
|
||||
cb0conf_url=$(echo "${row}" | cut -d '|' -f4)
|
||||
trace "[getactivewatches] cb0conf_url=${cb0conf_url}"
|
||||
cb1conf_url=$(echo "${row}" | cut -d '|' -f5)
|
||||
trace "[getactivewatches] cb1conf_url=${cb1conf_url}"
|
||||
timestamp=$(echo "${row}" | cut -d '|' -f6)
|
||||
trace "[getactivewatches] timestamp=${timestamp}"
|
||||
|
||||
data="{\"id\":\"${id}\","
|
||||
data="${data}\"address\":\"${address}\","
|
||||
data="${data}\"imported\":\"${imported}\","
|
||||
data="${data}\"unconfirmedCallbackURL\":\"${cb0conf_url}\","
|
||||
data="${data}\"confirmedCallbackURL\":\"${cb1conf_url}\","
|
||||
data="${data}\"watching_since\":\"${timestamp}\"}"
|
||||
trace "[getactivewatches] data=${data}"
|
||||
|
||||
echo -n "${data}"
|
||||
done
|
||||
|
||||
echo "]}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *getactivewatches.sh) getactivewatches;; esac
|
||||
21
proxy_docker/app/script/importaddress.sh
Normal file
21
proxy_docker/app/script/importaddress.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
|
||||
importaddress_rpc()
|
||||
{
|
||||
trace "[Entering importaddress_rpc()]"
|
||||
|
||||
local address=${1}
|
||||
local data="{\"method\":\"importaddress\",\"params\":[\"${address}\",\"\",false]}"
|
||||
local result
|
||||
result=$(send_to_watcher_node ${data})
|
||||
local returncode=$?
|
||||
|
||||
echo "${result}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *importaddress.sh) importaddress_rpc $@;; esac
|
||||
86
proxy_docker/app/script/manage_missed_conf.sh
Normal file
86
proxy_docker/app/script/manage_missed_conf.sh
Normal file
@@ -0,0 +1,86 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sql.sh
|
||||
. ./importaddress.sh
|
||||
. ./confirmation.sh
|
||||
|
||||
manage_not_imported()
|
||||
{
|
||||
# When we tried to import watched addresses in the watching node,
|
||||
# if it didn't succeed, we try again here.
|
||||
|
||||
trace "[Entering manage_not_imported()]"
|
||||
|
||||
local watches=$(sql 'SELECT address FROM watching WHERE watching AND NOT imported')
|
||||
trace "[manage_not_imported] watches=${watches}"
|
||||
|
||||
local result
|
||||
local returncode
|
||||
local IFS=$'\n'
|
||||
for address in ${watches}
|
||||
do
|
||||
result=$(importaddress_rpc "${address}")
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
sql "UPDATE watching SET imported=1 WHERE address=\"${address}\""
|
||||
fi
|
||||
done
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
manage_missed_conf()
|
||||
{
|
||||
# Maybe we missed confirmations, because we were down or no network or
|
||||
# whatever, so we look at what might be missed and do confirmations.
|
||||
|
||||
trace "[Entering manage_missed_conf()]"
|
||||
|
||||
# local watches=$(sql 'SELECT address FROM watching WHERE txid IS NULL AND watching AND imported')
|
||||
#local watches=$(sql 'SELECT address FROM watching LEFT JOIN watching_tx ON id = watching_id WHERE watching AND imported AND tx_id IS NULL')
|
||||
local watches=$(sql 'SELECT address FROM watching w LEFT JOIN watching_tx ON w.id = watching_id LEFT JOIN tx t ON t.id = tx_id WHERE watching AND imported AND (tx_id IS NULL OR t.confirmations=0)')
|
||||
trace "[manage_missed_conf] watches=${watches}"
|
||||
if [ ${#watches} -eq 0 ]; then
|
||||
trace "[manage_missed_conf] Nothing missed!"
|
||||
return 0
|
||||
fi
|
||||
|
||||
local addresses
|
||||
local data
|
||||
local result
|
||||
local returncode
|
||||
local IFS=$'\n'
|
||||
for address in ${watches}
|
||||
do
|
||||
if [ -z ${addresses} ]; then
|
||||
addresses="[\"${address}\""
|
||||
else
|
||||
addresses="${addresses},\"${address}\""
|
||||
fi
|
||||
done
|
||||
addresses="${addresses}]"
|
||||
|
||||
# Watching addresses with UTXO are transactions being watched that went through without us knowing it, we missed the conf
|
||||
data="{\"method\":\"listunspent\",\"params\":[0, 9999999, ${addresses}]}"
|
||||
local unspents
|
||||
unspents=$(send_to_watcher_node ${data})
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -ne 0 ]; then
|
||||
return ${returncode}
|
||||
fi
|
||||
|
||||
# | tr -d '"'
|
||||
local txids=$(echo ${unspents} | jq ".result[].txid" | tr -d '"')
|
||||
for txid in ${txids}
|
||||
do
|
||||
confirmation "${txid}"
|
||||
done
|
||||
|
||||
return 0
|
||||
|
||||
}
|
||||
|
||||
case "${0}" in *manage_missed_conf.sh) manage_not_imported $@; manage_missed_conf $@;; esac
|
||||
238
proxy_docker/app/script/requesthandler.sh
Normal file
238
proxy_docker/app/script/requesthandler.sh
Normal file
@@ -0,0 +1,238 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
#
|
||||
#
|
||||
#
|
||||
|
||||
. ./sendtobitcoinnode.sh
|
||||
. ./callbacks_job.sh
|
||||
. ./watchrequest.sh
|
||||
. ./unwatchrequest.sh
|
||||
. ./getactivewatches.sh
|
||||
. ./confirmation.sh
|
||||
. ./blockchainrpc.sh
|
||||
. ./responsetoclient.sh
|
||||
. ./trace.sh
|
||||
. ./manage_missed_conf.sh
|
||||
. ./walletoperations.sh
|
||||
. ./bitcoin.sh
|
||||
. ./call_lightningd.sh
|
||||
|
||||
main()
|
||||
{
|
||||
trace "Entering main()..."
|
||||
|
||||
local step=0
|
||||
local cmd
|
||||
local http_method
|
||||
local line
|
||||
local content_length
|
||||
local response
|
||||
local returncode
|
||||
|
||||
while read line; do
|
||||
line=$(echo "${line}" | tr -d '\r\n')
|
||||
trace "[main] line=${line}"
|
||||
|
||||
if [ "${cmd}" = "" ]; then
|
||||
# First line!
|
||||
# Looking for something like:
|
||||
# GET /cmd/params HTTP/1.1
|
||||
# POST / HTTP/1.1
|
||||
cmd=$(echo "${line}" | cut -d '/' -f2 | cut -d ' ' -f1)
|
||||
trace "[main] cmd=${cmd}"
|
||||
http_method=$(echo "${line}" | cut -d ' ' -f1)
|
||||
trace "[main] http_method=${http_method}"
|
||||
if [ "${http_method}" = "GET" ]; then
|
||||
step=1
|
||||
fi
|
||||
fi
|
||||
if [ "${line}" = "" ]; then
|
||||
trace "[main] empty line"
|
||||
if [ ${step} -eq 1 ]; then
|
||||
trace "[main] body part finished, disconnecting"
|
||||
break
|
||||
else
|
||||
trace "[main] headers part finished, body incoming"
|
||||
step=1
|
||||
fi
|
||||
fi
|
||||
# line=content-length: 406
|
||||
case "${line}" in *[cC][oO][nN][tT][eE][nN][tT]-[lL][eE][nN][gG][tT][hH]*)
|
||||
content_length=$(echo ${line} | cut -d ':' -f2)
|
||||
trace "[main] content_length=${content_length}";
|
||||
;;
|
||||
esac
|
||||
if [ ${step} -eq 1 ]; then
|
||||
trace "[main] step=${step}"
|
||||
if [ "${http_method}" = "POST" ]; then
|
||||
read -n ${content_length} line
|
||||
trace "[main] line=${line}"
|
||||
fi
|
||||
case "${cmd}" in
|
||||
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"}
|
||||
|
||||
response=$(watchrequest "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
unwatch)
|
||||
# curl (GET) 192.168.111.152:8080/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
|
||||
|
||||
response=$(unwatchrequest "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getactivewatches)
|
||||
# curl (GET) 192.168.111.152:8080/getactivewatches
|
||||
|
||||
response=$(getactivewatches)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
conf)
|
||||
# curl (GET) 192.168.111.152:8080/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387
|
||||
|
||||
response=$(confirmation_request "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getbestblockhash)
|
||||
# curl (GET) http://192.168.111.152:8080/getbestblockhash
|
||||
|
||||
response=$(get_best_block_hash)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getblockinfo)
|
||||
# curl (GET) http://192.168.111.152:8080/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea
|
||||
|
||||
response=$(get_block_info $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3))
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
gettransaction)
|
||||
# curl (GET) http://192.168.111.152:8080/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
|
||||
|
||||
response=$(get_rawtransaction $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3))
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getbestblockinfo)
|
||||
# curl (GET) http://192.168.111.152:8080/getbestblockinfo
|
||||
|
||||
response=$(get_best_block_info)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
executecallbacks)
|
||||
# curl (GET) http://192.168.111.152:8080/executecallbacks
|
||||
|
||||
manage_not_imported
|
||||
manage_missed_conf
|
||||
response=$(do_callbacks)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getbalance)
|
||||
# curl (GET) http://192.168.111.152:8080/getbalance
|
||||
|
||||
response=$(getbalance)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
getnewaddress)
|
||||
# curl (GET) http://192.168.111.152:8080/getnewaddress
|
||||
|
||||
response=$(getnewaddress)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
spend)
|
||||
# POST http://192.168.111.152:8080/spend
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
|
||||
response=$(spend "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
addtobatch)
|
||||
# POST http://192.168.111.152:8080/addtobatch
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
|
||||
response=$(addtobatching $(echo "${line}" | jq ".address" | tr -d '"') $(echo "${line}" | jq ".amount"))
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
batchspend)
|
||||
# GET http://192.168.111.152:8080/batchspend
|
||||
|
||||
response=$(batchspend "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
deriveindex)
|
||||
# curl GET http://192.168.111.152:8080/deriveindex/25-30
|
||||
# curl GET http://192.168.111.152:8080/deriveindex/34
|
||||
|
||||
response=$(deriveindex $(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3))
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
derivepubpath)
|
||||
# POST http://192.168.111.152:8080/derivepubpath
|
||||
# BODY {"pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/25-30"}
|
||||
# BODY {"pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/25-30"}
|
||||
# BODY {"pub32":"vpub5SLqN2bLY4WeZF3kL4VqiWF1itbf3A6oRrq9aPf16AZMVWYCuN9TxpAZwCzVgW94TNzZPNc9XAHD4As6pdnExBtCDGYRmNJrcJ4eV9hNqcv","path":"0/25-30"}
|
||||
|
||||
response=$(send_to_pycoin "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
ln_getinfo)
|
||||
# GET http://192.168.111.152:8080/ln_getinfo
|
||||
|
||||
response=$(ln_getinfo)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
ln_create_invoice)
|
||||
# POST http://192.168.111.152:8080/ln_create_invoice
|
||||
# BODY {"msatoshi":"10000","label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":"900"}
|
||||
|
||||
response=$(ln_create_invoice "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
ln_pay)
|
||||
# POST http://192.168.111.152:8080/ln_pay
|
||||
# BODY {"bolt11":"lntb1pdca82tpp5gv8mn5jqlj6xztpnt4r472zcyrwf3y2c3cvm4uzg2gqcnj90f83qdp2gf5hgcm0d9hzqnm4w3kx2apqdaexgetjyq3nwvpcxgcqp2g3d86wwdfvyxcz7kce7d3n26d2rw3wf5tzpm2m5fl2z3mm8msa3xk8nv2y32gmzlhwjved980mcmkgq83u9wafq9n4w28amnmwzujgqpmapcr3","expected_msatoshi":"10000","expected_description":"Bitcoin Outlet order #7082"}
|
||||
|
||||
response=$(ln_pay "${line}")
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
ln_newaddr)
|
||||
# GET http://192.168.111.152:8080/ln_newaddr
|
||||
|
||||
response=$(ln_newaddr)
|
||||
response_to_client "${response}" ${?}
|
||||
break
|
||||
;;
|
||||
esac
|
||||
break
|
||||
fi
|
||||
done
|
||||
trace "[main] exiting"
|
||||
return 0
|
||||
}
|
||||
|
||||
export NODE_RPC_URL=$BTC_NODE_RPC_URL
|
||||
export TRACING
|
||||
export DB_PATH
|
||||
export DB_FILE
|
||||
|
||||
main
|
||||
exit $?
|
||||
21
proxy_docker/app/script/responsetoclient.sh
Normal file
21
proxy_docker/app/script/responsetoclient.sh
Normal file
@@ -0,0 +1,21 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
response_to_client()
|
||||
{
|
||||
trace "Entering response_to_client()..."
|
||||
|
||||
local response=${1}
|
||||
local returncode=${2}
|
||||
|
||||
([ -z "${returncode}" ] || [ "${returncode}" -eq "0" ]) && echo -ne "HTTP/1.1 200 OK\r\n"
|
||||
[ -n "${returncode}" ] && [ "${returncode}" -ne "0" ] && echo -ne "HTTP/1.1 400 Bad Request\r\n"
|
||||
|
||||
echo -e "Content-Type: application/json\r\nContent-Length: ${#response}\r\n\r\n${response}"
|
||||
|
||||
# Small delay needed for the data to be processed correctly by peer
|
||||
sleep 0.2s
|
||||
}
|
||||
|
||||
case "${0}" in *responsetoclient.sh) response_to_client $@;; esac
|
||||
59
proxy_docker/app/script/sendtobitcoinnode.sh
Normal file
59
proxy_docker/app/script/sendtobitcoinnode.sh
Normal file
@@ -0,0 +1,59 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
send_to_watcher_node()
|
||||
{
|
||||
trace "Entering send_to_watcher_node()..."
|
||||
send_to_bitcoin_node ${WATCHER_NODE_RPC_URL} watcher_btcnode_curlcfg.properties $@
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
send_to_spender_node()
|
||||
{
|
||||
trace "Entering send_to_spender_node()..."
|
||||
send_to_bitcoin_node ${SPENDER_NODE_RPC_URL} spender_btcnode_curlcfg.properties $@
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
send_to_bitcoin_node()
|
||||
{
|
||||
trace "Entering send_to_bitcoin_node()..."
|
||||
local returncode
|
||||
local result
|
||||
local errorstring
|
||||
local node_url=${1}
|
||||
local configfile=${2}
|
||||
local data=${3}
|
||||
|
||||
trace "[send_to_bitcoin_node] curl -s --config ${configfile} -H \"Content-Type: application/json\" -d \"${data}\" ${node_url}"
|
||||
result=$(curl -s --config ${configfile} -H "Content-Type: application/json" -d "${data}" ${node_url})
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[send_to_bitcoin_node] result=${result}"
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
# Node responded, let's see if we got an error message from the node
|
||||
# jq -e will have a return code of 1 if the supplied tag is null.
|
||||
errorstring=$(echo "${result}" | jq -e ".error")
|
||||
if [ "$?" -eq "0" ]; then
|
||||
# Error tag not null, so there's an error
|
||||
trace "[send_to_bitcoin_node] Node responded, error found in response: ${errorstring}"
|
||||
returncode=1
|
||||
else
|
||||
trace "[send_to_bitcoin_node] Node responded, no error found in response, yayy!"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Output response to stdout before exiting with return code
|
||||
echo "${result}"
|
||||
|
||||
trace_rc ${returncode}
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *sendtobitcoinnode.sh) send_to_bitcoin_node $@;; esac
|
||||
12
proxy_docker/app/script/sql.sh
Normal file
12
proxy_docker/app/script/sql.sh
Normal file
@@ -0,0 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
sql()
|
||||
{
|
||||
trace "sqlite3 ${DB_FILE} '${1}'"
|
||||
sqlite3 -cmd ".timeout 20000" ${DB_FILE} "${1}"
|
||||
return $?
|
||||
}
|
||||
|
||||
case "${0}" in *sql.sh) sql $@;; esac
|
||||
15
proxy_docker/app/script/startproxy.sh
Normal file
15
proxy_docker/app/script/startproxy.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
export PROXY_LISTENING_PORT
|
||||
export WATCHER_NODE_RPC_URL=$WATCHER_BTC_NODE_RPC_URL
|
||||
export SPENDER_NODE_RPC_URL=$SPENDER_BTC_NODE_RPC_URL
|
||||
export TRACING
|
||||
export DB_PATH
|
||||
export DB_FILE
|
||||
|
||||
if [ ! -e ${DB_FILE} ]; then
|
||||
echo "DB not found, creating..." > /dev/stderr
|
||||
cat watching.sql | sqlite3 $DB_FILE
|
||||
fi
|
||||
|
||||
nc -vlkp${PROXY_LISTENING_PORT} -e ./requesthandler.sh
|
||||
9
proxy_docker/app/script/tests-cb.sh
Normal file
9
proxy_docker/app/script/tests-cb.sh
Normal file
@@ -0,0 +1,9 @@
|
||||
#!/bin/sh
|
||||
|
||||
read line
|
||||
echo ${line} > /dev/stderr
|
||||
echo -ne "HTTP/1.1 200 OK\r\n"
|
||||
echo -e "Content-Type: application/json\r\nContent-Length: 0\r\n\r\n"
|
||||
|
||||
# Small delay needed for the data to be processed correctly by peer
|
||||
sleep 0.5s
|
||||
322
proxy_docker/app/script/tests.sh
Normal file
322
proxy_docker/app/script/tests.sh
Normal file
@@ -0,0 +1,322 @@
|
||||
#!/bin/sh
|
||||
|
||||
tests()
|
||||
{
|
||||
local address
|
||||
local address1
|
||||
local address2
|
||||
local address3
|
||||
local response
|
||||
|
||||
# getbestblockhash
|
||||
# (GET) http://cyphernode:8888/getbestblockhash
|
||||
|
||||
echo "Testing getbestblockhash..."
|
||||
response=$(curl -s cyphernode:8888/getbestblockhash)
|
||||
echo "response=${response}"
|
||||
local blockhash=$(echo ${response} | jq ".result" | tr -d '\"')
|
||||
echo "blockhash=${blockhash}"
|
||||
if [ -z "${blockhash}" ]; then
|
||||
exit 2
|
||||
fi
|
||||
echo "Tested getbestblockhash."
|
||||
|
||||
# getbestblockinfo
|
||||
# curl (GET) http://cyphernode:8888/getbestblockinfo
|
||||
|
||||
echo "Testing getbestblockinfo..."
|
||||
response=$(curl -s cyphernode:8888/getbestblockinfo)
|
||||
echo "response=${response}"
|
||||
local blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"')
|
||||
echo "blockhash2=${blockhash2}"
|
||||
if [ "${blockhash2}" != "${blockhash}" ]; then
|
||||
exit 4
|
||||
fi
|
||||
echo "Tested getbestblockinfo."
|
||||
|
||||
# getblockinfo
|
||||
# (GET) http://cyphernode:8888/getblockinfo/000000006f82a384c208ecfa04d05beea02d420f3f398ddda5c7f900de5718ea
|
||||
|
||||
echo "Testing getblockinfo..."
|
||||
response=$(curl -s cyphernode:8888/getblockinfo/${blockhash})
|
||||
echo "response=${response}"
|
||||
blockhash2=$(echo ${response} | jq ".result.hash" | tr -d '\"')
|
||||
echo "blockhash2=${blockhash2}"
|
||||
if [ "${blockhash2}" != "${blockhash}" ]; then
|
||||
exit 6
|
||||
fi
|
||||
echo "Tested getblockinfo."
|
||||
|
||||
# gettransaction
|
||||
# (GET) http://cyphernode:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648
|
||||
|
||||
echo "Testing gettransaction..."
|
||||
response=$(curl -s cyphernode:8888/gettransaction/af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648)
|
||||
echo "response=${response}"
|
||||
local txid=$(echo ${response} | jq ".result.txid" | tr -d '\"')
|
||||
echo "txid=${txid}"
|
||||
if [ "${txid}" != "af867c86000da76df7ddb1054b273ca9e034e8c89d049b5b2795f9f590f67648" ]; then
|
||||
exit 8
|
||||
fi
|
||||
echo "Tested gettransaction."
|
||||
|
||||
# getnewaddress
|
||||
# (GET) http://cyphernode:8888/getnewaddress
|
||||
# returns {"address":"2MuiUu8AyuByAGYRDAqqhdYxt8gXcsQ1Ymw"}
|
||||
|
||||
echo "Testing getnewaddress..."
|
||||
response=$(curl -s cyphernode:8888/getnewaddress)
|
||||
echo "response=${response}"
|
||||
address1=$(echo ${response} | jq ".address" | tr -d '\"')
|
||||
echo "address1=${address1}"
|
||||
if [ -z "${address1}" ]; then
|
||||
exit 10
|
||||
fi
|
||||
address2=$(curl -s cyphernode:8888/getnewaddress | jq ".address" | tr -d '\"')
|
||||
echo "address2=${address2}"
|
||||
echo "Tested getnewaddress."
|
||||
|
||||
# getbalance
|
||||
# (GET) http://cyphernode:8888/getbalance
|
||||
|
||||
echo "Testing getbalance..."
|
||||
response=$(curl -s cyphernode:8888/getbalance)
|
||||
echo "response=${response}"
|
||||
local balance=$(echo ${response} | jq ".balance")
|
||||
echo "balance=${balance}"
|
||||
if [ -z "${balance}" ]; then
|
||||
exit 12
|
||||
fi
|
||||
echo "Tested getbalance."
|
||||
|
||||
# watch
|
||||
# POST http://cyphernode:8888/watch
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.122.233:1111/callback0conf","confirmedCallbackURL":"192.168.122.233:1111/callback1conf"}
|
||||
|
||||
echo "Testing watch..."
|
||||
local url1="$(hostname):1111/callback0conf"
|
||||
local url2="$(hostname):1111/callback1conf"
|
||||
echo "url1=${url1}"
|
||||
echo "url2=${url2}"
|
||||
response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"unconfirmedCallbackURL\":\"${url1}\",\"confirmedCallbackURL\":\"${url2}\"}" cyphernode:8888/watch)
|
||||
echo "response=${response}"
|
||||
|
||||
local id=$(echo "${response}" | jq ".id" | tr -d '\"')
|
||||
echo "id=${id}"
|
||||
local event=$(echo "${response}" | jq ".event" | tr -d '\"')
|
||||
echo "event=${event}"
|
||||
if [ "${event}" != "watch" ]; then
|
||||
exit 15
|
||||
fi
|
||||
address=$(echo "${response}" | jq ".address" | tr -d '\"')
|
||||
echo "address=${address}"
|
||||
if [ "${address}" != "${address1}" ]; then
|
||||
exit 20
|
||||
fi
|
||||
local imported=$(echo "${response}" | jq ".imported" | tr -d '\"')
|
||||
echo "imported=${imported}"
|
||||
if [ "${imported}" != "1" ]; then
|
||||
exit 30
|
||||
fi
|
||||
local inserted=$(echo "${response}" | jq ".inserted" | tr -d '\"')
|
||||
echo "inserted=${inserted}"
|
||||
if [ "${inserted}" != "1" ]; then
|
||||
exit 40
|
||||
fi
|
||||
local unconfirmedCallbackURL=$(echo "${response}" | jq ".unconfirmedCallbackURL" | tr -d '\"')
|
||||
echo "unconfirmedCallbackURL=${unconfirmedCallbackURL}"
|
||||
if [ "${unconfirmedCallbackURL}" != "${url1}" ]; then
|
||||
exit 60
|
||||
fi
|
||||
local confirmedCallbackURL=$(echo "${response}" | jq ".confirmedCallbackURL" | tr -d '\"')
|
||||
echo "confirmedCallbackURL=${confirmedCallbackURL}"
|
||||
if [ "${confirmedCallbackURL}" != "${url2}" ]; then
|
||||
exit 70
|
||||
fi
|
||||
|
||||
# Let's watch another address just to be able to test unwatch later and test if found in getactivewatches
|
||||
response=$(curl -s -H "Content-Type: application/json" -d "{\"address\":\"${address2}\",\"unconfirmedCallbackURL\":\"${url1}2\",\"confirmedCallbackURL\":\"${url2}2\"}" cyphernode:8888/watch)
|
||||
echo "response=${response}"
|
||||
echo "Tested watch."
|
||||
|
||||
# getactivewatches
|
||||
# (GET) http://cyphernode:8888/getactivewatches
|
||||
|
||||
echo "Testing getactivewatches..."
|
||||
response=$(curl -s cyphernode:8888/getactivewatches)
|
||||
echo "response=${response}"
|
||||
response=$(echo ${response} | jq ".watches[]")
|
||||
echo "response=${response}"
|
||||
local id2=$(echo ${response} | jq "select(.address == \"${address1}\") | .id" | tr -d '\"')
|
||||
echo "id2=${id2}"
|
||||
if [ "${id2}" != "${id}" ]; then
|
||||
exit 80
|
||||
fi
|
||||
id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"')
|
||||
echo "id2=${id2}"
|
||||
if [ -z "${id2}" ]; then
|
||||
exit 90
|
||||
fi
|
||||
echo "Tested getactivewatches."
|
||||
|
||||
# unwatch
|
||||
# (GET) http://cyphernode:8888/unwatch/2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp
|
||||
|
||||
echo "Testing unwatch..."
|
||||
response=$(curl -s cyphernode:8888/unwatch/${address2})
|
||||
echo "response=${response}"
|
||||
event=$(echo "${response}" | jq ".event" | tr -d '\"')
|
||||
echo "event=${event}"
|
||||
if [ "${event}" != "unwatch" ]; then
|
||||
exit 100
|
||||
fi
|
||||
address=$(echo "${response}" | jq ".address" | tr -d '\"')
|
||||
echo "address=${address}"
|
||||
if [ "${address}" != "${address2}" ]; then
|
||||
exit 110
|
||||
fi
|
||||
response=$(curl -s cyphernode:8888/getactivewatches)
|
||||
echo "response=${response}"
|
||||
response=$(echo "${response}" | jq ".watches[]")
|
||||
echo "response=${response}"
|
||||
id2=$(echo ${response} | jq "select(.address == \"${address2}\") | .id" | tr -d '\"')
|
||||
echo "id2=${id2}"
|
||||
if [ -n "${id2}" ]; then
|
||||
exit 120
|
||||
fi
|
||||
echo "Tested unwatch."
|
||||
|
||||
# deriveindex
|
||||
# (GET) http://cyphernode:8888/deriveindex/25-30
|
||||
# {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]}
|
||||
|
||||
echo "Testing deriveindex..."
|
||||
response=$(curl -v cyphernode:8888/deriveindex/25-30)
|
||||
echo "response=${response}"
|
||||
local nbaddr=$(echo "${response}" | jq ".addresses | length")
|
||||
if [ "${nbaddr}" -ne "6" ]; then
|
||||
exit 130
|
||||
fi
|
||||
address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"')
|
||||
if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then
|
||||
exit 140
|
||||
fi
|
||||
echo "Tested deriveindex."
|
||||
|
||||
# derivepubpath
|
||||
# (GET) http://cyphernode:8888/derivepubpath
|
||||
# BODY {"pub32":"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb","path":"0/25-30"}
|
||||
# {"addresses":[{"address":"2N6Q9kBcLtNswgMSLSQ5oduhbctk7hxEJW8"},{"address":"2NFLhFghAPKEPuZCKoeXYYxuaBxhKXbmhBV"},{"address":"2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP"},{"address":"2Mth8XDZpXkY9d95tort8HYEAuEesow2tF6"},{"address":"2MwqEmAXhUw6H7bJwMhD13HGWVEj2HgFiNH"},{"address":"2N2Y4BVRdrRFhweub2ehHXveGZC3nryMEJw"}]}
|
||||
|
||||
echo "Testing derivepubpath..."
|
||||
response=$(curl -v -H "Content-Type: application/json" -d "{\"pub32\":\"upub5GtUcgGed1aGH4HKQ3vMYrsmLXwmHhS1AeX33ZvDgZiyvkGhNTvGd2TA5Lr4v239Fzjj4ZY48t6wTtXUy2yRgapf37QHgt6KWEZ6bgsCLpb\",\"path\":\"0/25-30\"}" cyphernode:8888/derivepubpath)
|
||||
echo "response=${response}"
|
||||
local nbaddr=$(echo "${response}" | jq ".addresses | length")
|
||||
if [ "${nbaddr}" -ne "6" ]; then
|
||||
exit 150
|
||||
fi
|
||||
address=$(echo "${response}" | jq ".addresses[2].address" | tr -d '\"')
|
||||
if [ "${address}" != "2N7gepbQtRM5Hm4PTjvGadj9wAwEwnAsKiP" ]; then
|
||||
exit 160
|
||||
fi
|
||||
echo "Tested derivepubpath."
|
||||
|
||||
# spend
|
||||
# POST http://cyphernode:8888/spend
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
|
||||
# By spending to a watched address, we will test the spending feature and trigger the confirmation to test
|
||||
# confirmations of watched addresses... Cleva!!!
|
||||
|
||||
echo "Testing spend, conf and callbacks..."
|
||||
response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" cyphernode:8888/spend)
|
||||
echo "response=${response}"
|
||||
wait_for_callbacks
|
||||
echo "Tested spend, conf and callbacks."
|
||||
|
||||
# addtobatch
|
||||
# POST http://cyphernode:8888/addtobatch
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
|
||||
# By spending to a watched address, we will test the spending feature and trigger the confirmation to test
|
||||
# confirmations of watched addresses... Cleva!!!
|
||||
|
||||
# echo "Testing addtobatch..."
|
||||
# response=$(curl -v -H "Content-Type: application/json" -d "{\"address\":\"${address1}\",\"amount\":0.00001}" cyphernode:8888/spend)
|
||||
# echo "response=${response}"
|
||||
# wait_for_callbacks
|
||||
# echo "Tested addtobatch ."
|
||||
|
||||
|
||||
|
||||
|
||||
# conf
|
||||
# (GET) http://cyphernode:8888/conf/b081ca7724386f549cf0c16f71db6affeb52ff7a0d9b606fb2e5c43faffd3387
|
||||
|
||||
# Let's trigger tx confirmation even if not confirmed. Will be funny. Should take care of
|
||||
# multiple confirmations of the same state.
|
||||
|
||||
|
||||
|
||||
# executecallbacks
|
||||
# (GET) http://cyphernode::8080/executecallbacks
|
||||
|
||||
#echo "GET /getbestblockinfo" | nc cyphernode:8888 - | sed -En "s/^(\{.*)/\1/p" | jq
|
||||
|
||||
|
||||
|
||||
|
||||
# spend
|
||||
# POST http://cyphernode:8888/spend
|
||||
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","amount":0.00233}
|
||||
|
||||
#curl -v -H "Content-Type: application/json" -d '{"address":"2MsWyaQ8APbnqasFpWopqUKqsdpiVY3EwLE","amount":0.0001}' cyphernode:8888/spend
|
||||
|
||||
# ln_getinfo
|
||||
# (GET) http://cyphernode:8888/ln_getinfo
|
||||
|
||||
echo "Testing ln_getinfo..."
|
||||
response=$(curl -s cyphernode:8888/ln_getinfo)
|
||||
echo "response=${response}"
|
||||
local port=$(echo ${response} | jq ".binding[] | select(.type == \"ipv4\") | .port")
|
||||
echo "port=${port}"
|
||||
if [ "${port}" != "9735" ]; then
|
||||
exit 170
|
||||
fi
|
||||
echo "Tested ln_getinfo."
|
||||
|
||||
# ln_newaddr
|
||||
# (GET) http://cyphernode:8888/ln_newaddr
|
||||
|
||||
echo "Testing ln_newaddr..."
|
||||
response=$(curl -s cyphernode:8888/ln_newaddr)
|
||||
echo "response=${response}"
|
||||
address=$(echo ${response} | jq ".address")
|
||||
echo "address=${address}"
|
||||
if [ -z "${address}" ]; then
|
||||
exit 180
|
||||
fi
|
||||
echo "Tested ln_newaddr."
|
||||
|
||||
# ln_create_invoice
|
||||
# POST http://cyphernode:8888/ln_create_invoice
|
||||
# BODY {"msatoshi":"10000","label":"koNCcrSvhX3dmyFhW","description":"Bylls order #10649","expiry":"10"}
|
||||
|
||||
#echo "Testing ln_create_invoice..."
|
||||
#response=$(curl -v -H "Content-Type: application/json" -d "{\"msatoshi\":10000,\"label\":\"koNCcrSvhX3dmyFhW\",\"description\":\"Bylls order #10649\",\"expiry\":10}" cyphernode:8888/ln_create_invoice)
|
||||
#echo "response=${response}"
|
||||
|
||||
#echo "Tested ln_create_invoice."
|
||||
|
||||
# ln_pay
|
||||
|
||||
|
||||
}
|
||||
|
||||
wait_for_callbacks()
|
||||
{
|
||||
nc -vlp1111 -e ./tests-cb.sh
|
||||
nc -vlp1111 -e ./tests-cb.sh
|
||||
}
|
||||
|
||||
tests
|
||||
15
proxy_docker/app/script/trace.sh
Normal file
15
proxy_docker/app/script/trace.sh
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
|
||||
trace()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "$(date -Is) ${1}" > /dev/stderr
|
||||
fi
|
||||
}
|
||||
|
||||
trace_rc()
|
||||
{
|
||||
if [ -n "${TRACING}" ]; then
|
||||
echo "$(date -Is) Last return code: ${1}" > /dev/stderr
|
||||
fi
|
||||
}
|
||||
27
proxy_docker/app/script/unwatchrequest.sh
Normal file
27
proxy_docker/app/script/unwatchrequest.sh
Normal file
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sql.sh
|
||||
|
||||
unwatchrequest()
|
||||
{
|
||||
trace "Entering unwatchrequest()..."
|
||||
|
||||
local request=${1}
|
||||
local address=$(echo "${request}" | cut -d ' ' -f2 | cut -d '/' -f3)
|
||||
local returncode
|
||||
trace "[unwatchrequest] Unwatch request on address ${address})"
|
||||
|
||||
sql "UPDATE watching SET watching=0 WHERE address=\"${address}\""
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
|
||||
data="{\"event\":\"unwatch\",\"address\":\"${address}\"}"
|
||||
trace "[unwatchrequest] responding=${data}"
|
||||
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *unwatchrequest.sh) unwatchrequest $@;; esac
|
||||
17
proxy_docker/app/script/utils.sh
Normal file
17
proxy_docker/app/script/utils.sh
Normal file
@@ -0,0 +1,17 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
|
||||
get_prop()
|
||||
{
|
||||
trace "Entering get_prop()..."
|
||||
|
||||
local property=${1}
|
||||
trace "[get_prop] property=${property}"
|
||||
|
||||
local value=$(grep "${property}" config.properties | cut -d'=' -f2)
|
||||
|
||||
trace "[get_prop] value=${value}"
|
||||
|
||||
echo ${value}
|
||||
}
|
||||
193
proxy_docker/app/script/walletoperations.sh
Normal file
193
proxy_docker/app/script/walletoperations.sh
Normal file
@@ -0,0 +1,193 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
|
||||
spend()
|
||||
{
|
||||
trace "Entering spend()..."
|
||||
|
||||
local data
|
||||
local request=${1}
|
||||
local address=$(echo "${request}" | jq ".address" | tr -d '"')
|
||||
trace "[spend] address=${address}"
|
||||
local amount=$(echo "${request}" | jq ".amount" | awk '{ printf "%.8f", $0 }')
|
||||
trace "[spend] amount=${amount}"
|
||||
local response
|
||||
local id_inserted
|
||||
|
||||
response=$(send_to_spender_node "{\"method\":\"sendtoaddress\",\"params\":[\"${address}\",${amount}]}")
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[spend] response=${response}"
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
local txid=$(echo ${response} | jq ".result" | tr -d '"')
|
||||
trace "[spend] txid=${txid}"
|
||||
|
||||
# Let's insert the txid in our little DB to manage the confirmation and tell it's not a watching address
|
||||
# sql "INSERT OR IGNORE INTO watching (watching, txid) VALUES (0, ${txid})"
|
||||
sql "INSERT OR IGNORE INTO tx (txid) VALUES (\"${txid}\")"
|
||||
trace_rc $?
|
||||
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
|
||||
trace_rc $?
|
||||
sql "INSERT OR IGNORE INTO recipient (address, amount, tx_id) VALUES (\"${address}\", ${amount}, ${id_inserted})"
|
||||
trace_rc $?
|
||||
|
||||
data="{\"status\":\"accepted\""
|
||||
data="${data},\"hash\":\"${txid}\"}"
|
||||
else
|
||||
local message=$(echo "${response}" | jq -e ".error.message")
|
||||
data="{\"message\":${message}}"
|
||||
fi
|
||||
|
||||
trace "[spend] responding=${data}"
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
getbalance()
|
||||
{
|
||||
trace "Entering getbalance()..."
|
||||
|
||||
local response
|
||||
local data='{"method":"getbalance"}'
|
||||
response=$(send_to_spender_node "${data}")
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[getbalance] response=${response}"
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
local balance=$(echo ${response} | jq ".result")
|
||||
trace "[getbalance] balance=${balance}"
|
||||
|
||||
data="{\"balance\":${balance}}"
|
||||
else
|
||||
trace "[getbalance] Coudn't get balance!"
|
||||
data=""
|
||||
fi
|
||||
|
||||
trace "[getbalance] responding=${data}"
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
getnewaddress()
|
||||
{
|
||||
trace "Entering getnewaddress()..."
|
||||
|
||||
local response
|
||||
local data='{"method":"getnewaddress"}'
|
||||
response=$(send_to_spender_node "${data}")
|
||||
local returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[getnewaddress] response=${response}"
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
local address=$(echo ${response} | jq ".result")
|
||||
trace "[getnewaddress] address=${address}"
|
||||
|
||||
data="{\"address\":${address}}"
|
||||
else
|
||||
trace "[getnewaddress] Coudn't get a new address!"
|
||||
data=""
|
||||
fi
|
||||
|
||||
trace "[getnewaddress] responding=${data}"
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
addtobatching()
|
||||
{
|
||||
trace "Entering addtobatching()..."
|
||||
|
||||
local address=${1}
|
||||
trace "[addtobatching] address=${address}"
|
||||
local amount=${2}
|
||||
trace "[addtobatching] amount=${amount}"
|
||||
|
||||
sql "INSERT OR IGNORE INTO recipient (address, amount) VALUES (\"${address}\", ${amount})"
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
batchspend()
|
||||
{
|
||||
trace "Entering batchspend()..."
|
||||
|
||||
local data
|
||||
local response
|
||||
local recipientswhere
|
||||
local recipientsjson
|
||||
local id_inserted
|
||||
|
||||
# We will batch all the addresses in DB without a TXID
|
||||
local batching=$(sql 'SELECT address, amount FROM recipient WHERE tx_id IS NULL')
|
||||
trace "[batchspend] batching=${batching}"
|
||||
|
||||
local returncode
|
||||
local address
|
||||
local amount
|
||||
local notfirst=false
|
||||
local IFS=$'\n'
|
||||
for row in ${batching}
|
||||
do
|
||||
trace "[batchspend] row=${row}"
|
||||
address=$(echo "${row}" | cut -d '|' -f1)
|
||||
trace "[batchspend] address=${address}"
|
||||
amount=$(echo "${row}" | cut -d '|' -f2)
|
||||
trace "[batchspend] amount=${amount}"
|
||||
|
||||
if ${notfirst}; then
|
||||
recipientswhere="${recipientswhere},"
|
||||
recipientsjson="${recipientsjson},"
|
||||
else
|
||||
notfirst=true
|
||||
fi
|
||||
|
||||
recipientswhere="${recipientswhere}\"${address}\""
|
||||
recipientsjson="${recipientsjson}\"${address}\":${amount}"
|
||||
done
|
||||
|
||||
response=$(send_to_spender_node "{\"method\":\"sendmany\",\"params\":[\"\", {${recipientsjson}}]}")
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
trace "[batchspend] response=${response}"
|
||||
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
local txid=$(echo ${response} | jq ".result" | tr -d '"')
|
||||
trace "[batchspend] txid=${txid}"
|
||||
|
||||
# Let's insert the txid in our little DB to manage the confirmation and tell it's not a watching address
|
||||
# sql "INSERT OR IGNORE INTO watching (watching, txid) VALUES (0, ${txid})"
|
||||
# trace_rc $?
|
||||
sql "INSERT OR IGNORE INTO tx (txid) VALUES (\"${txid}\")"
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
id_inserted=$(sql "SELECT id FROM tx WHERE txid=\"${txid}\"")
|
||||
trace "[batchspend] id_inserted: ${id_inserted}"
|
||||
sql "UPDATE recipient SET tx_id=${id_inserted} WHERE address IN (${recipientswhere})"
|
||||
trace_rc $?
|
||||
fi
|
||||
|
||||
data="{\"status\":\"accepted\""
|
||||
data="${data},\"hash\":\"${txid}\"}"
|
||||
else
|
||||
local message=$(echo "${response}" | jq -e ".error.message")
|
||||
data="{\"message\":${message}}"
|
||||
fi
|
||||
|
||||
trace "[batchspend] responding=${data}"
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
#case "${0}" in *walletoperations.sh) getbalance $@;; esac
|
||||
75
proxy_docker/app/script/watchrequest.sh
Normal file
75
proxy_docker/app/script/watchrequest.sh
Normal file
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
|
||||
. ./trace.sh
|
||||
. ./importaddress.sh
|
||||
. ./sql.sh
|
||||
. ./sendtobitcoinnode.sh
|
||||
|
||||
watchrequest()
|
||||
{
|
||||
trace "Entering watchrequest()..."
|
||||
|
||||
local returncode
|
||||
local request=${1}
|
||||
local address=$(echo "${request}" | jq ".address" | tr -d '"')
|
||||
local cb0conf_url=$(echo "${request}" | jq ".unconfirmedCallbackURL" | tr -d '"')
|
||||
local cb1conf_url=$(echo "${request}" | jq ".confirmedCallbackURL" | tr -d '"')
|
||||
local imported
|
||||
local inserted
|
||||
local id_inserted
|
||||
local result
|
||||
trace "[watchrequest] Watch request on address (${address}), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url})"
|
||||
|
||||
result=$(importaddress_rpc "${address}")
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
imported=1
|
||||
else
|
||||
imported=0
|
||||
fi
|
||||
|
||||
# sql "INSERT OR IGNORE INTO watching (address, watching, callback0conf, callback1conf, imported) VALUES (\"${address}\", 1, \"${cb0conf_url}\", \"${cb1conf_url}\", ${imported})"
|
||||
sql "INSERT OR IGNORE INTO watching (address, watching, callback0conf, callback1conf, imported) VALUES (\"${address}\", 1, \"${cb0conf_url}\", \"${cb1conf_url}\", ${imported})"
|
||||
returncode=$?
|
||||
trace_rc ${returncode}
|
||||
if [ "${returncode}" -eq 0 ]; then
|
||||
inserted=1
|
||||
id_inserted=$(sql "SELECT id FROM watching WHERE address='${address}'")
|
||||
trace "[watchrequest] id_inserted: ${id_inserted}"
|
||||
else
|
||||
inserted=0
|
||||
fi
|
||||
|
||||
local fees2blocks
|
||||
local fees6blocks
|
||||
local fees36blocks
|
||||
local fees144blocks
|
||||
fees2blocks=$(getestimatesmartfee 2)
|
||||
trace_rc $?
|
||||
fees6blocks=$(getestimatesmartfee 6)
|
||||
trace_rc $?
|
||||
fees36blocks=$(getestimatesmartfee 36)
|
||||
trace_rc $?
|
||||
fees144blocks=$(getestimatesmartfee 144)
|
||||
trace_rc $?
|
||||
|
||||
local data="{\"id\":\"${id_inserted}\",
|
||||
\"event\":\"watch\",
|
||||
\"imported\":\"${imported}\",
|
||||
\"inserted\":\"${inserted}\",
|
||||
\"address\":\"${address}\",
|
||||
\"unconfirmedCallbackURL\":\"${cb0conf_url}\",
|
||||
\"confirmedCallbackURL\":\"${cb1conf_url}\",
|
||||
\"estimatesmartfee2blocks\":\"${fees2blocks}\",
|
||||
\"estimatesmartfee6blocks\":\"${fees6blocks}\",
|
||||
\"estimatesmartfee36blocks\":\"${fees36blocks}\",
|
||||
\"estimatesmartfee144blocks\":\"${fees144blocks}\"}"
|
||||
trace "[watchrequest] responding=${data}"
|
||||
|
||||
echo "${data}"
|
||||
|
||||
return ${returncode}
|
||||
}
|
||||
|
||||
case "${0}" in *watchrequest.sh) watchrequest $@;; esac
|
||||
11
proxy_docker/env.properties
Normal file
11
proxy_docker/env.properties
Normal file
@@ -0,0 +1,11 @@
|
||||
TRACING=1
|
||||
WATCHER_BTC_NODE_RPC_URL=btcnode:18332/wallet/watching01.dat
|
||||
SPENDER_BTC_NODE_RPC_URL=btcnode:18332/wallet/spending01.dat
|
||||
PROXY_LISTENING_PORT=8888
|
||||
# Variable substitutions don't work
|
||||
DB_PATH=/proxyuser/db
|
||||
DB_FILE=/proxyuser/db/proxydb
|
||||
# Pycoin container
|
||||
PYCOIN_CONTAINER=pycoinnode:7777
|
||||
# OTS container
|
||||
OTS_CONTAINER=otsnode:6666
|
||||
Reference in New Issue
Block a user