mirror of
https://github.com/aljazceru/cyphernode.git
synced 2025-12-17 04:35:14 +01:00
212 lines
8.8 KiB
Bash
212 lines
8.8 KiB
Bash
#!/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() {
|
|
(
|
|
flock -x 201
|
|
|
|
trace "Entering confirmation()..."
|
|
|
|
local returncode
|
|
local txid=${1}
|
|
local bypass_callbacks=${2}
|
|
trace "[confirmation] bypass_callbacks=${bypass_callbacks}"
|
|
local tx_details
|
|
tx_details="$(get_transaction ${txid})"
|
|
returncode=$?
|
|
trace_rc ${returncode}
|
|
trace "[confirmation] tx_details=${tx_details}"
|
|
if [ "${returncode}" -ne "0" ]; then
|
|
trace "[confirmation] Transaction not in watcher, exiting."
|
|
return 0
|
|
fi
|
|
########################################################################################################
|
|
# First of all, let's make sure we're working on watched addresses...
|
|
local address
|
|
local addresseswhere
|
|
local addresses=$(echo "${tx_details}" | jq -r ".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, watching_by_pub32_id, pub32_index, event_message FROM watching WHERE address IN (${addresseswhere}) AND watching")
|
|
if [ ${#rows} -eq 0 ]; then
|
|
trace "[confirmation] No watched address in this tx!"
|
|
return 0
|
|
fi
|
|
########################################################################################################
|
|
|
|
local tx=$(sql "SELECT id FROM tx WHERE txid='${txid}'")
|
|
local id_inserted
|
|
local tx_raw_details=$(get_rawtransaction ${txid} | tr -d '\n')
|
|
local tx_nb_conf=$(echo "${tx_details}" | jq -r '.result.confirmations // 0')
|
|
local tx_hash=$(echo "${tx_raw_details}" | jq -r '.result.hash')
|
|
|
|
# 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_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 -r '.result."bip125-replaceable"')
|
|
tx_replaceable=$([ ${tx_replaceable} = "yes" ] && echo "true" || echo "false")
|
|
|
|
local fees=$(compute_fees "${txid}")
|
|
trace "[confirmation] fees=${fees}"
|
|
|
|
# If we missed 0-conf...
|
|
local tx_blockhash=null
|
|
local tx_blockheight=null
|
|
local tx_blocktime=null
|
|
if [ "${tx_nb_conf}" -gt "0" ]; then
|
|
trace "[confirmation] tx_nb_conf=${tx_nb_conf}"
|
|
tx_blockhash="$(echo "${tx_details}" | jq -r '.result.blockhash')"
|
|
tx_blockheight=$(get_block_info ${tx_blockhash} | jq '.result.height')
|
|
tx_blockhash="'${tx_blockhash}'"
|
|
tx_blocktime=$(echo "${tx_details}" | jq '.result.blocktime')
|
|
fi
|
|
|
|
id_inserted=$(sql "INSERT INTO tx (txid, hash, confirmations, timereceived, fee, size, vsize, is_replaceable, blockhash, blockheight, blocktime)"\
|
|
" VALUES ('${txid}', '${tx_hash}', ${tx_nb_conf}, ${tx_ts_firstseen}, ${fees}, ${tx_size}, ${tx_vsize}, ${tx_replaceable}, ${tx_blockhash}, ${tx_blockheight}, ${tx_blocktime})"\
|
|
" ON CONFLICT (txid) DO"\
|
|
" UPDATE SET blockhash=${tx_blockhash}, blockheight=${tx_blockheight}, blocktime=${tx_blocktime}, confirmations=${tx_nb_conf}"\
|
|
" RETURNING id" \
|
|
"SELECT id FROM tx WHERE txid='${txid}'")
|
|
trace_rc $?
|
|
|
|
else
|
|
# TX found in our DB.
|
|
# 1-conf or executecallbacks on an unconfirmed tx or spending watched address (in this case, we probably missed conf) or spending to a watched address (in this case, spend inserted the tx in the DB)
|
|
|
|
local tx_blockhash=$(echo "${tx_details}" | jq -r '.result.blockhash')
|
|
trace "[confirmation] tx_blockhash=${tx_blockhash}"
|
|
if [ "${tx_blockhash}" = "null" ]; then
|
|
trace "[confirmation] probably being called by executecallbacks without any confirmations since the last time we checked"
|
|
else
|
|
local tx_blockheight=$(get_block_info "${tx_blockhash}" | jq '.result.height')
|
|
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} WHERE txid='${txid}'"
|
|
trace_rc $?
|
|
fi
|
|
id_inserted=${tx}
|
|
fi
|
|
# Delete the temp file containing the raw tx (see above)
|
|
rm rawtx-${txid}-$$.blob
|
|
|
|
########################################################################################################
|
|
|
|
local event_message
|
|
local watching_id
|
|
|
|
# Let's see if we need to insert tx in the join table
|
|
tx=$(sql "SELECT tx_id FROM watching_tx WHERE tx_id=${id_inserted}")
|
|
|
|
for row in ${rows}
|
|
do
|
|
|
|
address=$(echo "${row}" | cut -d '|' -f2)
|
|
tx_vout_amount=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .amount | fabs" | awk '{ printf "%.8f", $0 }')
|
|
# In the case of us spending to a watched address, the address appears twice in the details,
|
|
# once on the spend side (negative amount) and once on the receiving side (positive amount)
|
|
tx_vout_n=$(echo "${tx_details}" | jq ".result.details | map(select(.address==\"${address}\"))[0] | .vout")
|
|
|
|
########################################################################################################
|
|
# Let's now insert in the join table if not already done
|
|
if [ -z "${tx}" ]; then
|
|
trace "[confirmation] For this tx, there's no watching_tx row, let's create it"
|
|
|
|
# If the tx is batched and pays multiple watched addresses, we have to insert
|
|
# those additional addresses in watching_tx!
|
|
watching_id=$(echo "${row}" | cut -d '|' -f1)
|
|
sql "INSERT INTO watching_tx (watching_id, tx_id, vout, amount) VALUES (${watching_id}, ${id_inserted}, ${tx_vout_n}, ${tx_vout_amount})"\
|
|
" ON CONFLICT DO NOTHING"
|
|
trace_rc $?
|
|
else
|
|
trace "[confirmation] For this tx, there's already watching_tx rows"
|
|
fi
|
|
########################################################################################################
|
|
|
|
########################################################################################################
|
|
# Let's now grow the watch window in the case of a xpub watcher...
|
|
watching_by_pub32_id=$(echo "${row}" | cut -d '|' -f3)
|
|
if [ -n "${watching_by_pub32_id}" ]; then
|
|
trace "[confirmation] Let's now grow the watch window in the case of a xpub watcher"
|
|
|
|
pub32_index=$(echo "${row}" | cut -d '|' -f4)
|
|
extend_watchers ${watching_by_pub32_id} ${pub32_index}
|
|
fi
|
|
########################################################################################################
|
|
|
|
########################################################################################################
|
|
# Let's publish the event if needed
|
|
event_message=$(echo "${row}" | cut -d '|' -f5)
|
|
if [ -n "${event_message}" ]; then
|
|
# There's an event message, let's publish it!
|
|
|
|
trace "[confirmation] mosquitto_pub -h broker -t tx_confirmation -m \"{\"txid\":\"${txid}\",\"hash\":${tx_hash},\"address\":\"${address}\",\"vout_n\":${tx_vout_n},\"amount\":${tx_vout_amount},\"confirmations\":${tx_nb_conf},\"eventMessage\":\"${event_message}\"}\""
|
|
response=$(mosquitto_pub -h broker -t tx_confirmation -m "{\"txid\":\"${txid}\",\"hash\":${tx_hash},\"address\":\"${address}\",\"vout_n\":${tx_vout_n},\"amount\":${tx_vout_amount},\"confirmations\":${tx_nb_conf},\"eventMessage\":\"${event_message}\"}")
|
|
returncode=$?
|
|
trace_rc ${returncode}
|
|
fi
|
|
########################################################################################################
|
|
|
|
done
|
|
|
|
) 201>./.confirmation.lock
|
|
|
|
# There's a lock in callbacks, let's get out of the confirmation lock before entering another one
|
|
# If this was called by missed_conf algo, we don't want to process all the callbacks now. We wait
|
|
# for next cron.
|
|
if [ -z "${bypass_callbacks}" ]; then
|
|
trace "[confirmation] Let's do the callbacks!"
|
|
do_callbacks "${txid}"
|
|
fi
|
|
|
|
echo '{"result":"confirmed"}'
|
|
|
|
return 0
|
|
}
|
|
|
|
case "${0}" in *confirmation.sh) confirmation $@;; esac
|