Support for Bitcoin Core labels for watched and new addresses

This commit is contained in:
kexkey
2021-08-08 14:47:28 -04:00
parent 80f17d9176
commit 2e60b91a6a
10 changed files with 170 additions and 18 deletions

View File

@@ -9,7 +9,7 @@ Inserts the address, webhook URLs and eventMessage in the DB and imports the add
```http
POST http://cyphernode:8888/watch
with body...
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K"}
{"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K","label":"myLabel"}
```
Proxy response:
@@ -23,6 +23,7 @@ Proxy response:
"address": "2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp",
"unconfirmedCallbackURL": "192.168.133.233:1111/callback0conf",
"confirmedCallbackURL": "192.168.133.233:1111/callback1conf",
"label": "myLabel",
"estimatesmartfee2blocks": "0.000010",
"estimatesmartfee6blocks": "0.000010",
"estimatesmartfee36blocks": "0.000010",
@@ -721,6 +722,20 @@ GET http://cyphernode:8888/getnewaddress/legacy
GET http://cyphernode:8888/getnewaddress/p2sh-segwit
```
or
```http
POST http://cyphernode:8888/getnewaddress
with body...
{"address_type":"bech32","label":"myLabel"}
or
{"label":"myLabel"}
or
{"address_type":"p2sh-segwit"}
or
{}
```
Proxy response:
```json
@@ -731,7 +746,9 @@ Proxy response:
```json
{
"address":"tb1ql7yvh3lmajxmaljsnsu3w8lhwczu963tvjfzpj"
"address":"tb1ql7yvh3lmajxmaljsnsu3w8lhwczu963tvjfzpj",
"label":"myLabel",
"address_type":"bech32"
}
```

View File

@@ -62,6 +62,9 @@ paths:
eventMessage:
description: "Will be part of the published message on confirmations"
type: "string"
label:
description: "Label for this address that will be imported in Bitcoin Core"
type: "string"
responses:
'200':
description: "successfully created"
@@ -1096,8 +1099,16 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/getnewaddress:
/getnewaddress/{address_type}:
get:
parameters:
- in: "path"
name: "address_type"
description: "Address type"
required: false
schema:
type: "string"
enum: ["legacy", "p2sh-segwit", "bech32"]
tags:
- "spending wallet"
- "core features"
@@ -1114,6 +1125,57 @@ paths:
properties:
address:
$ref: '#/components/schemas/TypeAddressString'
address_type:
type: "string"
enum: ["legacy", "p2sh-segwit", "bech32"]
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/getnewaddress:
post:
tags:
- "spending wallet"
- "core features"
summary: "Generates a new address on the spending wallet"
description: "Generates a new address on the spending wallet. Useful to refill the spending wallet from cold wallet (ie Trezor)."
operationId: "getSpendingWalletNewAddress"
requestBody:
description: "Bitcoin address properties"
required: false
content:
application/json:
schema:
type: "object"
properties:
address_type:
type: "string"
enum: ["legacy", "p2sh-segwit", "bech32"]
label:
type: "string"
responses:
'200':
description: "successfully got an address"
content:
application/json:
schema:
type: "object"
required:
- "address"
properties:
address:
$ref: '#/components/schemas/TypeAddressString'
address_type:
type: "string"
enum: ["legacy", "p2sh-segwit", "bech32"]
label:
type: "string"
'400':
$ref: '#/components/schemas/ApiResponseInvalidInput'
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'503':
@@ -2491,6 +2553,7 @@ components:
- "unconfirmedCallbackURL"
- "confirmedCallbackURL"
- "eventMessage"
- "label"
properties:
id:
type: "string"
@@ -2512,6 +2575,9 @@ components:
description: "Async callback in case of activity on address"
type: "string"
format: "url"
label:
description: "Label for this address that will be imported in Bitcoin Core"
type: "string"
estimatesmartfee2blocks:
type: "string"
estimatesmartfee6blocks:

View File

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

View File

@@ -0,0 +1,9 @@
PRAGMA foreign_keys=off;
BEGIN TRANSACTION;
ALTER TABLE watching ADD COLUMN label TEXT;
COMMIT;
PRAGMA foreign_keys=on;

View File

@@ -67,7 +67,7 @@ getactivewatches() {
local watches
# Let's build the string directly with sqlite instead of manipulating multiple strings afterwards, it's faster.
# {"id":"${id}","address":"${address}","imported":"${imported}","unconfirmedCallbackURL":"${cb0conf_url}","confirmedCallbackURL":"${cb1conf_url}","watching_since":"${timestamp}"}
watches=$(sql "SELECT '{\"id\":' || id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":\"' || COALESCE(callback0conf, '') || '\",\"confirmedCallbackURL\":\"' || COALESCE(callback1conf, '') || '\",\"watching_since\":\"' || inserted_ts || '\"}' FROM watching WHERE watching AND NOT calledback1conf")
watches=$(sql "SELECT '{\"id\":' || id || ',\"address\":\"' || address || '\",\"imported\":' || imported || ',\"unconfirmedCallbackURL\":\"' || COALESCE(callback0conf, '') || '\",\"confirmedCallbackURL\":\"' || COALESCE(callback1conf, '') || '\",\"label\":\"' || COALESCE(label, '') || '\",\"watching_since\":\"' || inserted_ts || '\"}' FROM watching WHERE watching AND NOT calledback1conf")
returncode=$?
trace_rc ${returncode}

View File

@@ -7,7 +7,12 @@ importaddress_rpc() {
trace "[Entering importaddress_rpc()]"
local address=${1}
local data="{\"method\":\"importaddress\",\"params\":[\"${address}\",\"\",false]}"
local label=${2}
if [ -z "${label}" ]; then
label="null"
fi
local data='{"method":"importaddress","params":{"address":"'${address}'","label":'${label}',"rescan":false}}'
# local data="{\"method\":\"importaddress\",\"params\":[\"${address}\",\"\",false]}"
local result
result=$(send_to_watcher_node ${data})
local returncode=$?

View File

@@ -11,15 +11,17 @@ manage_not_imported() {
trace "[Entering manage_not_imported()]"
local watches=$(sql 'SELECT address FROM watching WHERE watching AND NOT imported')
local watches=$(sql 'SELECT address, label FROM watching WHERE watching AND NOT imported')
trace "[manage_not_imported] watches=${watches}"
local result
local returncode
local IFS=$'\n'
for address in ${watches}
for row in ${watches}
do
result=$(importaddress_rpc "${address}")
address=$(echo "${row}" | cut -d '|' -f1)
label=$(echo "${row}" | cut -d '|' -f2)
result=$(importaddress_rpc "${address}" "${label}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then

View File

@@ -93,6 +93,7 @@ main() {
# 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"}
# BODY {"address":"2N8DcqzfkYi8CkYzvNNS5amoq3SbAcQNXKp","confirmedCallbackURL":"192.168.111.233:1111/callback1conf","eventMessage":"eyJib3VuY2VfYWRkcmVzcyI6IjJNdkEzeHIzOHIxNXRRZWhGblBKMVhBdXJDUFR2ZTZOamNGIiwibmJfY29uZiI6MH0K","label":"myLabel"}
response=$(watchrequest "${line}")
response_to_client "${response}" ${?}
@@ -328,8 +329,23 @@ main() {
getnewaddress)
# curl (GET) http://192.168.111.152:8080/getnewaddress
# curl (GET) http://192.168.111.152:8080/getnewaddress/bech32
#
# or...
# POST http://192.168.111.152:8080/getnewaddress
# BODY {"address_type":"bech32","label":"myLabel"}
# BODY {"label":"myLabel"}
# BODY {"address_type":"p2sh-segwit"}
# BODY {}
response=$(getnewaddress "$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)")
# Let's make it work even for a GET request (equivalent to a POST with empty json object body)
if [ "$http_method" = "POST" ]; then
address_type=$(echo "${line}" | jq -er ".addressType // empty")
label=$(echo "${line}" | jq -er ".label // empty")
else
address_type=$(echo "${line}" | cut -d ' ' -f2 | cut -d '/' -f3)
fi
response=$(getnewaddress "${address_type}" "${label}")
response_to_client "${response}" ${?}
break
;;

View File

@@ -270,13 +270,29 @@ getnewaddress() {
local address_type=${1}
trace "[getnewaddress] address_type=${address_type}"
local label=${2}
trace "[getnewaddress] label=${label}"
local response
local data
if [ -z "${address_type}" ]; then
data='{"method":"getnewaddress"}'
else
data="{\"method\":\"getnewaddress\",\"params\":[\"\",\"${address_type}\"]}"
local jqop
local addedfields
local data='{"method":"getnewaddress"}'
if [ -n "${address_type}" ] || [ -n "${label}" ]; then
jqop='. += {"params":{}}'
if [ -n "${label}" ]; then
jqop=${jqop}' | .params += {"label":"'${label}'"}'
addedfields=' | . += {"label":"'${label}'"}'
fi
if [ -n "${address_type}" ]; then
jqop=${jqop}' | .params += {"address_type":"'${address_type}'"}'
addedfields=' | . += {"address_type":"'${address_type}'"}'
fi
trace "[getnewaddress] jqop=${jqop}"
data=$(echo "${data}" | jq -rc "${jqop}")
fi
trace "[getnewaddress] data=${data}"
response=$(send_to_spender_node "${data}")
local returncode=$?
trace_rc ${returncode}
@@ -286,7 +302,11 @@ getnewaddress() {
local address=$(echo ${response} | jq ".result")
trace "[getnewaddress] address=${address}"
data="{\"address\":${address}}"
data='{"address":'${address}'}'
if [ -n "${jqop}" ]; then
data=$(echo "${data}" | jq -rc "${data}${addedfields}")
trace "[getnewaddress] data=${data}"
fi
else
trace "[getnewaddress] Coudn't get a new address!"
data=""

View File

@@ -15,6 +15,7 @@ watchrequest() {
local cb0conf_url=$(echo "${request}" | jq ".unconfirmedCallbackURL")
local cb1conf_url=$(echo "${request}" | jq ".confirmedCallbackURL")
local event_message=$(echo "${request}" | jq ".eventMessage")
local label=$(echo "${request}" | jq ".label")
local imported
local inserted
local id_inserted
@@ -23,7 +24,7 @@ watchrequest() {
# Let's lowercase bech32 addresses
address=$(lowercase_if_bech32 "${address}")
trace "[watchrequest] Watch request on address (\"${address}\"), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url}) with event_message=${event_message}"
trace "[watchrequest] Watch request on address (\"${address}\"), cb 0-conf (${cb0conf_url}), cb 1-conf (${cb1conf_url}) with event_message=${event_message} and label=${label}"
local isvalid
isvalid=$(validateaddress "${address}" | jq ".result.isvalid")
@@ -38,6 +39,7 @@ watchrequest() {
\"address\":\"${address}\",
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url},
\"label\":${label},
\"eventMessage\":${event_message}}}}"
trace "[watchrequest] Invalid address"
trace "[watchrequest] responding=${result}"
@@ -47,7 +49,7 @@ watchrequest() {
return 1
fi
result=$(importaddress_rpc ${address})
result=$(importaddress_rpc "${address}" "${label}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
@@ -56,7 +58,7 @@ watchrequest() {
imported=0
fi
sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, event_message) VALUES (\"${address}\", 1, ${cb0conf_url}, ${cb1conf_url}, ${imported}, ${event_message}) ON CONFLICT(address,callback0conf,callback1conf) DO UPDATE SET watching=1, event_message=${event_message}, calledback0conf=0, calledback1conf=0"
sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, event_message, label) VALUES (\"${address}\", 1, ${cb0conf_url}, ${cb1conf_url}, ${imported}, ${event_message}, ${label}) ON CONFLICT(address,callback0conf,callback1conf) DO UPDATE SET watching=1, event_message=${event_message}, calledback0conf=0, calledback1conf=0, label=${label}"
returncode=$?
trace_rc ${returncode}
@@ -88,6 +90,7 @@ watchrequest() {
\"address\":\"${address}\",
\"unconfirmedCallbackURL\":${cb0conf_url},
\"confirmedCallbackURL\":${cb1conf_url},
\"label\":${label},
\"estimatesmartfee2blocks\":${fees2blocks},
\"estimatesmartfee6blocks\":${fees6blocks},
\"estimatesmartfee36blocks\":${fees36blocks},