Host-customized fork of https://github.com/tecnovert/basicswap/
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
516 lines
16 KiB
516 lines
16 KiB
# -*- coding: utf-8 -*- |
|
|
|
# Copyright (c) 2019-2023 tecnovert |
|
# Distributed under the MIT software license, see the accompanying |
|
# file LICENSE or http://www.opensource.org/licenses/mit-license.php. |
|
|
|
import time |
|
import struct |
|
import sqlalchemy as sa |
|
|
|
from enum import IntEnum, auto |
|
from sqlalchemy.ext.declarative import declarative_base |
|
|
|
|
|
CURRENT_DB_VERSION = 22 |
|
CURRENT_DB_DATA_VERSION = 4 |
|
Base = declarative_base() |
|
|
|
|
|
class Concepts(IntEnum): |
|
OFFER = auto() |
|
BID = auto() |
|
NETWORK_MESSAGE = auto() |
|
AUTOMATION = auto() |
|
|
|
|
|
def strConcepts(state): |
|
if state == Concepts.OFFER: |
|
return 'Offer' |
|
if state == Concepts.BID: |
|
return 'Bid' |
|
if state == Concepts.NETWORK_MESSAGE: |
|
return 'Network Message' |
|
return 'Unknown' |
|
|
|
|
|
class DBKVInt(Base): |
|
__tablename__ = 'kv_int' |
|
|
|
key = sa.Column(sa.String, primary_key=True) |
|
value = sa.Column(sa.Integer) |
|
|
|
|
|
class DBKVString(Base): |
|
__tablename__ = 'kv_string' |
|
|
|
key = sa.Column(sa.String, primary_key=True) |
|
value = sa.Column(sa.String) |
|
|
|
|
|
class Offer(Base): |
|
__tablename__ = 'offers' |
|
|
|
offer_id = sa.Column(sa.LargeBinary, primary_key=True) |
|
active_ind = sa.Column(sa.Integer) |
|
|
|
protocol_version = sa.Column(sa.Integer) |
|
coin_from = sa.Column(sa.Integer) |
|
coin_to = sa.Column(sa.Integer) |
|
amount_from = sa.Column(sa.BigInteger) |
|
rate = sa.Column(sa.BigInteger) |
|
min_bid_amount = sa.Column(sa.BigInteger) |
|
time_valid = sa.Column(sa.BigInteger) |
|
lock_type = sa.Column(sa.Integer) |
|
lock_value = sa.Column(sa.Integer) |
|
swap_type = sa.Column(sa.Integer) |
|
|
|
proof_address = sa.Column(sa.String) |
|
proof_signature = sa.Column(sa.LargeBinary) |
|
proof_utxos = sa.Column(sa.LargeBinary) |
|
pkhash_seller = sa.Column(sa.LargeBinary) |
|
secret_hash = sa.Column(sa.LargeBinary) |
|
|
|
addr_from = sa.Column(sa.String) |
|
addr_to = sa.Column(sa.String) |
|
created_at = sa.Column(sa.BigInteger) |
|
expire_at = sa.Column(sa.BigInteger) |
|
was_sent = sa.Column(sa.Boolean) # Sent by node |
|
|
|
from_feerate = sa.Column(sa.BigInteger) |
|
to_feerate = sa.Column(sa.BigInteger) |
|
|
|
amount_negotiable = sa.Column(sa.Boolean) |
|
rate_negotiable = sa.Column(sa.Boolean) |
|
|
|
# Local fields |
|
auto_accept_bids = sa.Column(sa.Boolean) |
|
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO |
|
security_token = sa.Column(sa.LargeBinary) |
|
bid_reversed = sa.Column(sa.Boolean) |
|
|
|
state = sa.Column(sa.Integer) |
|
states = sa.Column(sa.LargeBinary) # Packed states and times |
|
|
|
def setState(self, new_state): |
|
now = int(time.time()) |
|
self.state = new_state |
|
if self.states is None: |
|
self.states = struct.pack('<iq', new_state, now) |
|
else: |
|
self.states += struct.pack('<iq', new_state, now) |
|
|
|
|
|
class Bid(Base): |
|
__tablename__ = 'bids' |
|
|
|
bid_id = sa.Column(sa.LargeBinary, primary_key=True) |
|
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey('offers.offer_id')) |
|
active_ind = sa.Column(sa.Integer) |
|
|
|
protocol_version = sa.Column(sa.Integer) |
|
was_sent = sa.Column(sa.Boolean) # Sent by node |
|
was_received = sa.Column(sa.Boolean) |
|
contract_count = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
expire_at = sa.Column(sa.BigInteger) |
|
bid_addr = sa.Column(sa.String) |
|
proof_address = sa.Column(sa.String) |
|
proof_utxos = sa.Column(sa.LargeBinary) |
|
withdraw_to_addr = sa.Column(sa.String) # Address to spend lock tx to - address from wallet if empty TODO |
|
|
|
recovered_secret = sa.Column(sa.LargeBinary) |
|
amount_to = sa.Column(sa.BigInteger) # amount * offer.rate |
|
|
|
pkhash_buyer = sa.Column(sa.LargeBinary) |
|
amount = sa.Column(sa.BigInteger) |
|
rate = sa.Column(sa.BigInteger) |
|
|
|
pkhash_seller = sa.Column(sa.LargeBinary) |
|
|
|
initiate_txn_redeem = sa.Column(sa.LargeBinary) |
|
initiate_txn_refund = sa.Column(sa.LargeBinary) |
|
|
|
participate_txn_redeem = sa.Column(sa.LargeBinary) |
|
participate_txn_refund = sa.Column(sa.LargeBinary) |
|
|
|
in_progress = sa.Column(sa.Integer) |
|
state = sa.Column(sa.Integer) |
|
state_time = sa.Column(sa.BigInteger) # Timestamp of last state change |
|
states = sa.Column(sa.LargeBinary) # Packed states and times |
|
|
|
state_note = sa.Column(sa.String) |
|
|
|
debug_ind = sa.Column(sa.Integer) |
|
security_token = sa.Column(sa.LargeBinary) |
|
|
|
chain_a_height_start = sa.Column(sa.Integer) # Height of script chain before the swap |
|
chain_b_height_start = sa.Column(sa.Integer) # Height of scriptless chain before the swap |
|
|
|
reject_code = sa.Column(sa.Integer) |
|
|
|
initiate_tx = None |
|
participate_tx = None |
|
xmr_a_lock_tx = None |
|
xmr_a_lock_spend_tx = None |
|
xmr_b_lock_tx = None # TODO: Can't move to txns due to error: Exception UPDATE statement on table expected to update 1 row(s); 0 were matched |
|
|
|
txns = {} |
|
|
|
def getITxState(self): |
|
if self.initiate_tx is None: |
|
return None |
|
return self.initiate_tx.state |
|
|
|
def setITxState(self, new_state): |
|
if self.initiate_tx is not None: |
|
self.initiate_tx.setState(new_state) |
|
|
|
def getPTxState(self): |
|
if self.participate_tx is None: |
|
return None |
|
return self.participate_tx.state |
|
|
|
def setPTxState(self, new_state): |
|
if self.participate_tx is not None: |
|
self.participate_tx.setState(new_state) |
|
|
|
def setState(self, new_state, state_note=None): |
|
now = int(time.time()) |
|
self.state = new_state |
|
self.state_time = now |
|
|
|
if state_note is not None: |
|
self.state_note = state_note |
|
if self.states is None: |
|
self.states = struct.pack('<iq', new_state, now) |
|
else: |
|
self.states += struct.pack('<iq', new_state, now) |
|
|
|
|
|
class SwapTx(Base): |
|
__tablename__ = 'transactions' |
|
|
|
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey('bids.bid_id')) |
|
tx_type = sa.Column(sa.Integer) # TxTypes |
|
__table_args__ = ( |
|
sa.PrimaryKeyConstraint('bid_id', 'tx_type'), |
|
{}, |
|
) |
|
|
|
txid = sa.Column(sa.LargeBinary) |
|
vout = sa.Column(sa.Integer) |
|
tx_data = sa.Column(sa.LargeBinary) |
|
|
|
script = sa.Column(sa.LargeBinary) |
|
|
|
tx_fee = sa.Column(sa.BigInteger) |
|
chain_height = sa.Column(sa.Integer) |
|
conf = sa.Column(sa.Integer) |
|
|
|
spend_txid = sa.Column(sa.LargeBinary) |
|
spend_n = sa.Column(sa.Integer) |
|
|
|
block_hash = sa.Column(sa.LargeBinary) |
|
block_height = sa.Column(sa.Integer) |
|
block_time = sa.Column(sa.BigInteger) |
|
|
|
state = sa.Column(sa.Integer) |
|
states = sa.Column(sa.LargeBinary) # Packed states and times |
|
|
|
def setState(self, new_state): |
|
if self.state == new_state: |
|
return |
|
self.state = new_state |
|
self.states = (self.states if self.states is not None else bytes()) + struct.pack('<iq', new_state, int(time.time())) |
|
|
|
|
|
class PrefundedTx(Base): |
|
__tablename__ = 'prefunded_transactions' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
linked_type = sa.Column(sa.Integer) |
|
linked_id = sa.Column(sa.LargeBinary) |
|
tx_type = sa.Column(sa.Integer) # TxTypes |
|
tx_data = sa.Column(sa.LargeBinary) |
|
used_by = sa.Column(sa.LargeBinary) |
|
|
|
|
|
class PooledAddress(Base): |
|
__tablename__ = 'addresspool' |
|
|
|
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
coin_type = sa.Column(sa.Integer) |
|
addr = sa.Column(sa.String) |
|
bid_id = sa.Column(sa.LargeBinary) |
|
tx_type = sa.Column(sa.Integer) |
|
|
|
|
|
class SentOffer(Base): |
|
__tablename__ = 'sentoffers' |
|
|
|
offer_id = sa.Column(sa.LargeBinary, primary_key=True) |
|
|
|
|
|
class SmsgAddress(Base): |
|
__tablename__ = 'smsgaddresses' |
|
|
|
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
addr = sa.Column(sa.String, unique=True) |
|
pubkey = sa.Column(sa.String) |
|
use_type = sa.Column(sa.Integer) |
|
note = sa.Column(sa.String) |
|
|
|
|
|
class Action(Base): |
|
__tablename__ = 'actions' |
|
|
|
action_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
trigger_at = sa.Column(sa.BigInteger) |
|
linked_id = sa.Column(sa.LargeBinary) |
|
action_type = sa.Column(sa.Integer) |
|
action_data = sa.Column(sa.LargeBinary) |
|
|
|
|
|
class EventLog(Base): |
|
__tablename__ = 'eventlog' |
|
|
|
event_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
linked_type = sa.Column(sa.Integer) |
|
linked_id = sa.Column(sa.LargeBinary) |
|
event_type = sa.Column(sa.Integer) |
|
event_msg = sa.Column(sa.String) |
|
|
|
__table_args__ = (sa.Index('main_index', 'linked_type', 'linked_id'), ) |
|
|
|
|
|
class XmrOffer(Base): |
|
__tablename__ = 'xmr_offers' |
|
# TODO: Merge to Offer |
|
|
|
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
offer_id = sa.Column(sa.LargeBinary, sa.ForeignKey('offers.offer_id')) |
|
|
|
a_fee_rate = sa.Column(sa.BigInteger) # Chain a fee rate |
|
b_fee_rate = sa.Column(sa.BigInteger) # Chain b fee rate |
|
|
|
lock_time_1 = sa.Column(sa.Integer) # Delay before the chain a lock refund tx can be mined |
|
lock_time_2 = sa.Column(sa.Integer) # Delay before the follower can spend from the chain a lock refund tx |
|
|
|
|
|
class XmrSwap(Base): |
|
__tablename__ = 'xmr_swaps' |
|
|
|
swap_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
bid_id = sa.Column(sa.LargeBinary, sa.ForeignKey('bids.bid_id')) |
|
|
|
contract_count = sa.Column(sa.Integer) |
|
|
|
dest_af = sa.Column(sa.LargeBinary) # Destination for coin A amount to follower when swap completes successfully |
|
|
|
pkal = sa.Column(sa.LargeBinary) |
|
pkasl = sa.Column(sa.LargeBinary) |
|
|
|
pkaf = sa.Column(sa.LargeBinary) |
|
pkasf = sa.Column(sa.LargeBinary) |
|
|
|
vkbvl = sa.Column(sa.LargeBinary) |
|
vkbsl = sa.Column(sa.LargeBinary) |
|
pkbvl = sa.Column(sa.LargeBinary) |
|
pkbsl = sa.Column(sa.LargeBinary) |
|
|
|
vkbvf = sa.Column(sa.LargeBinary) |
|
vkbsf = sa.Column(sa.LargeBinary) |
|
pkbvf = sa.Column(sa.LargeBinary) |
|
pkbsf = sa.Column(sa.LargeBinary) |
|
|
|
kbsl_dleag = sa.Column(sa.LargeBinary) |
|
kbsf_dleag = sa.Column(sa.LargeBinary) |
|
|
|
vkbv = sa.Column(sa.LargeBinary) # chain b view private key |
|
pkbv = sa.Column(sa.LargeBinary) # chain b view public key |
|
pkbs = sa.Column(sa.LargeBinary) # chain b view spend key |
|
|
|
a_lock_tx = sa.Column(sa.LargeBinary) |
|
a_lock_tx_script = sa.Column(sa.LargeBinary) |
|
a_lock_tx_id = sa.Column(sa.LargeBinary) |
|
a_lock_tx_vout = sa.Column(sa.Integer) |
|
|
|
a_lock_refund_tx = sa.Column(sa.LargeBinary) |
|
a_lock_refund_tx_script = sa.Column(sa.LargeBinary) |
|
a_lock_refund_tx_id = sa.Column(sa.LargeBinary) |
|
a_swap_refund_value = sa.Column(sa.BigInteger) |
|
al_lock_refund_tx_sig = sa.Column(sa.LargeBinary) |
|
af_lock_refund_tx_sig = sa.Column(sa.LargeBinary) |
|
|
|
a_lock_refund_spend_tx = sa.Column(sa.LargeBinary) |
|
a_lock_refund_spend_tx_id = sa.Column(sa.LargeBinary) |
|
|
|
af_lock_refund_spend_tx_esig = sa.Column(sa.LargeBinary) |
|
af_lock_refund_spend_tx_sig = sa.Column(sa.LargeBinary) |
|
|
|
a_lock_spend_tx = sa.Column(sa.LargeBinary) |
|
a_lock_spend_tx_id = sa.Column(sa.LargeBinary) |
|
al_lock_spend_tx_esig = sa.Column(sa.LargeBinary) |
|
kal_sig = sa.Column(sa.LargeBinary) |
|
|
|
a_lock_refund_swipe_tx = sa.Column(sa.LargeBinary) # Follower spends script coin lock refund tx |
|
|
|
b_lock_tx_id = sa.Column(sa.LargeBinary) |
|
|
|
|
|
class XmrSplitData(Base): |
|
__tablename__ = 'xmr_split_data' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
addr_from = sa.Column(sa.String) |
|
addr_to = sa.Column(sa.String) |
|
bid_id = sa.Column(sa.LargeBinary) |
|
msg_type = sa.Column(sa.Integer) |
|
msg_sequence = sa.Column(sa.Integer) |
|
dleag = sa.Column(sa.LargeBinary) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
__table_args__ = (sa.UniqueConstraint('bid_id', 'msg_type', 'msg_sequence', name='uc_1'),) |
|
|
|
|
|
class RevokedMessage(Base): |
|
__tablename__ = 'revoked_messages' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
msg_id = sa.Column(sa.LargeBinary) |
|
created_at = sa.Column(sa.BigInteger) |
|
expires_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class Wallets(Base): |
|
__tablename__ = 'wallets' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
coin_id = sa.Column(sa.Integer) |
|
wallet_name = sa.Column(sa.String) |
|
wallet_data = sa.Column(sa.String) |
|
balance_type = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class KnownIdentity(Base): |
|
__tablename__ = 'knownidentities' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
address = sa.Column(sa.String) |
|
label = sa.Column(sa.String) |
|
publickey = sa.Column(sa.LargeBinary) |
|
num_sent_bids_successful = sa.Column(sa.Integer) |
|
num_recv_bids_successful = sa.Column(sa.Integer) |
|
num_sent_bids_rejected = sa.Column(sa.Integer) |
|
num_recv_bids_rejected = sa.Column(sa.Integer) |
|
num_sent_bids_failed = sa.Column(sa.Integer) |
|
num_recv_bids_failed = sa.Column(sa.Integer) |
|
automation_override = sa.Column(sa.Integer) # AutomationOverrideOptions |
|
visibility_override = sa.Column(sa.Integer) # VisibilityOverrideOptions |
|
data = sa.Column(sa.LargeBinary) |
|
note = sa.Column(sa.String) |
|
updated_at = sa.Column(sa.BigInteger) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class AutomationStrategy(Base): |
|
__tablename__ = 'automationstrategies' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
|
|
label = sa.Column(sa.String) |
|
type_ind = sa.Column(sa.Integer) |
|
only_known_identities = sa.Column(sa.Integer) |
|
num_concurrent = sa.Column(sa.Integer) |
|
data = sa.Column(sa.LargeBinary) |
|
|
|
note = sa.Column(sa.String) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class AutomationLink(Base): |
|
__tablename__ = 'automationlinks' |
|
# Contains per order/bid options |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
|
|
linked_type = sa.Column(sa.Integer) |
|
linked_id = sa.Column(sa.LargeBinary) |
|
strategy_id = sa.Column(sa.Integer) |
|
|
|
data = sa.Column(sa.LargeBinary) |
|
repeat_limit = sa.Column(sa.Integer) |
|
repeat_count = sa.Column(sa.Integer) |
|
|
|
note = sa.Column(sa.String) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
__table_args__ = (sa.Index('linked_index', 'linked_type', 'linked_id'), ) |
|
|
|
|
|
class History(Base): |
|
__tablename__ = 'history' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
concept_type = sa.Column(sa.Integer) |
|
concept_id = sa.Column(sa.Integer) |
|
|
|
changed_data = sa.Column(sa.LargeBinary) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class BidState(Base): |
|
__tablename__ = 'bidstates' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
state_id = sa.Column(sa.Integer) |
|
label = sa.Column(sa.String) |
|
in_progress = sa.Column(sa.Integer) |
|
in_error = sa.Column(sa.Integer) |
|
swap_failed = sa.Column(sa.Integer) |
|
swap_ended = sa.Column(sa.Integer) |
|
|
|
note = sa.Column(sa.String) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
|
|
class Notification(Base): |
|
__tablename__ = 'notifications' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
event_type = sa.Column(sa.Integer) |
|
event_data = sa.Column(sa.LargeBinary) |
|
|
|
|
|
class MessageLink(Base): |
|
__tablename__ = 'message_links' |
|
|
|
record_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True) |
|
active_ind = sa.Column(sa.Integer) |
|
created_at = sa.Column(sa.BigInteger) |
|
|
|
linked_type = sa.Column(sa.Integer) |
|
linked_id = sa.Column(sa.LargeBinary) |
|
# linked_row_id = sa.Column(sa.Integer) # TODO: Find a way to use table rowids |
|
|
|
msg_type = sa.Column(sa.Integer) |
|
msg_sequence = sa.Column(sa.Integer) |
|
msg_id = sa.Column(sa.LargeBinary)
|
|
|