From e78072d0aa2e7dc8a7f2ed1492b96bbe4642a5f7 Mon Sep 17 00:00:00 2001 From: Jay Graber Date: Wed, 2 Aug 2017 18:06:09 -0700 Subject: [PATCH] Save secret in key/value store --- xcat/cli.py | 45 +++++++++++++++++++++++++-------------------- xcat/db.py | 18 +++++++++++++++--- xcat/protocol.py | 10 +++++++--- xcat/userInput.py | 18 +----------------- xcat/utils.py | 33 ++++++++++++++------------------- xcat/xcat.json | 1 - 6 files changed, 62 insertions(+), 63 deletions(-) delete mode 100644 xcat/xcat.json diff --git a/xcat/cli.py b/xcat/cli.py index 5b81258..33d8d0f 100644 --- a/xcat/cli.py +++ b/xcat/cli.py @@ -18,14 +18,14 @@ def checkSellStatus(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) @@ -50,7 +50,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,14 +69,16 @@ 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': @@ -132,11 +137,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 +173,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 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 +202,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 +231,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) diff --git a/xcat/db.py b/xcat/db.py index 11f76f4..04fcaf1 100644 --- a/xcat/db.py +++ b/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') diff --git a/xcat/protocol.py b/xcat/protocol.py index d0dff54..9888dca 100644 --- a/xcat/protocol.py +++ b/xcat/protocol.py @@ -6,6 +6,7 @@ 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 def find_secret_from_fundtx(currency, p2sh, fundtx): print("Fund tx in protocol.py", fundtx) @@ -162,7 +163,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 +185,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 diff --git a/xcat/userInput.py b/xcat/userInput.py index fcc75ba..07baa18 100644 --- a/xcat/userInput.py +++ b/xcat/userInput.py @@ -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.") diff --git a/xcat/utils.py b/xcat/utils.py index 22be5b0..372bc14 100644 --- a/xcat/utils.py +++ b/xcat/utils.py @@ -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): diff --git a/xcat/xcat.json b/xcat/xcat.json deleted file mode 100644 index 8bbb213..0000000 --- a/xcat/xcat.json +++ /dev/null @@ -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"}} \ No newline at end of file