zbxcat/xcat.py

276 lines
11 KiB
Python

import zXcat
import bXcat
from utils import *
from waiting import *
from time import sleep
import json
import os, sys
from pprint import pprint
from trades import Contract, Trade
import userInput
def check_p2sh(currency, address):
if currency == 'bitcoin':
print("Checking funds in Bitcoin p2sh")
return bXcat.check_funds(address)
else:
print("Checking funds in Zcash p2sh")
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
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
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
def create_sell_p2sh(trade, secret, locktime):
# CREATE SELL CONTRACT
sell = trade.sellContract
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
print("sell contract", contract)
setattr(trade.sellContract, 'p2sh', contract['p2sh'])
setattr(trade.sellContract, 'redeemScript', contract['redeemScript'])
setattr(trade.sellContract, 'redeemblocknum', contract['redeemblocknum'])
save(trade)
def create_buy_p2sh(trade, secret, locktime):
## CREATE BUY CONTRACT
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)
print("Buy contract", buy_contract)
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)
save(trade)
# we try to redeem contract with secret
# we try to redeem revertcontract with time lock
# returns True if at least one redeem succeeded
def redeem_p2sh(contract, secret, revertcontract):
currency = contract.currency
res = False
revert_currency = revertcontract.currency
if (currency == 'bitcoin' and bXcat.still_locked(contract)):
print("trying to redeem btc with secret:")
try:
res = bXcat.redeem_with_secret(contract, secret)
except Exception:
print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
if (currency == 'zcash' and zXcat.still_locked(contract)):
print("trying to redeeming zec with secret:")
try:
res = zXcat.redeem_with_secret(contract, secret)
except Exception:
print("Failed - you might not have the correct secret - perhaps because the seller has not redeemed the buy contract correctly")
if(res): print("You have redeemed {0} {1}!".format(contract.amount, contract.currency))
if (revert_currency == 'bitcoin'):
if(bXcat.still_locked(revertcontract)):
print('too early for redeeminng with time lock on btc chain')
else:
print("trying to redeeming btc with timelock:")
try:
res = bXcat.redeem_after_timelock(revertcontract)
except Exception:
print("Failed - the other party might have redeemed the fund tx on the btc chain with the secret by now")
if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))
if (revert_currency == 'zcash'):
if(zXcat.still_locked(revertcontract)):
print('too early for redeeminng with time lock on zcash chain')
else:
print("trying to redeeming zec with timelock:")
try:
res = zXcat.redeem_after_timelock(revertcontract)
except Exception:
print("Failed - the other party might have redeemed the fund tx on the zcash chain with the secret by now")
if(res): print("You have redeemed {0} {1}!".format(revertcontract.amount, revertcontract.currency))
return res
def print_trade(role):
print("\nTrade status for {0}:".format(role))
trade = get_trade()
pprint(trade)
# #### 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))
# exit()
# else:
# # Buyer redeems seller's funded tx
# p2sh = trade.sellContract.p2sh
# currency = trade.sellContract.currency
# # Buy contract is where seller disclosed secret in redeeming
# if trade.buyContract.currency == 'bitcoin':
# secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
# else:
# secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
# print("Found secret in seller's redeem tx", secret)
# redeem_tx = redeem_p2sh(trade.sellContract, secret)
# setattr(trade.sellContract, 'redeem_tx', redeem_tx)
# save(trade)
# exit()
# def seller_redeem(trade):
# buy = trade.buyContract
# userInput.authorize_seller_redeem(buy)
# 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()
# tx_type, txid = redeem_p2sh(trade.buyContract, secret)
# setattr(trade.buyContract, tx_type, txid)
# 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')
def seller_initiate(trade):
# Get amounts
amounts = userInput.get_trade_amounts()
sell = amounts['sell']
buy = amounts['buy']
sell_currency = sell['currency']
buy_currency = buy['currency']
# Get addresses
init_addrs = userInput.get_initiator_addresses()
sell['initiator'] = init_addrs[sell_currency]
buy['initiator'] = init_addrs[buy_currency]
fulfill_addrs = userInput.get_fulfiller_addresses()
sell['fulfiller'] = fulfill_addrs[sell_currency]
buy['fulfiller'] = fulfill_addrs[buy_currency]
# initializing contract classes with addresses, currencies, and amounts
trade.sellContract = Contract(sell)
trade.buyContract = Contract(buy)
print(trade.sellContract.__dict__)
print(trade.buyContract.__dict__)
secret = userInput.create_password()
# TODO: Implement locktimes and mock block passage of time
sell_locktime = 5
buy_locktime = 10 # Must be more than first tx
create_sell_p2sh(trade, secret, sell_locktime)
userInput.authorize_fund_sell(trade)
txid = fund_sell_contract(trade)
print("Sent")
create_buy_p2sh(trade, secret, buy_locktime)
print_trade('seller')
if __name__ == '__main__':
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
print("=" * 50)
trade = get_trade()
if trade == None:
htlcTrade = Trade()
print("New empty trade")
else:
buyContract = Contract(trade['buy'])
sellContract = Contract(trade['sell'])
htlcTrade = Trade(buyContract=buyContract, sellContract=sellContract)
try:
if sys.argv[1] == 'new':
erase_trade()
role = 'seller'
htlcTrade = Trade()
print("Creating new XCAT transaction...")
else:
role = sys.argv[1]
print("Your role in demo:", role)
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()
if htlcTrade.buyContract is not None and htlcTrade.sellContract is not None:
if htlcTrade.sellContract.get_status() == 'redeemed' and htlcTrade.buyContract.get_status() == 'redeemed':
print("This trade is already complete! Trade details:")
pprint(trade)
exit()
if role == "seller":
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(htlcTrade.buyContract.currency))
else:
# Need better way of preventing buyer from having secret
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
if htlcTrade.sellContract.get_status() == 'funded' and htlcTrade.buyContract.get_status() != 'redeemed':
print("One active trade available, fulfilling buyer contract...")
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':
# Seller has redeemed buyer's tx, buyer can now redeem.
buyer_redeem(htlcTrade)
print("XCAT trade complete!")
# Note: there is some little endian weirdness in the bXcat and zXcat files, need to handle the endianness of txids better & more consistently