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 13:15:52 -07:00
from contract import Contract , Trade , Participant
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 )
2017-05-26 13:15:52 -07:00
def get_trade_amounts ( ) :
amounts = { }
sell_currency = input ( " Which currency would you like to trade out of (bitcoin or zcash)? " )
if sell_currency == ' ' :
sell_currency = ' bitcoin '
if sell_currency == ' bitcoin ' :
buy_currency = ' zcash '
2017-05-23 18:15:05 -07:00
else :
2017-05-26 13:15:52 -07:00
buy_currency = ' bitcoin '
print ( sell_currency )
sell_amt = input ( " How much {0} do you want to sell? " . format ( sell_currency ) )
2017-05-23 13:34:48 -07:00
sell_amt = 3.5
print ( sell_amt )
2017-05-26 13:15:52 -07:00
buy_amt = input ( " How much {0} do you want to receive in exchange? " . format ( buy_currency ) )
2017-05-23 13:34:48 -07:00
buy_amt = 1.2
print ( buy_amt )
2017-05-26 13:15:52 -07:00
sell = { ' currency ' : sell_currency , ' amount ' : sell_amt }
buy = { ' currency ' : buy_currency , ' amount ' : buy_amt }
amounts [ ' sell ' ] = sell
amounts [ ' buy ' ] = buy
return amounts
2017-05-22 18:00:34 -07:00
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 13:15:52 -07:00
def create_p2sh ( htlcTrade ) :
currency = htlcTrade . sellContract . currency
2017-05-22 18:00:34 -07:00
secret = input ( " Initiating trade: Enter a password to place the {0} you want to sell in escrow: " . format ( currency ) )
# TODO: hash and store secret only locally.
2017-05-26 09:23:49 -07:00
if secret == ' ' :
secret = generate_password ( )
2017-05-23 13:34:48 -07:00
print ( ' Remember your password: ' , secret )
2017-05-26 13:15:52 -07:00
# Saving secret locally for now
save_secret ( secret )
# TODO: Implement locktimes and mock block passage of time
2017-05-22 18:00:34 -07:00
locktime = 20 # Must be more than first tx
2017-05-26 13:15:52 -07:00
# CREATE SELL CONTRACT
contract = create_htlc ( currency , htlcTrade . sellContract . initiator , htlcTrade . sellContract . fulfiller , secret , locktime )
print ( " sell contract " , contract )
setattr ( htlcTrade . sellContract , ' p2sh ' , contract [ ' p2sh ' ] )
setattr ( htlcTrade . sellContract , ' redeemScript ' , contract [ ' redeemScript ' ] )
setattr ( htlcTrade . sellContract , ' redeemblocknum ' , contract [ ' redeemblocknum ' ] )
print ( ' To complete your sell, send {0} {1} to this p2sh: {2} ' . format ( htlcTrade . sellContract . amount , currency , htlcTrade . sellContract . p2sh ) )
2017-05-22 18:00:34 -07:00
response = input ( " Type ' enter ' to allow this program to send funds on your behalf. " )
print ( " Sent " )
2017-05-26 13:15:52 -07:00
sell_p2sh = htlcTrade . sellContract . p2sh
sell_amt = htlcTrade . sellContract . amount
2017-05-22 18:00:34 -07:00
txid = fund_htlc ( currency , sell_p2sh , sell_amt )
2017-05-26 13:15:52 -07:00
setattr ( htlcTrade . sellContract , ' fund_tx ' , txid )
# trade['sell']['status'] = 'funded'
2017-05-22 18:00:34 -07:00
# TODO: Save secret locally for seller
2017-05-26 13:15:52 -07:00
# setattr(htlcTrade.sellContract, 'secret', secret)
2017-05-22 18:00:34 -07:00
2017-05-26 13:15:52 -07:00
# TODO: How to cache/save trades locally
# save_trade(trade)
2017-05-22 18:00:34 -07:00
2017-05-26 13:15:52 -07:00
## CREATE BUY CONTRACT
buy_currency = htlcTrade . buyContract . currency
buy_initiator = htlcTrade . buyContract . initiator
buy_fulfiller = htlcTrade . buyContract . fulfiller
2017-05-23 13:34:48 -07:00
print ( " Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds... " . format ( buy_currency ) )
2017-05-26 13:15:52 -07:00
print ( " HTLC DETAILS " , buy_currency , buy_fulfiller , buy_initiator , secret , locktime )
2017-05-23 11:22:21 -07:00
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 )
setattr ( htlcTrade . buyContract , ' p2sh ' , buy_contract [ ' p2sh ' ] )
setattr ( htlcTrade . buyContract , ' redeemScript ' , buy_contract [ ' redeemScript ' ] )
setattr ( htlcTrade . buyContract , ' redeemblocknum ' , buy_contract [ ' redeemblocknum ' ] )
print ( " Now contact the buyer and tell them to send funds to this p2sh: " , htlcTrade . buyContract . p2sh )
save ( htlcTrade )
print_trade ( htlcTrade )
def get_initiator_addresses ( ) :
btc_addr = input ( " Enter your bitcoin address: " )
# btc_addr = bXcat.new_bitcoin_addr()
btc_addr = ' myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp '
print ( btc_addr )
zec_addr = input ( " Enter your zcash address: " )
# zec_addr = zXcat.new_zcash_addr()
zec_addr = ' tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ '
print ( zec_addr )
addresses = { ' bitcoin ' : btc_addr , ' zcash ' : zec_addr }
return addresses
# How to be saving the trade to the json file?
# save_trade(trade)
def get_fulfiller_addresses ( ) :
btc_addr = input ( " Enter the bitcoin address of the party you want to trade with: " )
# btc_addr = bXcat.new_bitcoin_addr()
btc_addr = ' mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z '
print ( btc_addr )
zec_addr = input ( " Enter the zcash address of the party you want to trade with: " )
# zec_addr = zXcat.new_zcash_addr()
zec_addr = ' tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY '
print ( zec_addr )
addresses = { ' bitcoin ' : btc_addr , ' zcash ' : zec_addr }
return addresses
def buyer_fulfill ( htlcTrade ) :
buy_p2sh = htlcTrade . buyContract . p2sh
sell_p2sh = htlcTrade . sellContract . p2sh
buy_currency = htlcTrade . buyContract . currency
sell_currency = htlcTrade . sellContract . currency
buy_amount = htlcTrade . buyContract . amount
sell_amount = htlcTrade . sellContract . amount
buy_p2sh_balance = check_p2sh ( buy_currency , buy_p2sh )
sell_p2sh_balance = check_p2sh ( sell_currency , sell_p2sh )
if buy_p2sh_balance == 0 :
input ( " The seller ' s p2sh is funded with {0} {1} , type ' enter ' if this is the amount you want to buy in {1} . " . format ( sell_p2sh_balance , sell_currency ) )
input ( " You have not send funds to the contract to buy {1} (requested amount: {0} ), type ' enter ' to allow this program to send the agreed upon funds on your behalf. " . format ( buy_p2sh_balance , buy_currency ) )
print ( " Buy amt, " , buy_amount )
txid = fund_htlc ( buy_currency , buy_p2sh , buy_amount )
print ( " Fund tx txid: " , txid )
setattr ( htlcTrade . buyContract , ' fund_tx ' , txid )
save ( htlcTrade )
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-22 18:00:34 -07:00
def check_blocks ( p2sh ) :
# blocks = []
with open ( ' watchdata ' , ' r ' ) as infile :
for line in infile :
res = bXcat . search_p2sh ( line . strip ( ' \n ' ) , p2sh )
# blocks.append(line.strip('\n'))
# print(blocks)
# for block in blocks:
# res = bXcat.search_p2sh(block, p2sh)
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 13:15:52 -07:00
# def redeem_p2sh(contract):
# currency = contract['currency']
# if currency == 'bitcoin':
# res = bXcat.redeem(htlcTrade.buyContract)
# else:
# res = zXcat.redeem(htlcTrade.buyContract)
# return res
def seller_redeem ( htlcTrade ) :
buy_currency = htlcTrade . buyContract . currency
buy_amount = htlcTrade . buyContract . amount
buy_p2sh = htlcTrade . buyContract . p2sh
input ( " Buyer funded the contract where you offered to buy {0} , type ' enter ' to redeem {1} {0} from {2} . " . format ( buy_currency , buy_amount , buy_p2sh ) )
if htlcTrade . sellContract . get_status ( ) == ' redeemed ' :
print ( " You already redeemed the funds and acquired {0} {1} " . format ( buy_amount , buy_currency ) )
2017-05-23 13:34:48 -07:00
exit ( )
else :
# Seller redeems buyer's funded tx (contract in p2sh)
2017-05-26 13:15:52 -07:00
secret = input ( " Enter the secret you used to lock the funds in order to redeem: " )
if secret == ' ' :
secret = get_secret ( )
print ( secret )
# txid = redeem_p2sh(htlcTrade.buyContract, secret)
txid = redeem_p2sh ( htlcTrade . buyContract , secret )
setattr ( htlcTrade . buyContract , ' redeem_tx ' , txid )
save ( htlcTrade )
print ( " You have redeemed {0} {1} ! " . format ( buy_amount , buy_currency ) )
print_trade ( ' seller ' )
def buyer_redeem ( htlcTrade ) :
input ( " Seller funded the contract where you paid them in {0} to buy {1} , type ' enter ' to redeem {2} {1} from {3} . " . format ( htlcTrade . buyContract . currency , htlcTrade . sellContract . currency , htlcTrade . sellContract . amount , htlcTrade . sellContract . p2sh ) )
if htlcTrade . sellContract . get_status ( ) == ' redeemed ' :
print ( " You already redeemed the funds and acquired {0} {1} " . format ( htlcTrade . sellContract . amount , htlcTrade . sellContract . currency ) )
exit ( )
2017-05-26 09:23:49 -07:00
else :
2017-05-26 13:15:52 -07:00
# Buyer redeems seller's funded tx
p2sh = htlcTrade . sellContract . p2sh
currency = htlcTrade . sellContract . currency
# Buy contract is where seller disclosed secret in redeeming
if htlcTrade . buyContract . currency == ' bitcoin ' :
secret = bXcat . parse_secret ( htlcTrade . buyContract . redeem_tx )
else :
secret = zXcat . parse_secret ( htlcTrade . buyContract . redeem_tx )
print ( " Found secret in seller ' s redeem tx " , secret )
redeem_tx = redeem_p2sh ( htlcTrade . sellContract , secret )
setattr ( htlcTrade . sellContract , ' redeem_tx ' , redeem_tx )
save ( htlcTrade )
exit ( )
2017-05-26 09:23:49 -07:00
2017-05-26 13:15:52 -07:00
def print_trade ( role ) :
print ( " \n Trade status for {0} : " . format ( role ) )
2017-05-22 18:00:34 -07:00
trade = get_trade ( )
2017-05-26 13:15:52 -07:00
pprint ( trade )
2017-05-26 09:23:49 -07:00
2017-05-26 13:15:52 -07:00
## Main functions determining user workflow
def seller_initiate ( trade ) :
# Collect or negotiate these participants and amounts any way
amounts = get_trade_amounts ( )
sell = amounts [ ' sell ' ]
buy = amounts [ ' buy ' ]
sell_currency = sell [ ' currency ' ]
buy_currency = buy [ ' currency ' ]
init_addrs = get_initiator_addresses ( )
sell [ ' initiator ' ] = init_addrs [ sell_currency ]
buy [ ' initiator ' ] = init_addrs [ buy_currency ]
fulfill_addrs = get_fulfiller_addresses ( )
sell [ ' fulfiller ' ] = fulfill_addrs [ sell_currency ]
buy [ ' fulfiller ' ] = fulfill_addrs [ buy_currency ]
# initializing contract classes with addresses, currencies, and amounts
htlcTrade . sellContract = Contract ( sell )
htlcTrade . buyContract = Contract ( buy )
print ( htlcTrade . sellContract . __dict__ )
print ( htlcTrade . buyContract . __dict__ )
create_p2sh ( htlcTrade )
exit ( )
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-22 18:00:34 -07:00
# TODO: Get trade indicated by id number
2017-05-26 13:15:52 -07:00
# TODO: pass trade into functions?
2017-05-23 00:03:42 -07:00
# TODO: workflow framed as currency you're trading out of being sell. appropriate?
2017-05-26 13:15:52 -07:00
# Have initiator propose amounts to trade
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 )
# print("status", trade.sellContract.get_status())
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 ' :
print ( " Buyer has not yet funded the contract where you offered to buy {0} , please wait for them to complete their part. " . format ( trade [ ' buy ' ] [ ' 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
print ( htlcTrade . buyContract . get_status ( ) )
print ( htlcTrade . sellContract . get_status ( ) )
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