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