zbxcat/xcat.py

330 lines
13 KiB
Python
Raw Normal View History

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.
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()
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 13:15:52 -07:00
def print_trade(role):
print("\nTrade 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 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