Skip to content

Commit

Permalink
Multisig monitor view
Browse files Browse the repository at this point in the history
  • Loading branch information
koirikivi committed May 14, 2024
1 parent 3323fa6 commit 31bbc38
Show file tree
Hide file tree
Showing 4 changed files with 161 additions and 3 deletions.
67 changes: 67 additions & 0 deletions bridge_node/bridge/api/monitor/templates/monitor/multisig.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
{% extends "monitor/base.jinja2" %}
{% block content %}
<h2>Multisig</h2>
<table class="table">
<tr>
<th>Change address</th>
<td><code>{{ change_address }}</code></td>
</tr>
<tr>
<th>Cardinal balance (BTC)</th>
<td><code>{{ cardinal_balance_btc }}</code></td>
</tr>
<tr>
<th>Runic balance (BTC)</th>
<td><code>{{ runic_balance_btc }}</code></td>
</tr>
</table>

<h3>Rune balances</h3>
<table>
{% for rune_name, amount_raw in rune_balances.items() %}
<tr>
<th>{{ rune_name }}</th>
<td>{{ format_raw_rune_amount(rune_name, amount_raw) }}</td>
</tr>
{% endfor %}
</table>

<h3>UTXOs</h3>
<table class="table">
<tr>
<th>txid:vout</th>
<th>address</th>
<th>amount sat</th>
<th>confirmations</th>
<th>rune balances</th>
</tr>
{% for utxo, ord_output in utxos_with_ord_outputs %}
<tr>
<td>{{ utxo.txid }}:{{ utxo.vout }}</td>
<td>
{{ utxo.address }}
{% if utxo.address == change_address %}
<small>(change)</small>
{% endif %}
</td>
<td>{{ utxo.amount_satoshi }}</td>
<td>{{ utxo.confirmations }}</td>
<th>
{% if not ord_output %}
(no ord output)
{% elif not ord_output.rune_balances %}
-
{% else %}
<table>
{% for rune_name, amount in ord_output.rune_balances.items() %}
<tr>
<th>{{ rune_name }}</th>
<td>{{ format_raw_rune_amount(rune_name, amount_raw) }}</td>
</tr>
{% endfor %}
</table>
{% endif %}
</tr>
{% endfor %}
<table>
{% endblock %}
5 changes: 5 additions & 0 deletions bridge_node/bridge/api/monitor/templates/monitor/users.jinja2
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{% extends "monitor/base.jinja2" %}
{% block content %}
<h2>Users</h2>
<p>TODO</p>
{% endblock %}
71 changes: 71 additions & 0 deletions bridge_node/bridge/api/monitor/views.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from decimal import Decimal

from anemic.ioc import (
auto,
Expand All @@ -17,13 +18,17 @@
RuneDeposit,
RuneTokenDeposit,
)
from ...bridges.runes.service import RuneBridgeService
from ..exceptions import ApiException

logger = logging.getLogger(__name__)


class MonitorViews:
request: Request
dbsession: Session = autowired(auto)
runesrsk_service: RuneBridgeService = autowired(RuneBridgeService, name="runesrsk-service")
runesbob_service: RuneBridgeService = autowired(RuneBridgeService, name="runesbob-service")

def __init__(self, request):
self.request = request
Expand All @@ -33,6 +38,7 @@ def __init__(self, request):
if not bridge:
raise HTTPNotFound("bridge not found")
self.bridge_id = bridge.id
self.bridge_name = bridge_name

@view_config(
route_name="monitor_deposits",
Expand Down Expand Up @@ -98,8 +104,73 @@ def runes(self):
"runes": runes,
}

@view_config(
route_name="monitor_users",
request_method="GET",
renderer="templates/monitor/users.jinja2",
)
def users(self):
pass

@view_config(
route_name="monitor_multisig",
request_method="GET",
renderer="templates/monitor/multisig.jinja2",
)
def multisig(self):
service = self._get_runebridge_service()
ord_client = service.ord_client
multisig = service.ord_multisig
utxos_with_ord_outputs = multisig.list_utxos_with_ord_outputs()
rune_balances = dict()
runic_balance_sat = 0
cardinal_balance_sat = 0
unindexed_balance_sat = 0
for utxo, ord_output in utxos_with_ord_outputs:
if not ord_output:
unindexed_balance_sat += utxo.amount_satoshi
continue
if ord_output.has_rune_balances():
for rune_name, amount in ord_output.rune_balances.items():
if rune_name not in rune_balances:
rune_balances[rune_name] = 0
rune_balances[rune_name] += amount

runic_balance_sat += utxo.amount_satoshi
else:
cardinal_balance_sat += utxo.amount_satoshi

rune_entries = {}
for rune in rune_balances.keys():
rune_response = ord_client.get_rune(rune)
rune_entries[rune] = rune_response["entry"]

def format_raw_rune_amount(rune: str, amount_raw: int) -> Decimal:
return Decimal(amount_raw) / rune_entries[rune]["divisibility"]

return {
"change_address": multisig.change_address,
"utxos_with_ord_outputs": utxos_with_ord_outputs,
"rune_balances": rune_balances,
"runic_balance_btc": Decimal(runic_balance_sat) / 10**8,
"cardinal_balance_btc": Decimal(cardinal_balance_sat) / 10**8,
"unindexed_balance_btc": Decimal(unindexed_balance_sat) / 10**8,
"rune_entries": rune_entries,
"format_raw_rune_amount": format_raw_rune_amount,
}

def _get_runebridge_service(self) -> RuneBridgeService:
if self.bridge_name == "runesrsk":
return self.runesrsk_service
elif self.bridge_name == "runesbob":
return self.runesbob_service
else:
raise ApiException(f"Invalid bridge name: {self.bridge_name}")


def includeme(config: Configurator):
config.add_jinja2_search_path("/srv/bridge_backend/templates")
config.add_route("monitor_deposits", "/:bridge/deposits")
config.add_route("monitor_runes", "/:bridge/runes")
config.add_route("monitor_users", "/:bridge/users")
config.add_route("monitor_multisig", "/:bridge/multisig")
21 changes: 18 additions & 3 deletions bridge_node/bridge/common/ord/multisig.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
ZeroTransferAmountError,
)
from .utxos import (
OrdOutput,
OrdOutputCache,
UnindexedOutput,
)
Expand Down Expand Up @@ -192,7 +193,7 @@ def get_rune_balance(
*,
wait_for_indexing: bool = True,
) -> int:
utxos = self._list_utxos()
utxos = self.list_utxos()
ret = 0
for utxo in utxos:
for i in range(20):
Expand Down Expand Up @@ -319,7 +320,7 @@ def create_rune_psbt(
)

# Coin selection
utxos = self._list_utxos()
utxos = self.list_utxos()
used_runes = tuple(required_rune_amounts.keys())
required_rune_amounts = dict(required_rune_amounts) # no defaultdict anymore
input_amount_sat = 0
Expand Down Expand Up @@ -555,9 +556,23 @@ def _derive_redeem_script(self, index: int) -> CScript:
pubkeys=sorted_child_pubkeys,
)

def _list_utxos(self) -> list[UTXO]:
def list_utxos(self) -> list[UTXO]:
# Don't filter by address here as we want all UTXOs
raw_utxos = self._bitcoin_rpc.listunspent(1, 9999999, [], False)
utxos = [UTXO.from_rpc_response(utxo) for utxo in raw_utxos]
utxos.sort(key=lambda utxo: utxo.confirmations, reverse=True)
return utxos

def list_utxos_with_ord_outputs(self) -> list[tuple[UTXO, OrdOutput | None]]:
ret = []
utxos = self.list_utxos()
for utxo in utxos:
try:
ord_output = self._ord_output_cache.get_ord_output(
txid=utxo.txid,
vout=utxo.vout,
)
except UnindexedOutput:
ord_output = None
ret.append((utxo, ord_output))
return ret

0 comments on commit 31bbc38

Please sign in to comment.