Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions node/bridge_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,7 @@ def get_bridge_transfer_by_hash(
"dest_chain": row[3],
"source_address": row[4],
"dest_address": row[5],
"amount_i64": row[6],
"amount_rtc": row[7],
"bridge_type": row[8],
"external_tx_hash": row[10],
Expand Down Expand Up @@ -640,6 +641,7 @@ def update_external_confirmation(
completed_at = None

try:
cursor.execute("BEGIN IMMEDIATE")
cursor.execute("""
UPDATE bridge_transfers
SET external_tx_hash = ?,
Expand All @@ -649,7 +651,21 @@ def update_external_confirmation(
completed_at = ?,
updated_at = ?
WHERE tx_hash = ?
AND status IN ('pending', 'locked', 'confirming')
""", (external_tx_hash, confirmations, req_conf, new_status, completed_at, now, tx_hash))

if cursor.rowcount != 1:
current = cursor.execute(
"SELECT status FROM bridge_transfers WHERE tx_hash = ?",
(tx_hash,),
).fetchone()
db_conn.rollback()
if not current:
return False, {"error": "Bridge transfer not found"}
return False, {
"error": "Cannot update completed/failed/voided transfer",
"current_status": current[0],
}

# If completed, release the lock
if new_status == "completed":
Expand All @@ -661,6 +677,15 @@ def update_external_confirmation(
WHERE bridge_transfer_id = ?
AND status = 'locked'
""", (now, external_tx_hash, transfer["id"]))
if transfer["direction"] == "withdraw":
cursor.execute(
"INSERT OR IGNORE INTO balances (miner_id, amount_i64) VALUES (?, 0)",
(transfer["dest_address"],),
)
cursor.execute(
"UPDATE balances SET amount_i64 = amount_i64 + ? WHERE miner_id = ?",
(transfer["amount_i64"], transfer["dest_address"]),
)

db_conn.commit()

Expand Down
115 changes: 115 additions & 0 deletions node/test_bridge_withdraw_completion_credits_destination_poc.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# SPDX-License-Identifier: MIT

import os
import sqlite3
import tempfile
import unittest

import bridge_api
from bridge_api import (
BRIDGE_UNIT,
BridgeTransferRequest,
create_bridge_transfer,
init_bridge_schema,
update_external_confirmation,
)
from lock_ledger import init_lock_ledger_schema


class TestBridgeWithdrawCompletionCreditsDestination(unittest.TestCase):
def setUp(self):
self.tmp = tempfile.NamedTemporaryFile(suffix=".db", delete=False)
self.tmp.close()
self.db_path = self.tmp.name
self.conn = sqlite3.connect(self.db_path)
self.conn.execute(
"CREATE TABLE balances (miner_id TEXT PRIMARY KEY, amount_i64 INTEGER DEFAULT 0)"
)
init_bridge_schema(self.conn.cursor())
init_lock_ledger_schema(self.conn.cursor())
self.conn.commit()

def tearDown(self):
self.conn.close()
os.unlink(self.db_path)

def test_completed_external_to_rustchain_withdraw_credits_destination(self):
request = BridgeTransferRequest(
direction="withdraw",
source_chain="solana",
dest_chain="rustchain",
source_address="A" * 32,
dest_address="RTCdest1234",
amount_rtc=10.0,
)

ok, result = create_bridge_transfer(self.conn, request, admin_initiated=False)
self.assertTrue(ok, result)

ok, result = update_external_confirmation(
self.conn,
result["tx_hash"],
external_tx_hash="solana_tx_123",
confirmations=12,
required_confirmations=12,
)
self.assertTrue(ok, result)
self.assertEqual(result["status"], "completed")

balance_i64 = self.conn.execute(
"SELECT amount_i64 FROM balances WHERE miner_id = ?",
("RTCdest1234",),
).fetchone()[0]
self.assertEqual(balance_i64, 10 * BRIDGE_UNIT)

def test_stale_completed_callback_does_not_credit_destination_twice(self):
request = BridgeTransferRequest(
direction="withdraw",
source_chain="solana",
dest_chain="rustchain",
source_address="B" * 32,
dest_address="RTCdest5678",
amount_rtc=10.0,
)

ok, result = create_bridge_transfer(self.conn, request, admin_initiated=False)
self.assertTrue(ok, result)

stale_transfer = bridge_api.get_bridge_transfer_by_hash(self.conn, result["tx_hash"])

ok, first_result = update_external_confirmation(
self.conn,
result["tx_hash"],
external_tx_hash="solana_tx_456",
confirmations=12,
required_confirmations=12,
)
self.assertTrue(ok, first_result)
self.assertEqual(first_result["status"], "completed")

original_get_bridge_transfer_by_hash = bridge_api.get_bridge_transfer_by_hash
try:
bridge_api.get_bridge_transfer_by_hash = (
lambda conn, tx_hash: stale_transfer
if tx_hash == result["tx_hash"]
else original_get_bridge_transfer_by_hash(conn, tx_hash)
)
update_external_confirmation(
self.conn,
result["tx_hash"],
external_tx_hash="solana_tx_456",
confirmations=12,
required_confirmations=12,
)
finally:
bridge_api.get_bridge_transfer_by_hash = original_get_bridge_transfer_by_hash

balance_i64 = self.conn.execute(
"SELECT amount_i64 FROM balances WHERE miner_id = ?",
("RTCdest5678",),
).fetchone()[0]
self.assertEqual(balance_i64, 10 * BRIDGE_UNIT)


if __name__ == "__main__":
unittest.main()
Loading