commit
905124f03c
66
xcat/cli.py
66
xcat/cli.py
|
@ -1,12 +1,9 @@
|
|||
import argparse, textwrap
|
||||
from xcat.utils import *
|
||||
import xcat.db as db
|
||||
import xcat.bitcoinRPC
|
||||
import xcat.zcashRPC
|
||||
import xcat.userInput as userInput
|
||||
from xcat.trades import *
|
||||
from xcat.protocol import *
|
||||
import ast
|
||||
import subprocess
|
||||
|
||||
def save_state(trade, tradeid):
|
||||
|
@ -17,22 +14,22 @@ def checkSellStatus(tradeid):
|
|||
trade = db.get(tradeid)
|
||||
status = seller_check_status(trade)
|
||||
print("In checkSellStatus", status)
|
||||
# if trade.buy.get_status() == 'funded':
|
||||
if status == 'initFund':
|
||||
if status == 'init':
|
||||
userInput.authorize_fund_sell(trade)
|
||||
fund_tx = fund_sell_contract(trade)
|
||||
print("Sent fund_tx", fund_tx)
|
||||
trade.sell.fund_tx = fund_tx
|
||||
save_state(trade, tradeid)
|
||||
elif status == 'buyerFunded':
|
||||
secret = userInput.retrieve_password()
|
||||
secret = db.get_secret(tradeid)
|
||||
print("SECRET found in checksellactions", secret)
|
||||
txs = seller_redeem_p2sh(trade, secret)
|
||||
print("TXS IN SELLER REDEEM BUYER TX", txs)
|
||||
trade.buy.redeem_tx = txs['redeem_tx']
|
||||
print("TRADE SUCCESSFULLY REDEEMED", trade)
|
||||
save_state(trade, tradeid)
|
||||
# elif trade.buy.get_status() == 'empty':
|
||||
# Remove from db? Or just from temporary file storage
|
||||
cleanup(tradeid)
|
||||
elif status == 'sellerFunded':
|
||||
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))
|
||||
# elif trade.buy.get_status() == 'redeemed':
|
||||
|
@ -50,7 +47,10 @@ def buyer_check_status(trade):
|
|||
elif sellState == 'funded' and buyState == 'funded':
|
||||
return 'buyerFunded' # step2
|
||||
elif sellState == 'empty' and buyState == 'empty':
|
||||
return 'buyerRedeemed' # step4
|
||||
if hasattr(trade.sell, 'redeem_tx'):
|
||||
return 'buyerRedeemed' # step4
|
||||
else:
|
||||
return 'init'
|
||||
|
||||
def seller_check_status(trade):
|
||||
sellState = check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
||||
|
@ -66,40 +66,32 @@ def seller_check_status(trade):
|
|||
if hasattr(trade.buy, 'redeem_tx'):
|
||||
return 'buyerRedeemed' # step4
|
||||
else:
|
||||
return 'initFund' # step0
|
||||
return 'init' # step0
|
||||
|
||||
# TODO: function to calculate appropriate locktimes between chains
|
||||
def checkBuyStatus(tradeid):
|
||||
trade = db.get(tradeid)
|
||||
status = buyer_check_status(trade)
|
||||
print("In checkBuyStatus", status)
|
||||
if status == 'buyerRedeemed':
|
||||
if status == 'init':
|
||||
print("Trade has not yet started, waiting for seller to fund the sell p2sh.")
|
||||
elif status == 'buyerRedeemed':
|
||||
print("This trade is complete, both sides redeemed.")
|
||||
# elif trade.sell.get_status() == 'funded' and trade.buy.get_status() != 'redeemed':
|
||||
elif status == 'sellerFunded':
|
||||
print("One active trade available, fulfilling buyer contract...")
|
||||
print("Trade commitment", trade.commitment)
|
||||
# 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:
|
||||
# if verify_p2sh(trade):
|
||||
fund_tx = fund_contract(trade.buy)
|
||||
print("Fund tx coming back in cli", fund_tx)
|
||||
print("\nBuyer's funding tx: ", fund_tx)
|
||||
trade.buy.fund_tx = fund_tx
|
||||
save_state(trade, tradeid)
|
||||
# else:
|
||||
# print("Compiled p2sh for htlc does not match what seller sent.")
|
||||
elif status == 'sellerRedeemed':
|
||||
print("FUND TX CLI", trade.buy.fund_tx)
|
||||
secret = find_secret_from_fundtx(trade.buy.currency, trade.buy.p2sh, trade.buy.fund_tx)
|
||||
print("Secret in cli", secret)
|
||||
# secret = parse_secret(trade.buy.currency, trade.buy.redeem_tx)
|
||||
if secret != None:
|
||||
print("Found secret", secret)
|
||||
txs = redeem_p2sh(trade.sell, secret)
|
||||
print("TXS IN SELLER REDEEMED", txs)
|
||||
# trade.sell.fund_tx = txs['fund_tx']
|
||||
trade.sell.redeem_tx = txs['redeem_tx']
|
||||
print("TXID after buyer redeem", trade.sell.redeem_tx)
|
||||
save_state(trade, tradeid)
|
||||
|
@ -132,11 +124,12 @@ def exporttrade(tradeid, wormhole=False):
|
|||
trade = db.get(tradeid)
|
||||
hexstr = s2x(trade.toJSON())
|
||||
if wormhole:
|
||||
tradefile = os.path.join(root_dir, '.tmp/{0}'.format(tradeid))
|
||||
tradefile = os.path.join(ROOT_DIR, '.tmp/{0}'.format(tradeid))
|
||||
print(tradefile)
|
||||
with open(tradefile, '+w') as outfile:
|
||||
outfile.write(hexstr)
|
||||
print("Exporting trade to buyer using magic wormhole.")
|
||||
subprocess.call('wormhole', 'send', tradefile)
|
||||
subprocess.call('wormhole send {0}'.format(tradefile), shell=True)
|
||||
else:
|
||||
print(hexstr)
|
||||
return hexstr
|
||||
|
@ -167,12 +160,11 @@ def newtrade(tradeid):
|
|||
erase_trade()
|
||||
role = 'seller'
|
||||
print("Creating new XCAT trade...")
|
||||
trade = seller_init(Trade())
|
||||
trade = seller_init(tradeid)
|
||||
print("Use 'xcat exporttrade <tradeid> to export the trade and sent to the buyer.'")
|
||||
save_state(trade, tradeid)
|
||||
|
||||
def main():
|
||||
root_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
|
||||
description=textwrap.dedent('''\
|
||||
== Trades ==
|
||||
|
@ -197,26 +189,26 @@ def main():
|
|||
if args.wormhole:
|
||||
wormhole_importtrade()
|
||||
else:
|
||||
if len(args.argument) != 2:
|
||||
if len(args.arguments) != 2:
|
||||
print("Usage: importtrade [tradeid] [hexstring]")
|
||||
exit()
|
||||
tradeid = args.argument[0]
|
||||
hexstr = args.argument[1]
|
||||
tradeid = args.arguments[0]
|
||||
hexstr = args.arguments[1]
|
||||
importtrade(tradeid, hexstr)
|
||||
elif command == 'exporttrade':
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
exporttrade(tradeid, args.wormhole)
|
||||
elif command == "findtrade":
|
||||
print("Finding trade")
|
||||
key = args.argument[0]
|
||||
key = args.arguments[0]
|
||||
findtrade(key)
|
||||
elif command == 'checktrade':
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
checktrade(tradeid)
|
||||
elif command == 'newtrade':
|
||||
print("in new trade")
|
||||
try:
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
newtrade(tradeid)
|
||||
except:
|
||||
tradeid = userInput.enter_trade_id()
|
||||
|
@ -226,16 +218,16 @@ def main():
|
|||
print("Run as daemon process")
|
||||
# Ad hoc testing of workflow starts here
|
||||
elif command == "step1":
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
checkSellStatus(tradeid)
|
||||
elif command == "step2":
|
||||
# trade = get_trade()
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
elif command == "step3":
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
checkSellStatus(tradeid)
|
||||
# TODO: When trade finishes, delete wormhole file in tmp dir.
|
||||
elif command == "step4":
|
||||
tradeid = args.argument[0]
|
||||
tradeid = args.arguments[0]
|
||||
checkBuyStatus(tradeid)
|
||||
|
|
18
xcat/db.py
18
xcat/db.py
|
@ -8,7 +8,8 @@ from xcat.trades import *
|
|||
|
||||
import xcat.bitcoinRPC as bitcoinRPC
|
||||
|
||||
db = plyvel.DB('/tmp/testdb', create_if_missing=True)
|
||||
db = plyvel.DB('/tmp/xcatDB', create_if_missing=True)
|
||||
preimageDB = plyvel.DB('/tmp/preimageDB', create_if_missing=True)
|
||||
|
||||
# Takes dict or obj, saves json str as bytes
|
||||
def create(trade, tradeid):
|
||||
|
@ -26,8 +27,8 @@ def createByFundtx(trade):
|
|||
txid = jt['sell']['fund_tx']
|
||||
db.put(b(txid), b(trade))
|
||||
|
||||
def get(txid):
|
||||
rawtrade = db.get(b(txid))
|
||||
def get(tradeid):
|
||||
rawtrade = db.get(b(tradeid))
|
||||
tradestr = str(rawtrade, 'utf-8')
|
||||
trade = instantiate(tradestr)
|
||||
return trade
|
||||
|
@ -38,7 +39,18 @@ def instantiate(trade):
|
|||
trade = Trade(buy=Contract(tradestr['buy']), sell=Contract(tradestr['sell']), commitment=tradestr['commitment'])
|
||||
return trade
|
||||
|
||||
#############################################
|
||||
###### Preimages stored by tradeid ##########
|
||||
#############################################
|
||||
|
||||
# Stores secret locally in key/value store by tradeid
|
||||
def save_secret(tradeid, secret):
|
||||
res = preimageDB.put(b(tradeid), b(secret))
|
||||
|
||||
def get_secret(tradeid):
|
||||
secret = preimageDB.get(b(tradeid))
|
||||
secret = str(secret, 'utf-8')
|
||||
return secret
|
||||
|
||||
# db.delete(b'hello')
|
||||
# testtrade = get('test')
|
||||
|
|
|
@ -6,6 +6,8 @@ import xcat.bitcoinRPC as bitcoinRPC
|
|||
from xcat.utils import *
|
||||
from xcat.trades import Contract, Trade
|
||||
import xcat.userInput as userInput
|
||||
import xcat.db as db
|
||||
from xcatconf import *
|
||||
|
||||
def find_secret_from_fundtx(currency, p2sh, fundtx):
|
||||
print("Fund tx in protocol.py", fundtx)
|
||||
|
@ -35,6 +37,16 @@ def check_fund_status(currency, address):
|
|||
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):
|
||||
print("Commitment in create_htlc", commitment)
|
||||
if currency == 'bitcoin':
|
||||
|
@ -48,15 +60,8 @@ def fund_htlc(currency, p2sh, amount):
|
|||
txid = bitcoinRPC.fund_htlc(p2sh, amount)
|
||||
else:
|
||||
txid = zcashRPC.fund_htlc(p2sh, amount)
|
||||
print("fund_htlc txid", txid )
|
||||
print("Fund_htlc txid", txid )
|
||||
return txid
|
||||
#
|
||||
# def fund_buy_contract(trade):
|
||||
# buy = trade.buy
|
||||
# txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
||||
# setattr(trade.buy, 'fund_tx', txid)
|
||||
# save(trade)
|
||||
# return txid
|
||||
|
||||
def fund_contract(contract):
|
||||
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
|
||||
|
@ -162,7 +167,8 @@ def buyer_fulfill(trade):
|
|||
print("Please wait for the seller to remove your funds from escrow to complete the trade.")
|
||||
print_trade('buyer')
|
||||
|
||||
def seller_init(trade):
|
||||
def seller_init(tradeid):
|
||||
trade = Trade()
|
||||
# TODO: pass in amounts, or get from cli. {"amounts": {"buy": {}, "sell": {}}}
|
||||
amounts = userInput.get_trade_amounts()
|
||||
sell = amounts['sell']
|
||||
|
@ -183,8 +189,10 @@ def seller_init(trade):
|
|||
print(trade.sell.__dict__)
|
||||
print(trade.buy.__dict__)
|
||||
|
||||
secret = userInput.create_password()
|
||||
save_secret(secret)
|
||||
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
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
from xcat.utils import *
|
||||
from xcat.db import *
|
||||
|
||||
def enter_trade_id():
|
||||
tradeid = input("Enter a unique identifier for this trade: ")
|
||||
|
@ -26,23 +27,6 @@ def get_trade_amounts():
|
|||
amounts['buy'] = buy
|
||||
return amounts
|
||||
|
||||
def create_password():
|
||||
secret = input("Initiating trade: Create a password to place the funds in escrow: ")
|
||||
# TODO: hash and store secret only locally.
|
||||
if secret == '':
|
||||
secret = generate_password()
|
||||
print('Remember your password:', secret)
|
||||
# Saving secret locally for now
|
||||
save_secret(secret)
|
||||
return secret
|
||||
|
||||
def retrieve_password():
|
||||
secret = input("Enter the secret you used to lock the funds in order to redeem:")
|
||||
if secret == '':
|
||||
secret = get_secret()
|
||||
print(secret)
|
||||
return secret
|
||||
|
||||
def authorize_fund_sell(htlcTrade):
|
||||
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sell.amount, htlcTrade.sell.currency, htlcTrade.sell.p2sh))
|
||||
response = input("Type 'enter' to allow this program to send funds on your behalf.")
|
||||
|
|
|
@ -2,6 +2,9 @@ import hashlib, json, random, binascii
|
|||
import xcat.trades as trades
|
||||
import xcat.bitcoinRPC as bitcoinRPC
|
||||
import xcat.zcashRPC as zcashRPC
|
||||
import os
|
||||
|
||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||
|
||||
############################################
|
||||
########### Data conversion utils ##########
|
||||
|
@ -63,38 +66,30 @@ def is_myaddr(address):
|
|||
########### Preimage utils #################
|
||||
############################################
|
||||
|
||||
def generate_password():
|
||||
s = "1234567890abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
passlen = 32
|
||||
p = "".join(random.sample(s,passlen))
|
||||
return p
|
||||
|
||||
def sha256(secret):
|
||||
preimage = secret.encode('utf8')
|
||||
h = hashlib.sha256(preimage).digest()
|
||||
return h
|
||||
|
||||
def generate_password():
|
||||
s = "abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
passlen = 8
|
||||
p = "".join(random.sample(s,passlen))
|
||||
return p
|
||||
|
||||
# caching the secret locally for now...
|
||||
def get_secret():
|
||||
with open('xcat/secret.json') as data_file:
|
||||
for line in data_file:
|
||||
return line.strip('\n')
|
||||
|
||||
def save_secret(secret):
|
||||
with open('xcat/secret.json', 'w+') as outfile:
|
||||
outfile.write(secret)
|
||||
|
||||
#############################################
|
||||
######### xcat.json temp file #############
|
||||
#############################################
|
||||
|
||||
xcatjson = os.path.join(ROOT_DIR, '.tmp/xcat.json')
|
||||
|
||||
def save_trade(trade):
|
||||
print("Trade in save_trade", trade)
|
||||
with open('xcat/xcat.json', 'w+') as outfile:
|
||||
with open(xcatjson, 'w+') as outfile:
|
||||
json.dump(trade, outfile)
|
||||
|
||||
def get_trade():
|
||||
with open('xcat/xcat.json') as data_file:
|
||||
with open(xcatjson) as data_file:
|
||||
xcatdb = json.load(data_file)
|
||||
sell = trades.Contract(xcatdb['sell'])
|
||||
buy = trades.Contract(xcatdb['buy'])
|
||||
|
@ -102,7 +97,7 @@ def get_trade():
|
|||
return trade
|
||||
|
||||
def erase_trade():
|
||||
with open('xcat.json', 'w') as outfile:
|
||||
with open(xcatjson, 'w') as outfile:
|
||||
outfile.write('')
|
||||
|
||||
def save(trade):
|
||||
|
@ -113,3 +108,10 @@ def save(trade):
|
|||
'commitment': trade.commitment
|
||||
}
|
||||
save_trade(trade)
|
||||
|
||||
# Remove tmp files when trade is complete
|
||||
def cleanup(tradeid):
|
||||
try:
|
||||
os.remove(os.path.join(ROOT_DIR, '.tmp/{0}'.format(tradeid)))
|
||||
except:
|
||||
pass
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
{"commitment": "ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd", "buy": {"redeemblocknum": 133, "p2sh": "t2PBPZSfLa9jBm2XyXtBF4GxnV65zdor9Q3", "locktime": 10, "fund_tx": "3b8ddbda1204f92d338e719772804395de0e9c8baaf50535edfa2a24ee4f22f2", "amount": 0.02, "redeem_tx": "549b73694a435f5db79e6dd21d39f9ac60cfbdf8ca15ed96f20cc5e66fb63e21", "currency": "zcash", "redeemScript": "63a820ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd8876a91465741c273525f5f4622d4b22b47168dab3055dc167028500b17576a914c04b595fc34f553ee756f9d9ad824462e75f4bfc6888ac", "initiator": "tmJxng1U3EMJaTRSDFT2SZZxCmXdJ7NRhZv", "fulfiller": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"}, "sell": {"redeemblocknum": 1081, "p2sh": "2NAycoLt43cPiXobAbASMkS6MwTWGpHbhio", "fund_tx": "e00144584303af28028bd51053bd67ae2cc085461b84bf4ee3fda30d14690763", "amount": 0.01, "redeem_tx": "57c4997351a588fd7694648aa5e7a6326cd83a4195e3f843340918334ed32d3f", "currency": "bitcoin", "redeemScript": "63a820ecd72edc27561378233ce2208358238fbd329f330bdd37596ff333a6a01323cd8876a914a581a5faa98ffb1cbe3ee75e1b4945ff18ec231e67023904b17576a914208462e3b373cf9e2b0b16e0d59eeb066f4873856888ac", "initiator": "miUtWZ2n71X3yahNu52eradR8vJxQRGw3Z", "fulfiller": "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b"}}
|
Loading…
Reference in New Issue