zbxcat/xcat/protocol.py

225 lines
8.0 KiB
Python

import json
import os, sys
from pprint import pprint
from xcat.utils import *
from xcat.trades import Contract, Trade
import xcat.userInput as userInput
import xcat.db as db
from xcat.xcatconf import *
from xcat.bitcoinRPC import bitcoinProxy
from xcat.zcashRPC import zcashProxy
bitcoinRPC = bitcoinProxy()
zcashRPC = zcashProxy()
def is_myaddr(address):
if address[:1] == 'm':
status = bitcoinRPC.validateaddress(address)
else:
status = zcashRPC.validateaddress(address)
status = status['ismine']
# print("Address {0} is mine: {1}".format(address, status))
return status
def find_secret_from_fundtx(currency, p2sh, fundtx):
if currency == 'bitcoin':
secret = bitcoinRPC.find_secret(p2sh, fundtx)
else:
secret = zcashRPC.find_secret(p2sh, fundtx)
return secret
def import_addrs(trade):
check_fund_status(trade.sell.currency, trade.sell.p2sh)
check_fund_status(trade.buy.currency, trade.buy.p2sh)
def check_p2sh(currency, address):
if currency == 'bitcoin':
print("Checking funds in Bitcoin p2sh")
return bitcoinRPC.check_funds(address)
else:
print("Checking funds in Zcash p2sh")
return zcashRPC.check_funds(address)
def check_fund_status(currency, address):
if currency == 'bitcoin':
print("Checking funds in Bitcoin p2sh")
return bitcoinRPC.get_fund_status(address)
else:
print("Checking funds in Zcash p2sh")
return zcashRPC.get_fund_status(address)
# TODO: function to calculate appropriate locktimes between chains
# def verify_p2sh(trade):
# htlc = 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(currency, funder, redeemer, commitment, locktime):
if currency == 'bitcoin':
sell_p2sh = bitcoinRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
else:
sell_p2sh = zcashRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
return sell_p2sh
def fund_htlc(currency, p2sh, amount):
if currency == 'bitcoin':
txid = bitcoinRPC.fund_htlc(p2sh, amount)
else:
txid = zcashRPC.fund_htlc(p2sh, amount)
return txid
def fund_contract(contract):
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
return txid
def fund_sell_contract(trade):
sell = trade.sell
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
setattr(trade.sell, 'fund_tx', txid)
save(trade)
return txid
def create_sell_p2sh(trade, commitment, locktime):
# CREATE SELL CONTRACT
sell = trade.sell
contract = 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'])
save(trade)
def create_buy_p2sh(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 = 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))
save(trade)
def redeem_p2sh(contract, secret):
currency = contract.currency
if currency == 'bitcoin':
res = bitcoinRPC.redeem_contract(contract, secret)
else:
res = zcashRPC.redeem_contract(contract, secret)
return res
def parse_secret(chain, txid):
if chain == 'bitcoin':
secret = bitcoinRPC.parse_secret(txid)
else:
secret = zcashRPC.parse_secret(txid)
return secret
#### Main functions determining user flow from command line
def buyer_redeem(trade):
userInput.authorize_buyer_redeem(trade)
if trade.sell.get_status() == 'redeemed':
print("You already redeemed the funds and acquired {0} {1}".format(trade.sell.amount, trade.sell.currency))
exit()
else:
# Buyer redeems seller's funded tx
p2sh = trade.sell.p2sh
currency = trade.sell.currency
# Buy contract is where seller disclosed secret in redeeming
if trade.buy.currency == 'bitcoin':
secret = bitcoinRPC.parse_secret(trade.buy.redeem_tx)
else:
secret = zcashRPC.parse_secret(trade.buy.redeem_tx)
print("Found secret in seller's redeem tx", secret)
redeem_tx = redeem_p2sh(trade.sell, secret)
setattr(trade.sell, 'redeem_tx', redeem_tx)
save(trade)
exit()
def seller_redeem_p2sh(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 = redeem_p2sh(trade.buy, secret)
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
return txs
def buyer_fulfill(trade):
buy = trade.buy
sell = trade.sell
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 initialize_trade(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:
init_addrs = ADDRS[conf]['initiator']
fulfill_addrs = ADDRS[conf]['fulfiller']
amounts = ADDRS[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
def seller_init(tradeid, trade):
secret = generate_password()
db.save_secret(tradeid, secret)
print("\nGenerated a secret preimage to lock funds. This will only be stored locally: ", secret)
hash_of_secret = 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
create_sell_p2sh(trade, hash_of_secret, sell_locktime)
create_buy_p2sh(trade, hash_of_secret, buy_locktime)
trade.commitment = b2x(hash_of_secret)
print("TRADE after seller init", trade.toJSON())
return trade