zbxcat/xcat/cli.py

383 lines
13 KiB
Python
Raw Normal View History

import argparse
import textwrap
import subprocess
2017-09-04 16:43:13 -07:00
import os
2017-09-12 21:16:42 -07:00
import logging
2017-09-12 22:28:36 -07:00
from xcat.db import DB
2017-07-31 13:51:54 -07:00
import xcat.userInput as userInput
2017-09-04 16:43:13 -07:00
import xcat.utils as utils
from xcat.protocol import Protocol
from xcat.trades import Trade
2017-09-22 19:28:44 -07:00
MODE = {'auto': False}
2017-07-28 17:23:32 -07:00
def save_state(trade, tradeid):
2017-09-12 22:28:36 -07:00
db = DB()
2017-09-04 16:43:13 -07:00
utils.save(trade)
2017-07-28 17:23:32 -07:00
db.create(trade, tradeid)
2017-07-31 15:15:22 -07:00
def checkSellStatus(tradeid):
2017-09-12 22:28:36 -07:00
db = DB()
protocol = Protocol()
2017-09-12 22:28:36 -07:00
2017-07-31 15:15:22 -07:00
trade = db.get(tradeid)
status = seller_check_status(trade)
2017-08-02 21:41:55 -07:00
print("Trade status: {0}\n".format(status))
2017-09-12 22:28:36 -07:00
2017-08-02 18:06:09 -07:00
if status == 'init':
2017-09-22 19:28:44 -07:00
<<<<<<< HEAD
userInput.authorize_fund_sell(trade)
fund_tx = protocol.fund_sell_contract(trade)
2017-09-22 19:28:44 -07:00
=======
if MODE['auto'] == False:
userInput.authorize_fund_sell(trade)
fund_tx = fund_sell_contract(trade)
>>>>>>> Add auto mode to skip user input
print("Sent fund_tx", fund_tx)
trade.sell.fund_tx = fund_tx
save_state(trade, tradeid)
elif status == 'buyerFunded':
2017-08-02 18:06:09 -07:00
secret = db.get_secret(tradeid)
2017-09-22 19:28:44 -07:00
<<<<<<< HEAD
print("Retrieved secret to redeem funds for "
"{0}: {1}".format(tradeid, secret))
txs = protocol.seller_redeem_p2sh(trade, secret)
2017-09-22 19:28:44 -07:00
=======
print("Retrieved secret to redeem funds for {0}: {1}".format(tradeid, secret))
if MODE['auto'] == False:
userInput.authorize_seller_redeem(buy)
txs = seller_redeem_p2sh(trade, secret)
>>>>>>> Add auto mode to skip user input
2017-08-25 15:22:38 -07:00
if 'redeem_tx' in txs:
trade.buy.redeem_tx = txs['redeem_tx']
print("Redeem tx: ", txs['redeem_tx'])
2017-08-30 12:14:42 -07:00
if 'refund_tx' in txs:
2017-08-25 15:22:38 -07:00
trade.buy.redeem_tx = txs['refund_tx']
2017-08-30 12:14:42 -07:00
print("Buyer refund tx: ", txs['refund_tx'])
2017-09-12 21:16:42 -07:00
txs = protocol.refund_contract(trade.sell) # Refund to seller
2017-08-30 12:14:42 -07:00
print("Your refund txid: ", txs['refund_tx'])
2017-07-31 15:15:22 -07:00
save_state(trade, tradeid)
2017-08-02 18:45:36 -07:00
# Remove from db? Or just from temporary file storage
2017-09-04 16:43:13 -07:00
utils.cleanup(tradeid)
elif status == 'sellerFunded':
2017-09-04 16:43:13 -07:00
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 status == 'sellerRedeemed':
2017-09-04 16:43:13 -07:00
print("You have already redeemed the p2sh on the second chain of "
"this trade.")
2017-07-26 13:23:12 -07:00
def buyer_check_status(trade):
protocol = Protocol()
2017-09-04 16:43:13 -07:00
sellState = protocol.check_fund_status(
trade.sell.currency, trade.sell.p2sh)
buyState = protocol.check_fund_status(
trade.buy.currency, trade.buy.p2sh)
if sellState == 'funded' and buyState == 'empty':
2017-09-04 16:43:13 -07:00
return 'sellerFunded' # step1
# TODO: Find funding txid. How does buyer get seller redeemed tx?
elif sellState == 'funded' and hasattr(trade.buy, 'fund_tx'):
2017-09-04 16:43:13 -07:00
return 'sellerRedeemed' # step3
elif sellState == 'funded' and buyState == 'funded':
2017-09-04 16:43:13 -07:00
return 'buyerFunded' # step2
elif sellState == 'empty' and buyState == 'empty':
2017-08-02 18:06:09 -07:00
if hasattr(trade.sell, 'redeem_tx'):
2017-09-04 16:43:13 -07:00
return 'buyerRedeemed' # step4
2017-08-02 18:06:09 -07:00
else:
return 'init'
def seller_check_status(trade):
protocol = Protocol()
2017-09-04 16:43:13 -07:00
sellState = protocol.check_fund_status(
trade.sell.currency, trade.sell.p2sh)
buyState = protocol.check_fund_status(
trade.buy.currency, trade.buy.p2sh)
if sellState == 'funded' and buyState == 'empty':
2017-09-04 16:43:13 -07:00
return 'sellerFunded' # step1
elif sellState == 'funded' and hasattr(trade.buy, 'redeem_tx'):
2017-09-04 16:43:13 -07:00
return 'sellerRedeemed' # step3
# TODO: How does seller get buyer funded tx?
elif sellState == 'funded' and buyState == 'funded':
2017-09-04 16:43:13 -07:00
return 'buyerFunded' # step2
elif sellState == 'empty' and buyState == 'empty':
if hasattr(trade.buy, 'redeem_tx'):
2017-09-04 16:43:13 -07:00
return 'buyerRedeemed' # step4
else:
2017-09-04 16:43:13 -07:00
return 'init' # step0
2017-07-31 15:15:22 -07:00
def checkBuyStatus(tradeid):
2017-09-12 22:28:36 -07:00
db = DB()
protocol = Protocol()
2017-07-31 15:15:22 -07:00
trade = db.get(tradeid)
status = buyer_check_status(trade)
2017-08-02 21:41:55 -07:00
print("Trade status: {0}\n".format(status))
2017-08-02 18:06:09 -07:00
if status == 'init':
2017-09-04 16:43:13 -07:00
print("Trade has not yet started, waiting for seller to fund the "
"sell p2sh.")
2017-08-02 18:06:09 -07:00
elif status == 'buyerRedeemed':
2017-07-31 16:25:49 -07:00
print("This trade is complete, both sides redeemed.")
elif status == 'sellerFunded':
print("One active trade available, fulfilling buyer contract...")
2017-09-22 19:28:44 -07:00
<<<<<<< HEAD
2017-09-04 16:43:13 -07:00
input("Type 'enter' to allow this program to send funds on your "
"behalf.")
2017-09-22 19:28:44 -07:00
=======
if MODE['auto'] == False:
input("Type 'enter' to allow this program to send funds on your behalf.")
>>>>>>> Add auto mode to skip user input
2017-07-28 13:57:44 -07:00
print("Trade commitment", trade.commitment)
2017-08-02 18:45:36 -07:00
# if verify_p2sh(trade):
fund_tx = protocol.fund_contract(trade.buy)
2017-08-02 21:41:55 -07:00
print("\nYou sent this funding tx: ", fund_tx)
2017-07-31 16:25:49 -07:00
trade.buy.fund_tx = fund_tx
save_state(trade, tradeid)
elif status == 'sellerRedeemed':
secret = protocol.find_secret_from_fundtx(trade.buy.currency,
trade.buy.p2sh,
trade.buy.fund_tx)
2017-09-04 16:43:13 -07:00
if secret is not None:
2017-08-02 21:41:55 -07:00
print("Found secret on blockchain in seller's redeem tx: ", secret)
txs = protocol.redeem_p2sh(trade.sell, secret)
2017-08-25 15:22:38 -07:00
if 'redeem_tx' in txs:
trade.sell.redeem_tx = txs['redeem_tx']
print("Redeem txid: ", trade.sell.redeem_tx)
elif 'refund_tx' in txs:
trade.sell.redeem_tx = txs['refund_tx']
print("Refund tx: ", txs['refund_tx'])
save_state(trade, tradeid)
2017-07-31 19:13:46 -07:00
print("XCAT trade complete!")
else:
2017-08-30 12:14:42 -07:00
# Search if tx has been refunded from p2sh
2017-07-31 19:13:46 -07:00
print("Secret not found in redeemtx")
2017-07-28 17:23:32 -07:00
# Import a trade in hex, and save to db
def importtrade(tradeid, hexstr=''):
protocol = Protocol()
2017-09-04 16:43:13 -07:00
trade = utils.x2s(hexstr)
trade = Trade(trade)
protocol.import_addrs(trade)
2017-07-31 19:13:46 -07:00
print(trade.toJSON())
2017-07-31 17:06:58 -07:00
save_state(trade, tradeid)
2017-07-28 17:23:32 -07:00
def wormhole_importtrade():
res = subprocess.call('wormhole receive', shell=True)
if res == 0:
2017-09-04 16:43:13 -07:00
tradeid = input("Enter filename of received trade data to import "
"(printed on line above): ")
with open(tradeid) as infile:
hexstr = infile.readline().strip()
importtrade(tradeid, hexstr)
print("Successfully imported trade using magic-wormhole")
os.remove(tradeid)
else:
print("Importing trade using magic-wormhole failed.")
2017-07-28 17:23:32 -07:00
# Export a trade by its tradeid
def exporttrade(tradeid, wormhole=False):
2017-09-12 22:28:36 -07:00
db = DB()
trade = db.get(tradeid)
2017-09-04 16:43:13 -07:00
hexstr = utils.s2x(trade.toJSON())
if wormhole:
2017-09-04 16:43:13 -07:00
tradefile = os.path.join(utils.ROOT_DIR, '.tmp/{0}'.format(tradeid))
2017-08-02 18:06:09 -07:00
print(tradefile)
with open(tradefile, '+w') as outfile:
outfile.write(hexstr)
print("Exporting trade to buyer using magic wormhole.")
2017-08-02 18:06:09 -07:00
subprocess.call('wormhole send {0}'.format(tradefile), shell=True)
else:
print(hexstr)
return hexstr
2017-07-31 13:32:51 -07:00
2017-07-31 16:25:49 -07:00
def findtrade(tradeid):
2017-09-12 22:28:36 -07:00
db = DB()
2017-07-31 16:25:49 -07:00
trade = db.get(tradeid)
2017-07-31 19:13:46 -07:00
print(trade.toJSON())
2017-07-31 17:06:58 -07:00
return trade
2017-07-28 17:23:32 -07:00
2017-08-25 15:42:30 -07:00
def find_role(contract):
protocol = Protocol()
2017-08-25 15:42:30 -07:00
# When regtest created both addrs on same machine, role is both.
if protocol.is_myaddr(contract.initiator):
if protocol.is_myaddr(contract.fulfiller):
return 'test'
else:
return 'initiator'
2017-08-25 15:42:30 -07:00
else:
2017-09-04 17:56:48 -07:00
if protocol.is_myaddr(contract.fulfiller):
return 'fulfiller'
else:
raise ValueError('You are not a participant in this contract.')
2017-08-25 15:42:30 -07:00
2017-07-31 16:25:49 -07:00
def checktrade(tradeid):
2017-09-12 22:28:36 -07:00
db = DB()
2017-07-31 16:25:49 -07:00
print("In checktrade")
trade = db.get(tradeid)
if find_role(trade.sell) == 'test':
2017-09-04 16:43:13 -07:00
input("Is this a test? Both buyer and seller addresses are yours, "
"press 'enter' to test.")
2017-08-03 09:04:51 -07:00
checkSellStatus(tradeid)
2017-07-31 16:25:49 -07:00
checkBuyStatus(tradeid)
checkSellStatus(tradeid)
checkBuyStatus(tradeid)
elif find_role(trade.sell) == 'initiator':
print("You are the seller in this trade.")
2017-09-04 16:43:13 -07:00
# role = 'seller'
2017-07-31 16:25:49 -07:00
checkSellStatus(tradeid)
else:
print("You are the buyer in this trade.")
2017-09-04 16:43:13 -07:00
# role = 'buyer'
2017-07-31 16:25:49 -07:00
checkBuyStatus(tradeid)
def newtrade(tradeid, **kwargs):
protocol = Protocol()
2017-07-28 17:23:32 -07:00
print("Creating new XCAT trade...")
2017-09-04 16:43:13 -07:00
utils.erase_trade()
conf = kwargs['conf'] if 'conf' in kwargs else 'regtest'
network = kwargs['network'] if 'network' in kwargs else 'regtest'
tradeid, trade = protocol.initialize_trade(tradeid, conf=conf, network=network)
2017-08-29 17:17:28 -07:00
print("New trade created: {0}".format(trade))
trade = protocol.seller_init(tradeid, trade, network=network)
print("\nUse 'xcat exporttrade [tradeid]' to export the trade and send to the buyer.\n")
2017-07-28 17:23:32 -07:00
save_state(trade, tradeid)
2017-08-06 23:19:21 -07:00
return trade
2017-07-28 17:23:32 -07:00
2017-08-04 20:49:02 -07:00
def listtrades():
2017-09-12 22:28:36 -07:00
db = DB()
2017-08-04 20:49:02 -07:00
print("Trades")
2017-09-04 16:43:13 -07:00
trade_list = db.dump()
for trade in trade_list:
2017-08-04 20:49:02 -07:00
print("{0}: {1}".format(trade[0], trade[1]))
2017-07-28 17:23:32 -07:00
def main():
2017-09-04 16:43:13 -07:00
parser = argparse.ArgumentParser(
formatter_class=argparse.RawTextHelpFormatter,
description=textwrap.dedent('''\
== Trades ==
2017-07-31 16:25:49 -07:00
newtrade "tradeid" - create a new trade
checktrade "tradeid"- check for actions to be taken on an existing trade
2017-07-31 13:32:51 -07:00
importtrade "hexstr" - import an existing trade from a hex string
2017-07-31 16:25:49 -07:00
exporttrade "tradeid" - export the data of an existing trade as a hex string. Takes the tradeid as an argument
findtrade "tradeid" - find a trade by the tradeid
'''))
parser.add_argument("command", action="store", help="list commands")
parser.add_argument("arguments", action="store", nargs="*", help="add arguments")
2017-09-22 19:28:44 -07:00
parser.add_argument("-a", "--auto", action="store_true", help="Authorize automatic execution of trade sequence without user input.")
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode. Defaults to false")
parser.add_argument("-w", "--wormhole", action="store_true", help="Transfer trade data through magic-wormhole")
parser.add_argument("-c", "--conf", action="store", help="Use trade data in conf file ('testnet' or 'regtest'), or pass trade data in on cli as json.")
parser.add_argument("-n", "--network", action="store", help="Set network to regtest or mainnet. Defaults to testnet while in alpha.")
# parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process")
args = parser.parse_args()
2017-09-22 19:28:44 -07:00
print("State of auto input: ", args.auto)
MODE['auto'] = args.auto
2017-08-02 21:41:55 -07:00
2017-09-12 21:16:42 -07:00
if hasattr(args, 'debug'):
2017-08-29 17:17:28 -07:00
numeric_level = getattr(logging, 'DEBUG', None)
2017-09-12 21:16:42 -07:00
logging.basicConfig(format='%(levelname)s: %(message)s',
level=numeric_level)
2017-08-29 17:17:28 -07:00
else:
2017-09-12 21:16:42 -07:00
logging.basicConfig(format='%(levelname)s: %(message)s',
level='INFO')
2017-08-29 17:17:28 -07:00
2017-09-12 21:16:42 -07:00
if hasattr(args, 'network'):
2017-08-29 17:17:28 -07:00
NETWORK = args.network
else:
NETWORK = 'testnet'
command = args.command
2017-09-12 21:16:42 -07:00
if command == 'importtrade':
if args.wormhole:
wormhole_importtrade()
else:
2017-09-04 16:43:13 -07:00
if len(args.arguments) != 2:
utils.throw("Usage: importtrade [tradeid] [hexstring]")
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
hexstr = args.arguments[1]
importtrade(tradeid, hexstr)
2017-09-12 21:16:42 -07:00
elif command == 'exporttrade':
2017-09-04 16:43:13 -07:00
if len(args.arguments) < 1:
utils.throw("Usage: exporttrade [tradeid]")
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
exporttrade(tradeid, args.wormhole)
2017-09-12 21:16:42 -07:00
2017-07-31 13:32:51 -07:00
elif command == "findtrade":
2017-09-04 16:43:13 -07:00
if len(args.arguments) < 1:
utils.throw("Usage: findtrade [tradeid]")
2017-07-31 13:32:51 -07:00
print("Finding trade")
2017-08-02 18:06:09 -07:00
key = args.arguments[0]
2017-07-31 15:27:43 -07:00
findtrade(key)
2017-09-12 21:16:42 -07:00
2017-07-31 16:25:49 -07:00
elif command == 'checktrade':
2017-09-04 16:43:13 -07:00
if len(args.arguments) < 1:
utils.throw("Usage: checktrade [tradeid]")
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
2017-07-31 16:25:49 -07:00
checktrade(tradeid)
2017-09-12 21:16:42 -07:00
2017-08-04 20:49:02 -07:00
elif command == 'listtrades':
listtrades()
2017-09-12 21:16:42 -07:00
2017-08-25 12:12:29 -07:00
# TODO: function to tell if tradeid already exists for newtrade
2017-09-12 21:16:42 -07:00
elif command == 'newtrade':
2017-09-04 16:43:13 -07:00
if len(args.arguments) < 1:
utils.throw("Usage: newtrade [tradeid]")
tradeid = args.arguments[0]
2017-09-04 16:43:13 -07:00
if args.conf is None:
conf = 'cli'
2017-08-06 23:19:21 -07:00
else:
conf = args.conf
newtrade(tradeid, network=NETWORK, conf=conf)
2017-09-12 21:16:42 -07:00
elif command == "daemon":
2017-09-04 16:43:13 -07:00
# TODO: not implemented
print("Run as daemon process")
2017-09-12 21:16:42 -07:00
2017-07-31 15:27:43 -07:00
# Ad hoc testing of workflow starts here
elif command == "step1":
2017-09-22 19:28:44 -07:00
MODE['auto'] = True
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
checkSellStatus(tradeid)
2017-07-26 13:23:12 -07:00
elif command == "step2":
2017-09-22 19:28:44 -07:00
MODE['auto'] = True
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
2017-07-31 15:15:22 -07:00
checkBuyStatus(tradeid)
2017-07-26 13:23:12 -07:00
elif command == "step3":
2017-09-22 19:28:44 -07:00
<<<<<<< HEAD
# protocol = Protocol()
# protocol.generate(31)
2017-09-22 19:28:44 -07:00
=======
MODE['auto'] = True
# generate(31)
>>>>>>> Add auto mode to skip user input
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
2017-07-31 15:15:22 -07:00
checkSellStatus(tradeid)
2017-07-26 13:23:12 -07:00
elif command == "step4":
2017-09-22 19:28:44 -07:00
MODE['auto'] = True
# generate(1)
2017-08-02 18:06:09 -07:00
tradeid = args.arguments[0]
2017-07-31 15:15:22 -07:00
checkBuyStatus(tradeid)