2017-05-22 18:00:34 -07:00
import zXcat
import bXcat
from utils import *
from waiting import *
from time import sleep
import json
2017-05-23 13:34:48 -07:00
import os , sys
2017-05-22 18:00:34 -07:00
from pprint import pprint
2017-05-26 15:21:23 -07:00
from trades import Contract , Trade
2017-05-26 15:08:57 -07:00
import userInput
2017-05-22 18:00:34 -07:00
def check_p2sh ( currency , address ) :
if currency == ' bitcoin ' :
2017-05-23 14:12:01 -07:00
print ( " Checking funds in Bitcoin p2sh " )
2017-05-22 18:00:34 -07:00
return bXcat . check_funds ( address )
else :
2017-05-23 14:12:01 -07:00
print ( " Checking funds in Zcash p2sh " )
2017-05-22 18:00:34 -07:00
return zXcat . check_funds ( address )
def create_htlc ( currency , funder , redeemer , secret , locktime ) :
if currency == ' bitcoin ' :
sell_p2sh = bXcat . hashtimelockcontract ( funder , redeemer , secret , locktime )
else :
sell_p2sh = zXcat . hashtimelockcontract ( funder , redeemer , secret , locktime )
return sell_p2sh
def fund_htlc ( currency , p2sh , amount ) :
if currency == ' bitcoin ' :
txid = bXcat . fund_htlc ( p2sh , amount )
else :
txid = zXcat . fund_htlc ( p2sh , amount )
return txid
2017-05-26 15:08:57 -07:00
def fund_buy_contract ( trade ) :
buy = trade . buyContract
txid = fund_htlc ( buy . currency , buy . p2sh , buy . amount )
setattr ( trade . buyContract , ' fund_tx ' , txid )
save ( trade )
return txid
2017-05-22 18:00:34 -07:00
2017-05-26 15:08:57 -07:00
def fund_sell_contract ( trade ) :
sell = trade . sellContract
txid = fund_htlc ( sell . currency , sell . p2sh , sell . amount )
setattr ( trade . sellContract , ' fund_tx ' , txid )
save ( trade )
return txid
2017-05-26 13:15:52 -07:00
2017-05-26 15:08:57 -07:00
def create_sell_p2sh ( trade , secret , locktime ) :
# CREATE SELL CONTRACT
sell = trade . sellContract
contract = create_htlc ( sell . currency , sell . initiator , sell . fulfiller , secret , locktime )
2017-05-26 13:15:52 -07:00
print ( " sell contract " , contract )
2017-05-26 15:08:57 -07:00
setattr ( trade . sellContract , ' p2sh ' , contract [ ' p2sh ' ] )
setattr ( trade . sellContract , ' redeemScript ' , contract [ ' redeemScript ' ] )
setattr ( trade . sellContract , ' redeemblocknum ' , contract [ ' redeemblocknum ' ] )
save ( trade )
2017-05-22 18:00:34 -07:00
2017-05-26 15:08:57 -07:00
def create_buy_p2sh ( trade , secret , locktime ) :
2017-05-26 13:15:52 -07:00
## CREATE BUY CONTRACT
2017-05-26 15:08:57 -07:00
buy = trade . buyContract
print ( " Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds... " . format ( buy . currency ) )
print ( " HTLC DETAILS " , buy . currency , buy . fulfiller , buy . initiator , secret , locktime )
buy_contract = create_htlc ( buy . currency , buy . fulfiller , buy . initiator , secret , locktime )
2017-05-26 13:15:52 -07:00
print ( " Buy contract " , buy_contract )
2017-05-26 15:08:57 -07:00
setattr ( trade . buyContract , ' p2sh ' , buy_contract [ ' p2sh ' ] )
setattr ( trade . buyContract , ' redeemScript ' , buy_contract [ ' redeemScript ' ] )
setattr ( trade . buyContract , ' redeemblocknum ' , buy_contract [ ' redeemblocknum ' ] )
print ( " Now contact the buyer and tell them to send funds to this p2sh: " , trade . buyContract . p2sh )
2017-05-26 13:15:52 -07:00
2017-05-26 15:08:57 -07:00
save ( trade )
2017-05-22 18:00:34 -07:00
2017-05-26 13:15:52 -07:00
def redeem_p2sh ( contract , secret ) :
currency = contract . currency
2017-05-22 18:00:34 -07:00
if currency == ' bitcoin ' :
2017-05-26 13:15:52 -07:00
res = bXcat . auto_redeem ( contract , secret )
2017-05-22 18:00:34 -07:00
else :
2017-05-26 13:15:52 -07:00
res = zXcat . auto_redeem ( contract , secret )
2017-05-22 18:00:34 -07:00
return res
2017-05-26 15:08:57 -07:00
def print_trade ( role ) :
print ( " \n Trade status for {0} : " . format ( role ) )
trade = get_trade ( )
pprint ( trade )
2017-05-26 13:15:52 -07:00
2017-05-26 15:08:57 -07:00
#### Main functions determining user flow from command line
def buyer_redeem ( trade ) :
userInput . authorize_buyer_redeem ( trade )
if trade . sellContract . get_status ( ) == ' redeemed ' :
print ( " You already redeemed the funds and acquired {0} {1} " . format ( trade . sellContract . amount , trade . sellContract . currency ) )
2017-05-26 13:15:52 -07:00
exit ( )
2017-05-26 09:23:49 -07:00
else :
2017-05-26 13:15:52 -07:00
# Buyer redeems seller's funded tx
2017-05-26 15:08:57 -07:00
p2sh = trade . sellContract . p2sh
currency = trade . sellContract . currency
2017-05-26 13:15:52 -07:00
# Buy contract is where seller disclosed secret in redeeming
2017-05-26 15:08:57 -07:00
if trade . buyContract . currency == ' bitcoin ' :
secret = bXcat . parse_secret ( trade . buyContract . redeem_tx )
2017-05-26 13:15:52 -07:00
else :
2017-05-26 15:08:57 -07:00
secret = zXcat . parse_secret ( trade . buyContract . redeem_tx )
2017-05-26 13:15:52 -07:00
print ( " Found secret in seller ' s redeem tx " , secret )
2017-05-26 15:08:57 -07:00
redeem_tx = redeem_p2sh ( trade . sellContract , secret )
setattr ( trade . sellContract , ' redeem_tx ' , redeem_tx )
save ( trade )
2017-05-26 13:15:52 -07:00
exit ( )
2017-05-26 15:08:57 -07:00
def seller_redeem ( trade ) :
buy = trade . buyContract
userInput . authorize_seller_redeem ( buy )
2017-05-26 09:23:49 -07:00
2017-05-26 15:08:57 -07:00
if trade . sellContract . 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)
secret = userInput . retrieve_password ( )
2017-05-31 12:03:16 -07:00
tx_type , txid = redeem_p2sh ( trade . buyContract , secret )
setattr ( trade . buyContract , tx_type , txid )
2017-05-26 15:08:57 -07:00
save ( trade )
print ( " You have redeemed {0} {1} ! " . format ( buy . amount , buy . currency ) )
print_trade ( ' seller ' )
def buyer_fulfill ( trade ) :
buy = trade . buyContract
sell = trade . sellContract
buy_p2sh_balance = check_p2sh ( buy . currency , buy . p2sh )
sell_p2sh_balance = check_p2sh ( sell . currency , sell . p2sh )
if buy_p2sh_balance == 0 :
userInput . authorize_buyer_fulfill ( sell_p2sh_balance , sell . currency , buy_p2sh_balance , buy . currency )
print ( " Buy amt: " , buy . amount )
txid = fund_buy_contract ( trade )
print ( " Fund tx txid: " , txid )
else :
print ( " It looks like you ' ve already funded the contract to buy {1} , the amount in escrow in the p2sh is {0} . " . format ( buy_p2sh_balance , buy . currency ) )
print ( " Please wait for the seller to remove your funds from escrow to complete the trade. " )
print_trade ( ' buyer ' )
2017-05-26 09:23:49 -07:00
2017-05-26 13:15:52 -07:00
def seller_initiate ( trade ) :
2017-05-26 15:08:57 -07:00
# Get amounts
amounts = userInput . get_trade_amounts ( )
2017-05-26 13:15:52 -07:00
sell = amounts [ ' sell ' ]
buy = amounts [ ' buy ' ]
sell_currency = sell [ ' currency ' ]
buy_currency = buy [ ' currency ' ]
2017-05-26 15:08:57 -07:00
# Get addresses
init_addrs = userInput . get_initiator_addresses ( )
2017-05-26 13:15:52 -07:00
sell [ ' initiator ' ] = init_addrs [ sell_currency ]
buy [ ' initiator ' ] = init_addrs [ buy_currency ]
2017-05-26 15:08:57 -07:00
fulfill_addrs = userInput . get_fulfiller_addresses ( )
2017-05-26 13:15:52 -07:00
sell [ ' fulfiller ' ] = fulfill_addrs [ sell_currency ]
buy [ ' fulfiller ' ] = fulfill_addrs [ buy_currency ]
# initializing contract classes with addresses, currencies, and amounts
2017-05-26 15:08:57 -07:00
trade . sellContract = Contract ( sell )
trade . buyContract = Contract ( buy )
print ( trade . sellContract . __dict__ )
print ( trade . buyContract . __dict__ )
2017-05-26 13:15:52 -07:00
2017-05-26 15:08:57 -07:00
secret = userInput . create_password ( )
# TODO: Implement locktimes and mock block passage of time
2017-05-31 12:03:16 -07:00
sell_locktime = 5
buy_locktime = 10 # Must be more than first tx
2017-05-26 15:08:57 -07:00
2017-05-31 12:03:16 -07:00
create_sell_p2sh ( trade , secret , sell_locktime )
2017-05-26 15:08:57 -07:00
userInput . authorize_fund_sell ( trade )
txid = fund_sell_contract ( trade )
print ( " Sent " )
2017-05-31 12:03:16 -07:00
create_buy_p2sh ( trade , secret , buy_locktime )
2017-05-26 15:08:57 -07:00
print_trade ( ' seller ' )
2017-05-23 14:12:01 -07:00
2017-05-22 18:00:34 -07:00
if __name__ == ' __main__ ' :
2017-05-23 12:28:48 -07:00
print ( " ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions) " )
2017-05-23 18:15:05 -07:00
print ( " = " * 50 )
2017-05-26 15:08:57 -07:00
2017-05-22 18:00:34 -07:00
trade = get_trade ( )
2017-05-26 13:15:52 -07:00
if trade == None :
htlcTrade = Trade ( )
print ( " New empty trade " )
else :
buyContract = Contract ( trade [ ' buy ' ] )
sellContract = Contract ( trade [ ' sell ' ] )
htlcTrade = Trade ( buyContract = buyContract , sellContract = sellContract )
2017-05-23 13:34:48 -07:00
try :
2017-05-23 14:12:01 -07:00
if sys . argv [ 1 ] == ' new ' :
erase_trade ( )
role = ' seller '
2017-05-26 13:15:52 -07:00
htlcTrade = Trade ( )
2017-05-23 18:15:05 -07:00
print ( " Creating new XCAT transaction... " )
2017-05-23 14:12:01 -07:00
else :
role = sys . argv [ 1 ]
print ( " Your role in demo: " , role )
2017-05-23 13:34:48 -07:00
except :
if trade == None :
print ( " No active trades available. " )
res = input ( " Would you like to initiate a trade? (y/n) " )
if res == ' y ' :
role = ' seller '
else :
exit ( )
else :
print ( " Trade exists, run script as buyer or seller to complete trade. " )
exit ( )
2017-05-26 13:15:52 -07:00
if htlcTrade . buyContract is not None and htlcTrade . sellContract is not None :
if htlcTrade . sellContract . get_status ( ) == ' redeemed ' and htlcTrade . buyContract . get_status ( ) == ' redeemed ' :
2017-05-23 13:34:48 -07:00
print ( " This trade is already complete! Trade details: " )
pprint ( trade )
exit ( )
if role == " seller " :
2017-05-26 13:15:52 -07:00
if htlcTrade . sellContract == None :
seller_initiate ( htlcTrade )
elif htlcTrade . buyContract . get_status ( ) == ' funded ' :
seller_redeem ( htlcTrade )
elif htlcTrade . buyContract . get_status ( ) == ' empty ' :
2017-05-26 15:08:57 -07:00
print ( " Buyer has not yet funded the contract where you offered to buy {0} , please wait for them to complete their part. " . format ( htlcTrade . buyContract . currency ) )
2017-05-22 18:00:34 -07:00
else :
2017-05-23 14:12:01 -07:00
# Need better way of preventing buyer from having secret
2017-05-26 13:15:52 -07:00
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
if htlcTrade . sellContract . get_status ( ) == ' funded ' and htlcTrade . buyContract . get_status ( ) != ' redeemed ' :
2017-05-23 13:34:48 -07:00
print ( " One active trade available, fulfilling buyer contract... " )
2017-05-26 13:15:52 -07:00
buyer_fulfill ( htlcTrade )
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
# p2sh = trade['buy']['p2sh']
# check_blocks(p2sh)
elif htlcTrade . buyContract . get_status ( ) == ' redeemed ' :
2017-05-23 13:34:48 -07:00
# Seller has redeemed buyer's tx, buyer can now redeem.
2017-05-26 13:15:52 -07:00
buyer_redeem ( htlcTrade )
2017-05-23 14:12:01 -07:00
print ( " XCAT trade complete! " )
2017-05-26 13:15:52 -07:00
# Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently