Files
cyphernode/proxy_docker/app/script/watchrequest.sh
2021-11-23 12:05:10 -05:00

584 lines
19 KiB
Bash

#!/bin/sh
. ./trace.sh
. ./importaddress.sh
. ./sql.sh
. ./sendtobitcoinnode.sh
. ./bitcoin.sh
watchrequest() {
trace "Entering watchrequest()..."
local returncode
local request=${1}
local address address_pg
address=$(echo "${request}" | jq -re ".address")
if [ "$?" -ne "0" ]; then
# address not found or null
result='{"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"address required"}}'
trace "[watchrequest] address required"
trace "[watchrequest] responding=${result}"
echo "${result}"
return 1
else
address_pg="'${address}'"
fi
local cb0conf_url cb0conf_url_pg cb0conf_url_pg_where cb0conf_url_json
cb0conf_url=$(echo "${request}" | jq -re ".unconfirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# unconfirmedCallbackURL not found or null
cb0conf_url_json="null"
cb0conf_url_pg="null"
cb0conf_url_pg_where=" IS NULL"
else
cb0conf_url_json="\"${cb0conf_url}\""
cb0conf_url_pg="'${cb0conf_url}'"
cb0conf_url_pg_where="=${cb0conf_url_pg}"
fi
local cb1conf_url cb1conf_url_pg cb1conf_url_pg_where cb1conf_url_json
cb1conf_url=$(echo "${request}" | jq -re ".confirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# confirmedCallbackURL not found or null
cb1conf_url_json="null"
cb1conf_url_pg="null"
cb1conf_url_pg_where=" IS NULL"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
cb1conf_url_pg_where="=${cb1conf_url_pg}"
fi
local event_message event_message_pg event_message_json
event_message=$(echo "${request}" | jq -re ".eventMessage")
if [ "$?" -ne "0" ]; then
# eventMessage not found or null
event_message_json="null"
event_message_pg="null"
else
event_message_json="\"${event_message}\""
event_message_pg="'${event_message}'"
fi
local label label_pg label_json
label=$(echo "${request}" | jq -re ".label")
if [ "$?" -ne "0" ]; then
# label not found or null
label_json="null"
label_pg="null"
else
label_json="\"${label}\""
label_pg="'${label}'"
fi
local imported
local inserted
local id_inserted
local result
# Let's lowercase bech32 addresses
address=$(lowercase_if_bech32 "${address}")
trace "[watchrequest] Watch request on address (${address}), cb 0-conf (${cb0conf_url_json}), cb 1-conf (${cb1conf_url_json}) with event_message=${event_message_json} and label=${label_json}"
local isvalid
isvalid=$(validateaddress "${address}" | jq ".result.isvalid")
if [ "${isvalid}" != "true" ]; then
result='{'\
'"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"Invalid address",'\
'"data":{'\
'"event":"watch",'\
'"address":'"${address}"','\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"label":'${label_json}','\
'"eventMessage":'${event_message_json}'}}}'
trace "[watchrequest] Invalid address"
trace "[watchrequest] responding=${result}"
echo "${result}"
return 1
fi
result=$(importaddress_rpc "${address}" "${label}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
imported=true
else
imported=false
fi
id_inserted=$(sql "INSERT INTO watching (address, watching, callback0conf, callback1conf, imported, event_message, label)"\
" VALUES (${address_pg}, true, ${cb0conf_url_pg}, ${cb1conf_url_pg}, ${imported}, ${event_message_pg}, ${label_pg})"\
" ON CONFLICT (address, COALESCE(callback0conf, ''), COALESCE(callback1conf, '')) DO"\
" UPDATE SET watching=true, event_message=${event_message_pg}, calledback0conf=false, calledback1conf=false, label=${label_pg}"\
" RETURNING id" \
"SELECT id FROM watching WHERE address=${address_pg} AND callback0conf${cb0conf_url_pg_where} AND callback1conf${cb1conf_url_pg_where}")
returncode=$?
trace_rc ${returncode}
trace "[watchrequest] id_inserted=${id_inserted}"
if [ "${returncode}" -eq 0 ]; then
inserted=true
trace "[watchrequest] id_inserted: ${id_inserted}"
else
inserted=false
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 $?
result='{"id":'${id_inserted}','\
'"event":"watch",'\
'"imported":'${imported}','\
'"inserted":'${inserted}','\
'"address":"'${address}'",'\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"label":'${label_json}','\
'"estimatesmartfee2blocks":'${fees2blocks}','\
'"estimatesmartfee6blocks":'${fees6blocks}','\
'"estimatesmartfee36blocks":'${fees36blocks}','\
'"estimatesmartfee144blocks":'${fees144blocks}','\
'"eventMessage":'${event_message_json}'}'
trace "[watchrequest] responding=${result}"
echo "${result}"
return ${returncode}
}
watchpub32request() {
trace "Entering watchpub32request()..."
# BODY {"label":"4421","pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/n","nstart":0,"unconfirmedCallbackURL":"192.168.111.233:1111/callback0conf","confirmedCallbackURL":"192.168.111.233:1111/callback1conf"}
# Required:
# - "label"
# - "pub32"
# - "path"
# - "nstart"
local returncode
local request=${1}
local label=$(echo "${request}" | jq -er ".label")
if [ "$?" -ne "0" ]; then
# label not found or null
trace "[watchpub32request] label required"
echo '{"error":"label required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] label=${label}"
local pub32=$(echo "${request}" | jq -er ".pub32")
if [ "$?" -ne "0" ]; then
# pub32 not found or null
trace "[watchpub32request] pub32 required"
echo '{"error":"pub32 required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] pub32=${pub32}"
local path=$(echo "${request}" | jq -er ".path")
if [ "$?" -ne "0" ]; then
# path not found or null
trace "[watchpub32request] path required"
echo '{"error":"path required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] path=${path}"
local nstart=$(echo "${request}" | jq -er ".nstart")
if [ "$?" -ne "0" ]; then
# nstart not found or null
trace "[watchpub32request] nstart required"
echo '{"error":"nstart required","event":"watchxpub"}'
return 1
fi
trace "[watchpub32request] nstart=${nstart}"
local cb0conf_url=$(echo "${request}" | jq -r ".unconfirmedCallbackURL // empty")
trace "[watchpub32request] cb0conf_url=${cb0conf_url}"
local cb1conf_url=$(echo "${request}" | jq -r ".confirmedCallbackURL // empty")
trace "[watchpub32request] cb1conf_url=${cb1conf_url}"
watchpub32 "${label}" "${pub32}" "${path}" "${nstart}" "${cb0conf_url}" "${cb1conf_url}"
returncode=$?
trace_rc ${returncode}
return ${returncode}
}
watchpub32() {
trace "Entering watchpub32()..."
# Expecting args without quotes
# label, pub32, path and nstart are required
# When cb0conf_url and cb1conf_url are empty, means null
local returncode
local label=${1}
local label_pg="'${label}'"
trace "[watchpub32] label=${label}, label_pg=${label_pg}"
local pub32=${2}
local pub32_pg="'${pub32}'"
trace "[watchpub32] pub32=${pub32}, pub32_pg=${pub32_pg}"
local path=${3}
local path_pg="'${path}'"
trace "[watchpub32] path=${path}, path_pg=${path_pg}"
local nstart=${4}
trace "[watchpub32] nstart=${nstart}"
local last_n=$((${nstart}+${XPUB_DERIVATION_GAP}))
trace "[watchpub32] last_n=${last_n}"
local cb0conf_url=${5}
local cb0conf_url_pg cb0conf_url_json
if [ -z "${cb0conf_url}" ]; then
# Empty url
cb0conf_url_json="null"
cb0conf_url_pg="null"
else
cb0conf_url_json="\"${cb0conf_url}\""
cb0conf_url_pg="'${cb0conf_url}'"
fi
trace "[watchpub32] cb0conf_url=${cb0conf_url}, cb0conf_url_pg=${cb0conf_url_pg}"
local cb1conf_url=${6}
local cb1conf_url_pg cb1conf_url_json
if [ -z "${cb1conf_url}" ]; then
# Empty url
cb1conf_url_json="null"
cb1conf_url_pg="null"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
fi
trace "[watchpub32] cb1conf_url=${cb1conf_url}, cb1conf_url_pg=${cb1conf_url_pg}"
# upto_n is used when extending the watching window
local upto_n=${7}
trace "[watchpub32] upto_n=${upto_n}"
local id_inserted
local result
local error_msg
local data
# Derive with bitcoind...
# {"pub32":"tpubD6NzVbkrYhZ4YR3QK2tyfMMvBghAvqtNaNK1LTyDWcRHLcMUm3ZN2cGm5BS3MhCRCeCkXQkTXXjiJgqxpqXK7PeUSp86DTTgkLpcjMtpKWk","path":"0/25-30"}
if [ -n "${upto_n}" ]; then
# If upto_n provided, then we create from nstart to upto_n (instead of + GAP)
last_n=${upto_n}
fi
local subspath=$(echo -e $path | sed -En "s/n/${nstart}-${last_n}/p")
trace "[watchpub32] subspath=${subspath}"
local addresses
addresses=$(derivepubpath '{"pub32":"'${pub32}'","path":"'${subspath}'"}')
returncode=$?
trace_rc ${returncode}
# trace "[watchpub32] addresses=${addresses}"
if [ "${returncode}" -eq 0 ]; then
# result=$(create_wallet "${pub32}")
# returncode=$?
# trace_rc ${returncode}
# trace "[watchpub32request] result=${result}"
trace "[watchpub32] Skipping create_wallet"
if [ "${returncode}" -eq 0 ]; then
# Importmulti in Bitcoin Core...
result=$(importmulti_rpc "${WATCHER_BTC_NODE_XPUB_WALLET}" "${pub32}" "${addresses}")
returncode=$?
trace_rc ${returncode}
trace "[watchpub32] result=${result}"
if [ "${returncode}" -eq 0 ]; then
if [ -n "${upto_n}" ]; then
# Update existing row, we are extending the watching window
id_inserted=$(sql "UPDATE watching_by_pub32 set last_imported_n=${upto_n} WHERE pub32=${pub32_pg} RETURNING id")
returncode=$?
trace_rc ${returncode}
else
# Insert in our DB...
id_inserted=$(sql "INSERT INTO watching_by_pub32 (pub32, label, derivation_path, watching, callback0conf, callback1conf, last_imported_n)"\
" VALUES (${pub32_pg}, ${label_pg}, ${path_pg}, true, ${cb0conf_url_pg}, ${cb1conf_url_pg}, ${last_n})"\
" ON CONFLICT (pub32) DO"\
" UPDATE SET watching=true, label=${label_pg}, callback0conf=${cb0conf_url_pg}, callback1conf=${cb1conf_url_pg}, derivation_path=${path_pg}, last_imported_n=${last_n}"\
" RETURNING id" \
"SELECT id FROM watching_by_pub32 WHERE pub32=${pub32_pg}")
returncode=$?
trace_rc ${returncode}
fi
if [ "${returncode}" -eq 0 ]; then
trace "[watchpub32] id_inserted: ${id_inserted}"
addresses=$(echo ${addresses} | jq -r ".addresses[].address")
insert_watches "${addresses}" "${label}" "${cb0conf_url}" "${cb1conf_url}" "${id_inserted}" "${nstart}"
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -ne 0 ]; then
error_msg="Can't insert xpub watches in DB"
fi
else
error_msg="Can't insert xpub watcher in DB"
fi
else
error_msg="Can't import addresses"
fi
else
error_msg="Can't create wallet"
fi
else
error_msg="Can't derive addresses"
fi
if [ -z "${error_msg}" ]; then
data='{"id":'${id_inserted}','\
'"event":"watchxpub",'\
'"pub32":"'${pub32}'",'\
'"label":"'${label}'",'\
'"path":"'${path}'",'\
'"nstart":'${nstart}','\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}'}'
returncode=0
else
data='{"error":"'${error_msg}'",'\
'"event":"watchxpub",'\
'"pub32":"'${pub32}'",'
'"label":"'${label}'",'\
'"path":"'${path}'",'\
'"nstart":${nstart},'\
'"unconfirmedCallbackURL":'${cb0conf_url_json}','\
'"confirmedCallbackURL":'${cb1conf_url_json}'}'
returncode=1
fi
trace "[watchpub32] responding=${data}"
echo "${data}"
return ${returncode}
}
insert_watches() {
trace "Entering insert_watches()..."
# Expecting args without quotes
# When callback0conf and callback1conf are empty, means null
local addresses=${1}
local label=${2}
local label_pg
if [ -z "${label}" ]; then
# Empty url
label_pg="null"
else
label_pg="'${label}'"
fi
local callback0conf=${3}
local callback0conf_pg
if [ -z "${callback0conf}" ]; then
# Empty url
callback0conf_pg="null"
else
callback0conf_pg="'${callback0conf}'"
fi
local callback1conf=${4}
local callback1conf_pg
if [ -z "${callback1conf}" ]; then
# Empty url
callback1conf_pg="null"
else
callback1conf_pg="'${callback1conf}'"
fi
local xpub_id=${5}
local nstart=${6}
local inserted_values
local IFS=$'\n'
for address in ${addresses}
do
# (address, label, watching, callback0conf, callback1conf, imported, watching_by_pub32_id)
if [ -n "${inserted_values}" ]; then
inserted_values="${inserted_values},"
fi
inserted_values="${inserted_values}('${address}', ${label_pg}, true, ${callback0conf_pg}, ${callback1conf_pg}, true, ${xpub_id}, ${nstart})"
nstart=$((${nstart} + 1))
done
sql "INSERT INTO watching (address, label, watching, callback0conf, callback1conf, imported, watching_by_pub32_id, pub32_index)"\
" VALUES ${inserted_values}"\
" ON CONFLICT (address, COALESCE(callback0conf, ''), COALESCE(callback1conf, '')) DO"\
" UPDATE SET watching=true, calledback0conf=false, calledback1conf=false, label=${label_pg}"
returncode=$?
trace_rc ${returncode}
return ${returncode}
}
extend_watchers() {
trace "Entering extend_watchers()..."
# Expecting args without quotes
local watching_by_pub32_id=${1}
trace "[extend_watchers] watching_by_pub32_id=${watching_by_pub32_id}"
local pub32_index=${2}
trace "[extend_watchers] pub32_index=${pub32_index}"
local upgrade_to_n=$((${pub32_index} + ${XPUB_DERIVATION_GAP}))
trace "[extend_watchers] upgrade_to_n=${upgrade_to_n}"
local last_imported_n
local row
# row=$(sql "SELECT COALESCE('\"'||pub32||'\"', 'null'), COALESCE('\"'||label||'\"', 'null'), COALESCE('\"'||derivation_path||'\"', 'null'), COALESCE('\"'||callback0conf||'\"', 'null'), COALESCE('\"'||callback1conf||'\"', 'null'), last_imported_n FROM watching_by_pub32 WHERE id=${watching_by_pub32_id} AND watching")
row=$(sql "SELECT pub32, label, derivation_path, callback0conf, callback1conf, last_imported_n FROM watching_by_pub32 WHERE id=${watching_by_pub32_id} AND watching")
returncode=$?
trace_rc ${returncode}
trace "[extend_watchers] row=${row}"
local pub32=$(echo "${row}" | cut -d '|' -f1)
trace "[extend_watchers] pub32=${pub32}"
local label=$(echo "${row}" | cut -d '|' -f2)
trace "[extend_watchers] label=${label}"
local derivation_path=$(echo "${row}" | cut -d '|' -f3)
trace "[extend_watchers] derivation_path=${derivation_path}"
local callback0conf=$(echo "${row}" | cut -d '|' -f4)
trace "[extend_watchers] callback0conf=${callback0conf}"
local callback1conf=$(echo "${row}" | cut -d '|' -f5)
trace "[extend_watchers] callback1conf=${callback1conf}"
local last_imported_n=$(echo "${row}" | cut -d '|' -f6)
trace "[extend_watchers] last_imported_n=${last_imported_n}"
if [ "${last_imported_n}" -lt "${upgrade_to_n}" ]; then
# We want to keep our gap between last tx and last n watched...
# For example, if the last imported n is 155 and we just got a tx with pub32 index of 66,
# we want to extend the watched addresses to 166 if our gap is 100 (default).
trace "[extend_watchers] We have addresses to add to watchers!"
watchpub32 "${label}" "${pub32}" "${derivation_path}" "$((${last_imported_n} + 1))" "${callback0conf}" "${callback1conf}" "${upgrade_to_n}" > /dev/null
returncode=$?
trace_rc ${returncode}
else
trace "[extend_watchers] Nothing to add!"
fi
return ${returncode}
}
watchtxidrequest() {
trace "Entering watchtxidrequest()..."
local returncode
local result
local request=${1}
trace "[watchtxidrequest] request=${request}"
local txid txid_pg txid_pg_where
txid=$(echo "${request}" | jq -re ".txid")
if [ "$?" -ne "0" ]; then
# txid not found or null
result='{"result":null,'\
'"error":{'\
'"code":-5,'\
'"message":"txid required"}}'
trace "[watchrequest] txid required"
trace "[watchrequest] responding=${result}"
echo "${result}"
return 1
else
txid_pg="'${address}'"
fi
trace "[watchtxidrequest] txid=${txid}, txid_pg=${txid_pg}"
local cb1conf_url cb1conf_url_pg cb1conf_url_pg_where cb1conf_url_json
cb1conf_url=$(echo "${request}" | jq -re ".confirmedCallbackURL")
if [ "$?" -ne "0" ]; then
# cb1conf_url not found or null
cb1conf_url_json="null"
cb1conf_url_pg="null"
cb1conf_url_pg_where=" IS NULL"
else
cb1conf_url_json="\"${cb1conf_url}\""
cb1conf_url_pg="'${cb1conf_url}'"
cb1conf_url_pg_where="=${cb1conf_url_pg}"
fi
trace "[watchtxidrequest] cb1conf_url=${cb1conf_url}, cb1conf_url_pg=${cb1conf_url_pg}, cb1conf_url_pg_where=${cb1conf_url_pg_where}, cb1conf_url_json=${cb1conf_url_json}"
local cbxconf_url cbxconf_url_pg cbxconf_url_pg_where
cbxconf_url=$(echo "${request}" | jq -e ".xconfCallbackURL")
if [ "$?" -ne "0" ]; then
# cbxconf_url not found or null
cbxconf_url_json="null"
cbxconf_url_pg="null"
cbxconf_url_pg_where=" IS NULL"
else
cbxconf_url_json="\"${cbxconf_url}\""
cbxconf_url_pg="'${cbxconf_url}'"
cbxconf_url_pg_where="=${cbxconf_url_pg}"
fi
trace "[watchtxidrequest] cbxconf_url=${cbxconf_url}, cbxconf_url_pg=${cbxconf_url_pg}, cbxconf_url_pg_where=${cbxconf_url_pg_where}, cbxconf_url_json=${cbxconf_url_json}"
local nbxconf=$(echo "${request}" | jq ".nbxconf")
trace "[watchtxidrequest] nbxconf=${nbxconf}"
local cb1cond
local cbxcond
local inserted
local id_inserted
trace "[watchtxidrequest] Watch request on txid (${txid}), cb 1-conf (${cb1conf_url}) and cb x-conf (${cbxconf_url}) on ${nbxconf} confirmations."
id_inserted=$(sql "INSERT INTO watching_by_txid (txid, watching, callback1conf, callbackxconf, nbxconf)"\
" VALUES (${txid_pg}, true, ${cb1conf_url_pg}, ${cbxconf_url_pg}, ${nbxconf})"\
" ON CONFLICT (txid, COALESCE(callback1conf, ''), COALESCE(callbackxconf, '')) DO"\
" UPDATE SET watching=true, nbxconf=${nbxconf}, calledback1conf=false, calledbackxconf=false"\
" RETURNING id" \
"SELECT id FROM watching_by_txid WHERE txid=${txid_pg} AND callback1conf${cb1conf_url_pg_where} AND callbackxconf${cbxconf_url_pg_where}")
returncode=$?
trace_rc ${returncode}
if [ "${returncode}" -eq 0 ]; then
inserted=true
trace "[watchtxidrequest] id_inserted: ${id_inserted}"
else
inserted=false
id_inserted=null
fi
local data='{"id":'${id_inserted}','\
'"event":"watchtxid",'\
'"inserted":'${inserted}','\
'"txid":"'${txid}'",'\
'"confirmedCallbackURL":'${cb1conf_url_json}','\
'"xconfCallbackURL":'${cbxconf_url_json}','\
'"nbxconf":'${nbxconf}'}'
trace "[watchtxidrequest] responding=${data}"
echo "${data}"
return ${returncode}
}