Skip to content

Commit

Permalink
Fee Rates (#320)
Browse files Browse the repository at this point in the history
* Initial mempool feerate implementation

* Made conf target > priority conversion configurable

* feedback implementation

* test script

* fixed returncode, added feerate in batchspend

* Small adjustments

* Use proxy in getfeerate tests

* typo

* getfeerate testnet/regtest support and default conf_target

---------

Co-authored-by: kexkey <[email protected]>
  • Loading branch information
Talej and Kexkey authored Dec 3, 2024
1 parent 31cccc7 commit 9d63ff8
Show file tree
Hide file tree
Showing 11 changed files with 336 additions and 3 deletions.
1 change: 1 addition & 0 deletions cyphernodeconf_docker/templates/gatekeeper/api.properties
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ action_ln_listpays=watcher
action_ln_paystatus=watcher
action_bitcoin_estimatesmartfee=watcher
action_bitcoin_gettxoutproof=watcher
action_bitcoin_getfeerate=watcher
action_validateaddress=watcher

# Spender can do what the watcher can do, plus:
Expand Down
7 changes: 6 additions & 1 deletion cyphernodeconf_docker/templates/proxy/proxy.env
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,9 @@ WATCHER_BTC_NODE_PRUNED=<%= bitcoin_prune ? 'true' : 'false' %>
OTSCLIENT_CONTAINER=otsclient:6666
OTS_FILES=/proxy/otsfiles
XPUB_DERIVATION_GAP=100
PGPASSFILE=/proxy/db/pgpass
PGPASSFILE=/proxy/db/pgpass

CONFTARGET_PRIORITY_FASTEST=1
CONFTARGET_PRIORITY_HALFHOUR=3
CONFTARGET_PRIORITY_HOUR=6
CONFTARGET_PRIORITY_ECONOMY=59
22 changes: 22 additions & 0 deletions doc/API.v0.md
Original file line number Diff line number Diff line change
Expand Up @@ -1858,6 +1858,28 @@ Proxy response:
}
```

### Get an estimation of current Bitcoin fees based on mempool.space rates

This will retrieve a fee rate by converting confTarget to a fee priority and retrieving the current rate from mempool.space

```http
POST http://cyphernode:8888/bitcoin_getfeerate
with body...
{"confTarget":2}
```

Proxy response:

```json
{
"result": {
"feerate": 25,
},
"error": null,
"id": null
}
```

### Mine blocks immediately to a specified address (before the RPC call returns)

This will call the Bitcoin Core generatetoaddress RPC call and return the result as is.
Expand Down
45 changes: 45 additions & 0 deletions doc/openapi/v0/cyphernode-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1822,6 +1822,51 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/bitcoin_getfeerate:
post:
tags:
- "core features"
- "bitcoin"
summary: "Returns current fee estimation based on mempool.space rates"
description: "Returns current fee estimation based on mempool.space rates"
operationId: "getfeerate"
requestBody:
description: "Conf Target"
required: true
content:
application/json:
schema:
type: "object"
properties:
confTarget:
type: "number"
responses:
'200':
description: "operation successful"
content:
application/json:
schema:
type: "object"
properties:
result:
type: "object"
properties:
feerate:
type: "number"
error:
type: "object"
id:
type: "number"
'403':
$ref: '#/components/schemas/ApiResponseNotAllowed'
'405':
$ref: '#/components/schemas/ApiResponseInvalidInput'
'503':
description: "Resource temporarily unavailable"
content:
application/json:
schema:
$ref: '#/components/schemas/ApiResponseTemporarilyUnavailable'
/bitcoin_gettxoutproof:
post:
tags:
Expand Down
4 changes: 4 additions & 0 deletions proxy_docker/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ WATCHER_BTC_NODE_PRUNED=false
OTSCLIENT_CONTAINER=otsclient:6666
OTS_FILES=/proxy/otsfiles
XPUB_DERIVATION_GAP=100
CONFTARGET_PRIORITY_FASTEST=1
CONFTARGET_PRIORITY_HALFHOUR=3
CONFTARGET_PRIORITY_HOUR=6
CONFTARGET_PRIORITY_ECONOMY=59
```

## Create sqlite3 database path and give rights
Expand Down
12 changes: 11 additions & 1 deletion proxy_docker/app/script/batching.sh
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,17 @@ batchspend() {

local bitcoincore_args='{"method":"sendmany","params":["", {'${recipientsjson}'}'
if [ -n "${conf_target}" ]; then
bitcoincore_args="${bitcoincore_args}, 1, \"\", null, null, ${conf_target}"
local fee_rate
fee_rate=$(getfeerate "${conf_target}" | jq -r '.feerate')
returncode=$?
trace_rc ${returncode}
trace "[batchspend] fee_rate=${fee_rate}"
if [ "${returncode}" -eq 0 ]; then
# minconf, comment, subtractfeefrom, replaceable, conf_target, estimate_mode, fee_rate
bitcoincore_args="${bitcoincore_args}, 1, \"\", null, null, null, \"unset\", ${fee_rate}"
else
bitcoincore_args="${bitcoincore_args}, 1, \"\", null, null, ${conf_target}"
fi
fi
bitcoincore_args="${bitcoincore_args}]}"

Expand Down
112 changes: 112 additions & 0 deletions proxy_docker/app/script/bitcoin.sh
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,118 @@ derivepubpath_bitcoind() {
return $?
}

getfeeratefromurl() {
local url=$1
local priority=$2
local response
local returncode

trace "[getfeeratefromurl] url=${url}"
trace "[getfeeratefromurl] priority=${priority}"

response=$(curl -s -m 3 $url)
returncode=$?

if [ $returncode -ne 0 ] || [ -z "$response" ]; then
trace "[getfeeratefromurl] Failed to get response from $url"
return 1
fi

local feerate=$(echo $response | jq ".${priority}Fee")
if [ -z "$feerate" ]; then
trace "[getfeeratefromurl] Failed to extract $priority fee from response"
return 1
fi

echo $feerate
return 0
}

getpriorityfromconftarget() {
local conf_target=${1}
trace "[getpriorityfromconftarget] conf_target=${conf_target}"

# default to hour priority if no conf_target is provided
if [ -z "${conf_target}" ] || [ "${conf_target}" = "null" ]; then
conf_target=6
trace "[getpriorityfromconftarget] defaulted conf_target=${conf_target}"
fi

conftarget_fastest=$(($CONFTARGET_PRIORITY_FASTEST + 1))
conftarget_halfhour=$(($CONFTARGET_PRIORITY_HALFHOUR + 1))
conftarget_hour=$(($CONFTARGET_PRIORITY_HOUR + 1))
conftarget_economy=$(($CONFTARGET_PRIORITY_ECONOMY + 1))

local priority
if [ "$conf_target" -lt $conftarget_fastest ]; then
priority="fastest"
elif [ "$conf_target" -lt $conftarget_halfhour ]; then
priority="halfHour"
elif [ "$conf_target" -lt $conftarget_hour ]; then
priority="hour"
elif [ "$conf_target" -lt $conftarget_economy ]; then
priority="economy"
else
priority="hour"
fi

echo $priority
}

getfeerate() {
trace "Entering getfeerate()..."

local conf_target=${1}
trace "[getfeerate] conf_target=${conf_target}"
local priority=$(getpriorityfromconftarget "${conf_target}")
trace "[getfeerate] priority=${priority}"
local feerate

if [ "${BITCOIN_NETWORK}" = "mainnet" ]; then
feerate=$(getfeeratefromurl "https://mempool.bullbitcoin.com/api/v1/fees/recommended" "${priority}")
if [ -n "$feerate" ]; then
echo "{\"feerate\":\"${feerate}\"}"
return 0
fi

feerate=$(getfeeratefromurl "https://mempool.space/api/v1/fees/recommended" "${priority}")
if [ -n "$feerate" ]; then
echo "{\"feerate\":\"${feerate}\"}"
return 0
fi
elif [ "${BITCOIN_NETWORK}" = "testnet" ]; then
feerate=$(getfeeratefromurl "https://mempool.space/testnet/api/v1/fees/recommended" "${priority}")
if [ -n "$feerate" ]; then
echo "{\"feerate\":\"${feerate}\"}"
return 0
fi
fi

local response
local returncode
local data='{"method":"estimatesmartfee","params":['${conf_target}']}'
response=$(send_to_spender_node "${data}")
returncode=$?
trace_rc ${returncode}
trace "[getfeerate] response=${response}"

if [ "${returncode}" -eq 0 ]; then
feerate=$(echo ${response} | jq ".result.feerate")
feerate=$(awk "BEGIN { printf \"%d\", $feerate * 100000000 }")
trace "[getfeerate] feerate=${feerate}"

data="{\"feerate\":\"${feerate}\"}"
else
trace "[getfeerate] Faild to get feerate!"
data="{\"feerate\":\"0\"}"
fi

trace "[getfeerate] responding=${data}"
echo "${data}"

return ${returncode}
}

# xpub = P2PKH / P2SH = 1addr or 3addr = pkh()
# ypub = Segwit P2WPKH in P2SH = 3addr = sh(wpkh())
# Ypub = Segwit Multisig P2WSH in P2SH = 3addr
Expand Down
16 changes: 16 additions & 0 deletions proxy_docker/app/script/requesthandler.sh
Original file line number Diff line number Diff line change
Expand Up @@ -571,6 +571,22 @@ main() {
response=$(bitcoin_gettxoutproof "$(echo "${line}" | jq -r ".txids")" "$(echo ${line} | jq -r ".blockhash // empty")")
returncode=$?
;;
bitcoin_getfeerate)
# POST http://192.168.111.152:8080/bitcoin_getfeerate
# BODY {"confTarget":4}
#
# args:
# - confTarget the required confirmation target in blocks
#
# response:
# - feerate, the feerate in sat/vB
#
# BODY {"feerate":20.4}

local conf_target=$(echo "${line}" | jq -er ".confTarget // empty")
response=$(getfeerate "${conf_target}")
returncode=$?
;;
deriveindex)
# curl GET http://192.168.111.152:8080/deriveindex/25-30
# curl GET http://192.168.111.152:8080/deriveindex/34
Expand Down
5 changes: 4 additions & 1 deletion proxy_docker/app/script/walletoperations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,15 @@ spend() {
# Let's lowercase bech32 addresses
address=$(lowercase_if_bech32 "${address}")

local fee_rate=$(getfeerate "${conf_target}" | jq -r ".feerate")
trace "[spend] fee_rate=${fee_rate}"

local response
local id_inserted
local tx_details
local tx_raw_details

response=$(send_to_spender_node "{\"method\":\"sendtoaddress\",\"params\":[\"${address}\",${amount},\"\",\"\",${subtractfeefromamount},${replaceable},${conf_target}]}")
response=$(send_to_spender_node "{\"method\":\"sendtoaddress\",\"params\":[\"${address}\",${amount},\"\",\"\",${subtractfeefromamount},${replaceable},null,\"unset\",false,${fee_rate}]}")
local returncode=$?
trace_rc ${returncode}
trace "[spend] response=${response}"
Expand Down
Loading

0 comments on commit 9d63ff8

Please sign in to comment.