Added an rudimentary address pool.
This commit is contained in:
parent
b02ddb3bec
commit
d155774dbc
@ -116,6 +116,13 @@ class OpCodes(IntEnum):
|
|||||||
OP_CHECKSEQUENCEVERIFY = 0xb2,
|
OP_CHECKSEQUENCEVERIFY = 0xb2,
|
||||||
|
|
||||||
|
|
||||||
|
class TxTypes(IntEnum): # For PooledAddress
|
||||||
|
ITX_REDEEM = auto()
|
||||||
|
ITX_REFUND = auto()
|
||||||
|
PTX_REDEEM = auto()
|
||||||
|
PTX_REFUND = auto()
|
||||||
|
|
||||||
|
|
||||||
SEQUENCE_LOCK_BLOCKS = 1
|
SEQUENCE_LOCK_BLOCKS = 1
|
||||||
SEQUENCE_LOCK_TIME = 2
|
SEQUENCE_LOCK_TIME = 2
|
||||||
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
|
SEQUENCE_LOCKTIME_GRANULARITY = 9 # 512 seconds
|
||||||
@ -405,6 +412,8 @@ class PooledAddress(Base):
|
|||||||
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
addr_id = sa.Column(sa.Integer, primary_key=True, autoincrement=True)
|
||||||
coin_type = sa.Column(sa.Integer)
|
coin_type = sa.Column(sa.Integer)
|
||||||
addr = sa.Column(sa.String)
|
addr = sa.Column(sa.String)
|
||||||
|
bid_id = sa.Column(sa.LargeBinary)
|
||||||
|
tx_type = sa.Column(sa.Integer)
|
||||||
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
@ -816,6 +825,46 @@ class BasicSwap():
|
|||||||
|
|
||||||
return hashlib.sha256(bytes(self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, path])['key_info']['result'], 'utf-8')).digest()
|
return hashlib.sha256(bytes(self.callcoinrpc(Coins.PART, 'extkey', ['info', evkey, path])['key_info']['result'], 'utf-8')).digest()
|
||||||
|
|
||||||
|
def getReceiveAddressFromPool(self, coin_type, bid_id, tx_type):
|
||||||
|
self.log.debug('Get address from pool bid_id {}, type {}, coin {}'.format(bid_id.hex(), tx_type, coin_type))
|
||||||
|
self.mxDB.acquire()
|
||||||
|
try:
|
||||||
|
session = scoped_session(self.session_factory)
|
||||||
|
try:
|
||||||
|
record = session.query(PooledAddress).filter(PooledAddress.coin_type == int(coin_type) and PooledAddress.bid_id == None).one()
|
||||||
|
except Exception:
|
||||||
|
address = self.getReceiveAddressForCoin(coin_type)
|
||||||
|
record = PooledAddress(
|
||||||
|
addr=address,
|
||||||
|
coin_type=int(coin_type))
|
||||||
|
record.bid_id = bid_id
|
||||||
|
record.tx_type = tx_type
|
||||||
|
addr = record.addr
|
||||||
|
session.add(record)
|
||||||
|
session.commit()
|
||||||
|
session.close()
|
||||||
|
session.remove()
|
||||||
|
finally:
|
||||||
|
self.mxDB.release()
|
||||||
|
return addr
|
||||||
|
|
||||||
|
def returnAddressToPool(self, bid_id, tx_type):
|
||||||
|
self.log.debug('Return address to pool bid_id {}, type {}'.format(bid_id.hex(), tx_type))
|
||||||
|
self.mxDB.acquire()
|
||||||
|
try:
|
||||||
|
session = scoped_session(self.session_factory)
|
||||||
|
try:
|
||||||
|
record = session.query(PooledAddress).filter(PooledAddress.bid_id == bid_id and PooledAddress.tx_type == tx_type).one()
|
||||||
|
self.log.debug('Returning address to pool addr {}'.format(record.addr))
|
||||||
|
record.bid_id = None
|
||||||
|
session.commit()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
session.close()
|
||||||
|
session.remove()
|
||||||
|
finally:
|
||||||
|
self.mxDB.release()
|
||||||
|
|
||||||
def getReceiveAddressForCoin(self, coin_type):
|
def getReceiveAddressForCoin(self, coin_type):
|
||||||
if coin_type == Coins.PART:
|
if coin_type == Coins.PART:
|
||||||
new_addr = self.callcoinrpc(Coins.PART, 'getnewaddress')
|
new_addr = self.callcoinrpc(Coins.PART, 'getnewaddress')
|
||||||
@ -1157,6 +1206,16 @@ class BasicSwap():
|
|||||||
# Remove any watched outputs
|
# Remove any watched outputs
|
||||||
self.removeWatchedOutput(Coins(offer.coin_from), bid_id, None)
|
self.removeWatchedOutput(Coins(offer.coin_from), bid_id, None)
|
||||||
self.removeWatchedOutput(Coins(offer.coin_to), bid_id, None)
|
self.removeWatchedOutput(Coins(offer.coin_to), bid_id, None)
|
||||||
|
|
||||||
|
# Return unused addrs to pool
|
||||||
|
if bid.initiate_txn_state != TxStates.TX_REDEEMED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.ITX_REDEEM)
|
||||||
|
if bid.initiate_txn_state != TxStates.TX_REFUNDED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.ITX_REFUND)
|
||||||
|
if bid.participate_txn_state != TxStates.TX_REDEEMED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.PTX_REDEEM)
|
||||||
|
if bid.participate_txn_state != TxStates.TX_REFUNDED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.PTX_REFUND)
|
||||||
finally:
|
finally:
|
||||||
session.close()
|
session.close()
|
||||||
session.remove()
|
session.remove()
|
||||||
@ -1228,7 +1287,7 @@ class BasicSwap():
|
|||||||
|
|
||||||
txn_signed = self.callcoinrpc(coin_to, 'signrawtransactionwithwallet', [txn_funded])['hex']
|
txn_signed = self.callcoinrpc(coin_to, 'signrawtransactionwithwallet', [txn_funded])['hex']
|
||||||
|
|
||||||
refund_txn = self.createRefundTxn(coin_to, txn_signed, bid, bid.participate_script)
|
refund_txn = self.createRefundTxn(coin_to, txn_signed, bid, bid.participate_script, tx_type=TxTypes.PTX_REFUND)
|
||||||
bid.participate_txn_refund = bytes.fromhex(refund_txn)
|
bid.participate_txn_refund = bytes.fromhex(refund_txn)
|
||||||
|
|
||||||
chain_height = self.callcoinrpc(coin_to, 'getblockchaininfo')['blocks']
|
chain_height = self.callcoinrpc(coin_to, 'getblockchaininfo')['blocks']
|
||||||
@ -1307,7 +1366,7 @@ class BasicSwap():
|
|||||||
assert(amount_out > 0), 'Amount out <= 0'
|
assert(amount_out > 0), 'Amount out <= 0'
|
||||||
|
|
||||||
if addr_redeem_out is None:
|
if addr_redeem_out is None:
|
||||||
addr_redeem_out = self.getReceiveAddressForCoin(coin_type)
|
addr_redeem_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, TxTypes.PTX_REDEEM if for_txn_type == 'participate' else TxTypes.ITX_REDEEM)
|
||||||
assert(addr_redeem_out is not None)
|
assert(addr_redeem_out is not None)
|
||||||
|
|
||||||
if self.coin_clients[coin_type]['use_segwit']:
|
if self.coin_clients[coin_type]['use_segwit']:
|
||||||
@ -1359,7 +1418,7 @@ class BasicSwap():
|
|||||||
|
|
||||||
return redeem_txn
|
return redeem_txn
|
||||||
|
|
||||||
def createRefundTxn(self, coin_type, txn, bid, txn_script, addr_refund_out=None, fee_rate=None):
|
def createRefundTxn(self, coin_type, txn, bid, txn_script, addr_refund_out=None, fee_rate=None, tx_type=TxTypes.ITX_REFUND):
|
||||||
self.log.debug('createRefundTxn')
|
self.log.debug('createRefundTxn')
|
||||||
if self.coin_clients[coin_type]['connection_type'] != 'rpc':
|
if self.coin_clients[coin_type]['connection_type'] != 'rpc':
|
||||||
return None
|
return None
|
||||||
@ -1400,7 +1459,7 @@ class BasicSwap():
|
|||||||
assert(amount_out > 0), 'Amount out <= 0'
|
assert(amount_out > 0), 'Amount out <= 0'
|
||||||
|
|
||||||
if addr_refund_out is None:
|
if addr_refund_out is None:
|
||||||
addr_refund_out = self.getReceiveAddressForCoin(coin_type)
|
addr_refund_out = self.getReceiveAddressFromPool(coin_type, bid.bid_id, tx_type)
|
||||||
assert(addr_refund_out is not None), 'addr_refund_out is null'
|
assert(addr_refund_out is not None), 'addr_refund_out is null'
|
||||||
if self.coin_clients[coin_type]['use_segwit']:
|
if self.coin_clients[coin_type]['use_segwit']:
|
||||||
# Change to btc hrp
|
# Change to btc hrp
|
||||||
@ -1659,6 +1718,16 @@ class BasicSwap():
|
|||||||
if (bid.initiate_txn_state is None or bid.initiate_txn_state >= TxStates.TX_REDEEMED) \
|
if (bid.initiate_txn_state is None or bid.initiate_txn_state >= TxStates.TX_REDEEMED) \
|
||||||
and (bid.participate_txn_state is None or bid.participate_txn_state >= TxStates.TX_REDEEMED):
|
and (bid.participate_txn_state is None or bid.participate_txn_state >= TxStates.TX_REDEEMED):
|
||||||
self.log.info('Swap completed for bid %s', bid_id.hex())
|
self.log.info('Swap completed for bid %s', bid_id.hex())
|
||||||
|
|
||||||
|
if bid.initiate_txn_state == TxStates.TX_REDEEMED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.ITX_REFUND)
|
||||||
|
else:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.ITX_REDEEM)
|
||||||
|
if bid.participate_txn_state == TxStates.TX_REDEEMED:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.PTX_REFUND)
|
||||||
|
else:
|
||||||
|
self.returnAddressToPool(bid_id, TxTypes.PTX_REDEEM)
|
||||||
|
|
||||||
bid.setState(BidStates.SWAP_COMPLETED)
|
bid.setState(BidStates.SWAP_COMPLETED)
|
||||||
self.saveBid(bid_id, bid)
|
self.saveBid(bid_id, bid)
|
||||||
return True # Mark bid for archiving
|
return True # Mark bid for archiving
|
||||||
@ -2218,9 +2287,10 @@ class BasicSwap():
|
|||||||
if offer_id is not None:
|
if offer_id is not None:
|
||||||
q = session.query(Bid).filter(Bid.offer_id == offer_id)
|
q = session.query(Bid).filter(Bid.offer_id == offer_id)
|
||||||
elif sent:
|
elif sent:
|
||||||
q = session.query(Bid).filter(Bid.was_sent == True).order_by(Bid.created_at.desc()) # noqa E712
|
q = session.query(Bid).filter(Bid.was_sent == True)
|
||||||
else:
|
else:
|
||||||
q = session.query(Bid).filter(Bid.was_received == True).order_by(Bid.created_at.desc()) # noqa E712
|
q = session.query(Bid).filter(Bid.was_received == True)
|
||||||
|
q = q.order_by(Bid.created_at.desc()) # noqa E712
|
||||||
for row in q:
|
for row in q:
|
||||||
rv.append(row)
|
rv.append(row)
|
||||||
return rv
|
return rv
|
||||||
|
@ -23,6 +23,8 @@ from .chainparams import (
|
|||||||
)
|
)
|
||||||
from .basicswap import (
|
from .basicswap import (
|
||||||
SwapTypes,
|
SwapTypes,
|
||||||
|
BidStates,
|
||||||
|
TxStates,
|
||||||
getOfferState,
|
getOfferState,
|
||||||
getBidState,
|
getBidState,
|
||||||
getTxState,
|
getTxState,
|
||||||
@ -34,10 +36,11 @@ def getCoinName(c):
|
|||||||
return chainparams[c]['name'].capitalize()
|
return chainparams[c]['name'].capitalize()
|
||||||
|
|
||||||
|
|
||||||
def html_content_start(title, h2=None):
|
def html_content_start(title, h2=None, refresh=None):
|
||||||
content = '<!DOCTYPE html><html lang="en">\n<head>' \
|
content = '<!DOCTYPE html><html lang="en">\n<head>' \
|
||||||
+ '<meta charset="UTF-8">' \
|
+ '<meta charset="UTF-8">' \
|
||||||
+ '<title>' + title + '</title></head>' \
|
+ ('' if not refresh else '<meta http-equiv="refresh" content="{}">'.format(refresh)) \
|
||||||
|
+ '<title>' + title + '</title></head>\n' \
|
||||||
+ '<body>'
|
+ '<body>'
|
||||||
if h2 is not None:
|
if h2 is not None:
|
||||||
content += '<h2>' + h2 + '</h2>'
|
content += '<h2>' + h2 + '</h2>'
|
||||||
@ -294,8 +297,9 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
raise ValueError('Bad bid ID')
|
raise ValueError('Bad bid ID')
|
||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
|
|
||||||
content = html_content_start(self.server.title, self.server.title) \
|
content = html_content_start(self.server.title, self.server.title, 30) \
|
||||||
+ '<h3>Bid: ' + bid_id.hex() + '</h3>'
|
+ '<h3>Bid: ' + bid_id.hex() + '</h3>' \
|
||||||
|
+ '<p>Page Refresh: 30 seconds</p>'
|
||||||
|
|
||||||
show_txns = False
|
show_txns = False
|
||||||
if post_string != '':
|
if post_string != '':
|
||||||
@ -328,11 +332,38 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
ticker_from = swap_client.getTicker(coin_from)
|
ticker_from = swap_client.getTicker(coin_from)
|
||||||
ticker_to = swap_client.getTicker(coin_to)
|
ticker_to = swap_client.getTicker(coin_to)
|
||||||
|
|
||||||
|
if bid.state == BidStates.BID_SENT:
|
||||||
|
state_description = 'Waiting for seller to accept.'
|
||||||
|
elif bid.state == BidStates.BID_RECEIVED:
|
||||||
|
state_description = 'Waiting for seller to accept.'
|
||||||
|
elif bid.state == BidStates.BID_ACCEPTED:
|
||||||
|
if not bid.initiate_txid:
|
||||||
|
state_description = 'Waiting for seller to send initiate tx.'
|
||||||
|
else:
|
||||||
|
state_description = 'Waiting for initiate tx to confirm.'
|
||||||
|
elif bid.state == BidStates.SWAP_INITIATED:
|
||||||
|
state_description = 'Waiting for participate txn to be confirmed in {} chain'.format(ticker_to)
|
||||||
|
elif bid.state == BidStates.SWAP_PARTICIPATING:
|
||||||
|
state_description = 'Waiting for initiate txn to be spent in {} chain'.format(ticker_from)
|
||||||
|
elif bid.state == BidStates.SWAP_COMPLETED:
|
||||||
|
state_description = 'Swap completed'
|
||||||
|
if bid.initiate_txn_state == TxStates.TX_REDEEMED and bid.participate_txn_state == TxStates.TX_REDEEMED:
|
||||||
|
state_description += ' successfully'
|
||||||
|
else:
|
||||||
|
state_description += ', ITX ' + getTxState(bid.initiate_txn_state + ', PTX ' + getTxState(bid.participate_txn_state))
|
||||||
|
elif bid.state == BidStates.SWAP_TIMEDOUT:
|
||||||
|
state_description = 'Timed out waiting for initiate txn'
|
||||||
|
elif bid.state == BidStates.BID_ABANDONED:
|
||||||
|
state_description = 'Bid abandoned'
|
||||||
|
else:
|
||||||
|
state_description = ''
|
||||||
|
|
||||||
tr = '<tr><td>{}</td><td>{}</td></tr>'
|
tr = '<tr><td>{}</td><td>{}</td></tr>'
|
||||||
content += '<table>'
|
content += '<table>'
|
||||||
|
|
||||||
content += tr.format('Swap', format8(bid.amount) + ' ' + ticker_from + ' for ' + format8((bid.amount * offer.rate) // COIN) + ' ' + ticker_to)
|
content += tr.format('Swap', format8(bid.amount) + ' ' + ticker_from + ' for ' + format8((bid.amount * offer.rate) // COIN) + ' ' + ticker_to)
|
||||||
content += tr.format('Bid State', getBidState(bid.state))
|
content += tr.format('Bid State', getBidState(bid.state))
|
||||||
|
content += tr.format('State Description', state_description)
|
||||||
content += tr.format('ITX State', getTxState(bid.initiate_txn_state))
|
content += tr.format('ITX State', getTxState(bid.initiate_txn_state))
|
||||||
content += tr.format('PTX State', getTxState(bid.participate_txn_state))
|
content += tr.format('PTX State', getTxState(bid.participate_txn_state))
|
||||||
content += tr.format('Offer', '<a href="/offer/' + bid.offer_id.hex() + '">' + bid.offer_id.hex() + '</a>')
|
content += tr.format('Offer', '<a href="/offer/' + bid.offer_id.hex() + '">' + bid.offer_id.hex() + '</a>')
|
||||||
@ -419,9 +450,10 @@ class HttpHandler(BaseHTTPRequestHandler):
|
|||||||
swap_client = self.server.swap_client
|
swap_client = self.server.swap_client
|
||||||
summary = swap_client.getSummary()
|
summary = swap_client.getSummary()
|
||||||
|
|
||||||
content = html_content_start(self.server.title, self.server.title) \
|
content = html_content_start(self.server.title, self.server.title, 30) \
|
||||||
+ '<p><a href="/wallets">View Wallets</a></p>' \
|
+ '<p><a href="/wallets">View Wallets</a></p>' \
|
||||||
+ '<p>' \
|
+ '<p>' \
|
||||||
|
+ 'Page Refresh: 30 seconds<br/>' \
|
||||||
+ 'Network: ' + str(summary['network']) + '<br/>' \
|
+ 'Network: ' + str(summary['network']) + '<br/>' \
|
||||||
+ '<a href="/active">Swaps in progress: ' + str(summary['num_swapping']) + '</a><br/>' \
|
+ '<a href="/active">Swaps in progress: ' + str(summary['num_swapping']) + '</a><br/>' \
|
||||||
+ '<a href="/offers">Network Offers: ' + str(summary['num_network_offers']) + '</a><br/>' \
|
+ '<a href="/offers">Network Offers: ' + str(summary['num_network_offers']) + '</a><br/>' \
|
||||||
|
@ -264,7 +264,7 @@ def main():
|
|||||||
sys.stderr.write('Error: {} exists, exiting.\n'.format(config_path))
|
sys.stderr.write('Error: {} exists, exiting.\n'.format(config_path))
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
port_offset = 300 if chain == testnet else 0
|
port_offset = 300 if chain == 'testnet' else 0
|
||||||
settings = {
|
settings = {
|
||||||
'debug': True,
|
'debug': True,
|
||||||
'zmqhost': 'tcp://127.0.0.1',
|
'zmqhost': 'tcp://127.0.0.1',
|
||||||
|
Loading…
Reference in New Issue
Block a user