238 lines
9.1 KiB
Python
238 lines
9.1 KiB
Python
import logging
|
|
import xcat.userInput as userInput
|
|
import xcat.utils as utils
|
|
from xcat.xcatconf import ADDRS
|
|
from xcat.trades import Contract, Trade
|
|
from xcat.bitcoinRPC import bitcoinProxy
|
|
from xcat.zcashRPC import zcashProxy
|
|
import json
|
|
|
|
class Protocol():
|
|
def __init__(self):
|
|
self.bitcoinRPC = bitcoinProxy()
|
|
self.zcashRPC = zcashProxy()
|
|
|
|
def generate(self, num):
|
|
self.bitcoinRPC.generate(num)
|
|
self.zcashRPC.generate(num)
|
|
|
|
def is_myaddr(self, address):
|
|
# Handle differnt network prefixes
|
|
if address[:1] == 'm':
|
|
status = self.bitcoinRPC.validateaddress(address)
|
|
else:
|
|
status = self.zcashRPC.validateaddress(address)
|
|
|
|
logging.debug("Address status: ", status)
|
|
|
|
if not status['isvalid']:
|
|
raise ValueError("Invalid address: %s" % address)
|
|
elif 'ismine' in status:
|
|
status = status['ismine']
|
|
# print("Address {0} is mine: {1}".format(address, status))
|
|
return status
|
|
|
|
def find_secret_from_fundtx(self, currency, p2sh, fundtx):
|
|
if currency == 'bitcoin':
|
|
secret = self.bitcoinRPC.find_secret(p2sh, fundtx)
|
|
elif currency == 'zcash':
|
|
secret = self.zcashRPC.find_secret(p2sh, fundtx)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
return secret
|
|
|
|
def import_addrs(self, trade):
|
|
self.check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
|
self.check_fund_status(trade.buy.currency, trade.buy.p2sh)
|
|
|
|
def check_p2sh(self, currency, address):
|
|
if currency == 'bitcoin':
|
|
print("Checking funds in Bitcoin p2sh")
|
|
return self.bitcoinRPC.check_funds(address)
|
|
elif currency == 'zcash':
|
|
print("Checking funds in Zcash p2sh")
|
|
return self.zcashRPC.check_funds(address)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
|
|
def check_fund_status(self, currency, address):
|
|
if currency == 'bitcoin':
|
|
print("Checking funds in Bitcoin p2sh")
|
|
return self.bitcoinRPC.get_fund_status(address)
|
|
elif currency == 'zcash':
|
|
print("Checking funds in Zcash p2sh")
|
|
return self.zcashRPC.get_fund_status(address)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
|
|
# TODO: function to calculate appropriate locktimes between chains
|
|
# def verify_p2sh(trade):
|
|
# htlc = self.create_htlc(
|
|
# trade.buy.currency, trade.buy.fulfiller,
|
|
# trade.buy.initiator, trade.commitment,
|
|
# trade.buy.locktime)
|
|
# buyer_p2sh = htlc['p2sh']
|
|
# print("Buyer p2sh:", buyer_p2sh)
|
|
# If the two p2sh match...
|
|
# if buyer_p2sh == trade.buy.p2sh:
|
|
# else:
|
|
# print("Compiled p2sh for htlc does not match what seller sent.")
|
|
|
|
def create_htlc(self, currency, funder, redeemer, commitment, locktime):
|
|
if currency == 'bitcoin':
|
|
sell_p2sh = self.bitcoinRPC.hashtimelockcontract(
|
|
funder, redeemer, commitment, locktime)
|
|
elif currency == 'zcash':
|
|
sell_p2sh = self.zcashRPC.hashtimelockcontract(
|
|
funder, redeemer, commitment, locktime)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
return sell_p2sh
|
|
|
|
def fund_htlc(self, currency, p2sh, amount):
|
|
if currency == 'bitcoin':
|
|
txid = self.bitcoinRPC.fund_htlc(p2sh, amount)
|
|
elif currency == 'zcash':
|
|
txid = self.zcashRPC.fund_htlc(p2sh, amount)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
return txid
|
|
|
|
def fund_contract(self, contract):
|
|
txid = self.fund_htlc(
|
|
contract.currency, contract.p2sh, contract.amount)
|
|
return txid
|
|
|
|
def fund_sell_contract(self, trade):
|
|
sell = trade.sell
|
|
txid = self.fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
|
setattr(trade.sell, 'fund_tx', txid)
|
|
utils.save(trade)
|
|
return txid
|
|
|
|
def create_sell_p2sh(self, trade, commitment, locktime):
|
|
# CREATE SELL CONTRACT
|
|
sell = trade.sell
|
|
contract = self.create_htlc(sell.currency, sell.initiator,
|
|
sell.fulfiller, commitment, locktime)
|
|
print("sell contract", contract)
|
|
setattr(trade.sell, 'p2sh', contract['p2sh'])
|
|
setattr(trade.sell, 'redeemScript', contract['redeemScript'])
|
|
setattr(trade.sell, 'redeemblocknum', contract['redeemblocknum'])
|
|
setattr(trade.buy, 'locktime', contract['locktime'])
|
|
utils.save(trade)
|
|
|
|
def create_buy_p2sh(self, trade, commitment, locktime):
|
|
# CREATE BUY CONTRACT
|
|
buy = trade.buy
|
|
print("\nNow creating buy contract on the {0} blockchain where you "
|
|
"will wait for the buyer to send funds...".format(buy.currency))
|
|
buy_contract = self.create_htlc(
|
|
buy.currency, buy.fulfiller, buy.initiator, commitment, locktime)
|
|
print("Buy contract", buy_contract)
|
|
|
|
setattr(trade.buy, 'p2sh', buy_contract['p2sh'])
|
|
setattr(trade.buy, 'redeemScript', buy_contract['redeemScript'])
|
|
setattr(trade.buy, 'redeemblocknum', buy_contract['redeemblocknum'])
|
|
setattr(trade.buy, 'locktime', buy_contract['locktime'])
|
|
print("\nNow contact the buyer and tell them to send funds to this "
|
|
"p2sh: {0}\n".format(trade.buy.p2sh))
|
|
|
|
utils.save(trade)
|
|
|
|
def redeem_p2sh(self, contract, secret):
|
|
currency = contract.currency
|
|
if currency == 'bitcoin':
|
|
res = self.bitcoinRPC.redeem_contract(contract, secret)
|
|
elif currency == 'zcash':
|
|
res = self.zcashRPC.redeem_contract(contract, secret)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s' % currency)
|
|
return res
|
|
|
|
def refund_contract(self, contract):
|
|
currency = contract.currency
|
|
if currency == 'bitcoin':
|
|
res = self.bitcoinRPC.refund(contract)
|
|
elif currency == 'zcash':
|
|
res = self.zcashRPC.refund(contract)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s', currency)
|
|
return res
|
|
|
|
def parse_secret(self, currency, txid):
|
|
if currency == 'bitcoin':
|
|
secret = self.bitcoinRPC.parse_secret(txid)
|
|
elif currency == 'zcash':
|
|
secret = self.zcashRPC.parse_secret(txid)
|
|
else:
|
|
raise ValueError('Currency not recognized: %s', currency)
|
|
return secret
|
|
|
|
def seller_redeem_p2sh(self, trade, secret):
|
|
buy = trade.buy
|
|
userInput.authorize_seller_redeem(buy)
|
|
|
|
if trade.sell.get_status() == 'redeemed':
|
|
print("You already redeemed the funds and acquired "
|
|
"{0} {1}".format(buy.amount, buy.currency))
|
|
exit()
|
|
else:
|
|
# Seller redeems buyer's funded tx (contract in p2sh)
|
|
txs = self.redeem_p2sh(trade.buy, secret)
|
|
print("You have redeemed "
|
|
"{0} {1}!".format(buy.amount, buy.currency))
|
|
return txs
|
|
|
|
def seller_init(self, tradeid, trade, network):
|
|
secret = utils.generate_password()
|
|
hash_of_secret = utils.sha256(secret)
|
|
# TODO: Implement locktimes and mock block passage of time
|
|
sell_locktime = 20
|
|
buy_locktime = 10 # Must be more than first tx
|
|
print("Creating pay-to-script-hash for sell contract...")
|
|
|
|
# create the p2sh addrs
|
|
self.create_sell_p2sh(trade, hash_of_secret, sell_locktime)
|
|
self.create_buy_p2sh(trade, hash_of_secret, buy_locktime)
|
|
|
|
trade.commitment = utils.b2x(hash_of_secret)
|
|
print("TRADE after seller init {0}".format(trade.toJSON()))
|
|
return trade, secret
|
|
|
|
def initialize_trade(self, tradeid, **kwargs):
|
|
trade = Trade()
|
|
conf = kwargs['conf']
|
|
if conf == 'cli':
|
|
init_addrs = userInput.get_initiator_addresses()
|
|
fulfill_addrs = userInput.get_fulfiller_addresses()
|
|
amounts = userInput.get_trade_amounts()
|
|
print("AMOUNTS", amounts)
|
|
else:
|
|
print("Conf in init trade", conf)
|
|
if conf == 'testnet' or conf == 'regtest':
|
|
# If json is not passed on cli, use ADDR obj from xcatconf.py
|
|
conf = ADDRS[conf]
|
|
else:
|
|
# Allow for passing in multiple trades at a time
|
|
conf = json.loads(conf)[0]
|
|
init_addrs = conf['initiator']
|
|
fulfill_addrs = conf['fulfiller']
|
|
amounts = conf['amounts']
|
|
|
|
sell = amounts['sell']
|
|
buy = amounts['buy']
|
|
sell_currency = sell['currency']
|
|
buy_currency = buy['currency']
|
|
sell['initiator'] = init_addrs[sell_currency]
|
|
buy['initiator'] = init_addrs[buy_currency]
|
|
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
|
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
|
|
|
# initializing contract classes with addresses, currencies, and amounts
|
|
trade.sell = Contract(sell)
|
|
trade.buy = Contract(buy)
|
|
print(trade.sell.__dict__)
|
|
print(trade.buy.__dict__)
|
|
return tradeid, trade
|