Merge pull request #29 from frdwrd/better-tests
Refactoring and unittests
This commit is contained in:
commit
76c1729031
|
@ -1,4 +1,15 @@
|
||||||
*.pyc
|
# xcat
|
||||||
xcat.egg-info/
|
|
||||||
.tmp/
|
.tmp/
|
||||||
|
|
||||||
|
# Python
|
||||||
|
*.pyc
|
||||||
|
*.egg-info/
|
||||||
|
|
||||||
|
# Virtual environment
|
||||||
venv/
|
venv/
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
.tox/
|
||||||
|
coverage/
|
||||||
|
.coverage
|
||||||
|
.cache
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
flake8
|
||||||
|
pytest
|
||||||
|
pytest-cov
|
|
@ -0,0 +1,26 @@
|
||||||
|
[tox]
|
||||||
|
envlist =
|
||||||
|
py3{5}
|
||||||
|
|
||||||
|
[pytest]
|
||||||
|
nonrecursedirs = .git .tox venv coverage
|
||||||
|
|
||||||
|
[testenv]
|
||||||
|
usedevelop = True
|
||||||
|
deps =
|
||||||
|
-rrequirements.txt
|
||||||
|
-rrequirements-test.txt
|
||||||
|
|
||||||
|
commands =
|
||||||
|
flake8 \
|
||||||
|
--ignore=E501,E266,W503 \
|
||||||
|
--exclude test.py \
|
||||||
|
xcat
|
||||||
|
pytest \
|
||||||
|
-q \
|
||||||
|
--junitxml=coverage/unit.xml \
|
||||||
|
--cov xcat \
|
||||||
|
--cov-report xml:coverage/coverage.xml \
|
||||||
|
--cov-report html:coverage/html/ \
|
||||||
|
--cov-report term-missing \
|
||||||
|
{posargs}
|
|
@ -1,33 +1,40 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import bitcoin
|
||||||
|
import bitcoin.rpc
|
||||||
|
from xcat.utils import x2s
|
||||||
|
from bitcoin.core import b2x, lx, x, COIN, CMutableTxOut
|
||||||
|
from bitcoin.core import CMutableTxIn, CMutableTransaction
|
||||||
|
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF
|
||||||
|
from bitcoin.core.script import OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||||
|
from bitcoin.core.script import SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP
|
||||||
|
from bitcoin.core.script import OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
|
||||||
|
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
||||||
|
from bitcoin.wallet import CBitcoinAddress, P2SHBitcoinAddress
|
||||||
|
from bitcoin.wallet import P2PKHBitcoinAddress
|
||||||
|
import logging
|
||||||
|
|
||||||
if sys.version_info.major < 3:
|
if sys.version_info.major < 3:
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
import bitcoin
|
FEE = 0.001 * COIN
|
||||||
import bitcoin.rpc
|
|
||||||
from bitcoin import SelectParams
|
|
||||||
from bitcoin.core import b2x, lx, b2lx, x, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160, CTransaction
|
|
||||||
from bitcoin.base58 import decode
|
|
||||||
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE, OP_FALSE
|
|
||||||
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
|
||||||
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
|
|
||||||
|
|
||||||
from xcat.utils import *
|
|
||||||
import logging
|
|
||||||
|
|
||||||
FEE = 0.001*COIN
|
|
||||||
|
|
||||||
class bitcoinProxy():
|
class bitcoinProxy():
|
||||||
def __init__(self, network='regtest', timeout=900):
|
def __init__(self, network='regtest', timeout=900):
|
||||||
if network is not 'testnet' and network is not 'mainnet':
|
if network not in ['testnet', 'mainnet', 'regtest']:
|
||||||
network='regtest'
|
raise ValueError('Allowed networks are regtest, testnet, mainnet.')
|
||||||
|
if not isinstance(timeout, int) or timeout < 1:
|
||||||
|
raise ValueError('Timeout should be a positive integer.')
|
||||||
|
|
||||||
logging.debug("NETWORK in proxy: {0}".format(network))
|
logging.debug("NETWORK in proxy: {0}".format(network))
|
||||||
|
|
||||||
self.network = network
|
self.network = network
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
SelectParams(self.network)
|
bitcoin.SelectParams(self.network)
|
||||||
self.bitcoind = bitcoin.rpc.Proxy(timeout=self.timeout)
|
self.bitcoind = bitcoin.rpc.Proxy(timeout=self.timeout)
|
||||||
|
|
||||||
def validateaddress(self, addr):
|
def validateaddress(self, addr):
|
||||||
|
@ -41,9 +48,9 @@ class bitcoinProxy():
|
||||||
print("TXINFO", decoded['vin'][0])
|
print("TXINFO", decoded['vin'][0])
|
||||||
if('txid' in decoded['vin'][0]):
|
if('txid' in decoded['vin'][0]):
|
||||||
sendid = decoded['vin'][0]['txid']
|
sendid = decoded['vin'][0]['txid']
|
||||||
if (sendid == fundtx_input ):
|
if (sendid == fundtx_input):
|
||||||
print("Found funding tx: ", sendid)
|
print("Found funding tx: ", sendid)
|
||||||
return parse_secret(lx(tx['txid']))
|
return self.parse_secret(lx(tx['txid']))
|
||||||
print("Redeem transaction with secret not found")
|
print("Redeem transaction with secret not found")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -52,9 +59,9 @@ class bitcoinProxy():
|
||||||
decoded = self.bitcoind.call('decoderawtransaction', raw)
|
decoded = self.bitcoind.call('decoderawtransaction', raw)
|
||||||
scriptSig = decoded['vin'][0]['scriptSig']
|
scriptSig = decoded['vin'][0]['scriptSig']
|
||||||
asm = scriptSig['asm'].split(" ")
|
asm = scriptSig['asm'].split(" ")
|
||||||
pubkey = asm[1]
|
# pubkey = asm[1]
|
||||||
secret = x2s(asm[2])
|
secret = x2s(asm[2])
|
||||||
redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
# redeemPubkey = P2PKHBitcoinAddress.from_pubkey(x(pubkey))
|
||||||
return secret
|
return secret
|
||||||
|
|
||||||
def get_keys(self, funder_address, redeemer_address):
|
def get_keys(self, funder_address, redeemer_address):
|
||||||
|
@ -77,18 +84,27 @@ class bitcoinProxy():
|
||||||
print("Current blocknum on Bitcoin: ", blocknum)
|
print("Current blocknum on Bitcoin: ", blocknum)
|
||||||
redeemblocknum = blocknum + locktime
|
redeemblocknum = blocknum + locktime
|
||||||
print("Redeemblocknum on Bitcoin: ", redeemblocknum)
|
print("Redeemblocknum on Bitcoin: ", redeemblocknum)
|
||||||
redeemScript = CScript([OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
redeemScript = CScript([
|
||||||
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY, OP_DUP, OP_HASH160,
|
||||||
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY,
|
||||||
# print("Redeem script for p2sh contract on Bitcoin blockchain: {0}".format(b2x(redeemScript)))
|
OP_DROP, OP_DUP, OP_HASH160, funderAddr, OP_ENDIF, OP_EQUALVERIFY,
|
||||||
|
OP_CHECKSIG])
|
||||||
|
# print("Redeem script for p2sh contract on Bitcoin blockchain: "
|
||||||
|
# "{0}".format(b2x(redeemScript)))
|
||||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(
|
||||||
|
txin_scriptPubKey)
|
||||||
p2sh = str(txin_p2sh_address)
|
p2sh = str(txin_p2sh_address)
|
||||||
# Import address at same time you create
|
# Import address at same time you create
|
||||||
self.bitcoind.importaddress(p2sh, "", False)
|
self.bitcoind.importaddress(p2sh, "", False)
|
||||||
print("p2sh computed", p2sh)
|
print("p2sh computed", p2sh)
|
||||||
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
|
return {'p2sh': p2sh,
|
||||||
|
'redeemblocknum': redeemblocknum,
|
||||||
|
'redeemScript': b2x(redeemScript),
|
||||||
|
'redeemer': redeemer,
|
||||||
|
'funder': funder,
|
||||||
|
'locktime': locktime}
|
||||||
|
|
||||||
def fund_htlc(self, p2sh, amount):
|
def fund_htlc(self, p2sh, amount):
|
||||||
send_amount = float(amount) * COIN
|
send_amount = float(amount) * COIN
|
||||||
|
@ -103,20 +119,20 @@ class bitcoinProxy():
|
||||||
self.bitcoind.importaddress(p2sh, "", False)
|
self.bitcoind.importaddress(p2sh, "", False)
|
||||||
# Get amount in address
|
# Get amount in address
|
||||||
amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
|
amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
|
||||||
amount = amount/COIN
|
amount = amount / COIN
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
def get_fund_status(self, p2sh):
|
def get_fund_status(self, p2sh):
|
||||||
self.bitcoind.importaddress(p2sh, "", False)
|
self.bitcoind.importaddress(p2sh, "", False)
|
||||||
amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
|
amount = self.bitcoind.getreceivedbyaddress(p2sh, 0)
|
||||||
amount = amount/COIN
|
amount = amount / COIN
|
||||||
print("Amount in bitcoin p2sh: ", amount, p2sh)
|
print("Amount in bitcoin p2sh: ", amount, p2sh)
|
||||||
if amount > 0:
|
if amount > 0:
|
||||||
return 'funded'
|
return 'funded'
|
||||||
else:
|
else:
|
||||||
return 'empty'
|
return 'empty'
|
||||||
|
|
||||||
## TODO: FIX search for p2sh in block
|
# TODO: FIX search for p2sh in block
|
||||||
def search_p2sh(self, block, p2sh):
|
def search_p2sh(self, block, p2sh):
|
||||||
print("Fetching block...")
|
print("Fetching block...")
|
||||||
blockdata = self.bitcoind.getblock(lx(block))
|
blockdata = self.bitcoind.getblock(lx(block))
|
||||||
|
@ -145,9 +161,9 @@ class bitcoinProxy():
|
||||||
scriptarray = self.parse_script(contract.redeemScript)
|
scriptarray = self.parse_script(contract.redeemScript)
|
||||||
redeemblocknum = scriptarray[8]
|
redeemblocknum = scriptarray[8]
|
||||||
self.redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
|
self.redeemPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[6]))
|
||||||
refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
|
# refundPubKey = P2PKHBitcoinAddress.from_bytes(x(scriptarray[13]))
|
||||||
p2sh = contract.p2sh
|
p2sh = contract.p2sh
|
||||||
#checking there are funds in the address
|
# checking there are funds in the address
|
||||||
amount = self.check_funds(p2sh)
|
amount = self.check_funds(p2sh)
|
||||||
if(amount == 0):
|
if(amount == 0):
|
||||||
print("address ", p2sh, " not funded")
|
print("address ", p2sh, " not funded")
|
||||||
|
@ -174,7 +190,9 @@ class bitcoinProxy():
|
||||||
# TODO: Compare with script on blockchain?
|
# TODO: Compare with script on blockchain?
|
||||||
redeemScript = CScript(x(contract.redeemScript))
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
txin = CMutableTxIn(fundtx['outpoint'])
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
txout = CMutableTxOut(fundtx['amount'] - FEE, self.redeemPubKey.to_scriptPubKey())
|
txout = CMutableTxOut(fundtx['amount'] - FEE,
|
||||||
|
self.redeemPubKey.to_scriptPubKey())
|
||||||
|
|
||||||
# Create the unsigned raw transaction.
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
|
@ -182,17 +200,19 @@ class bitcoinProxy():
|
||||||
privkey = self.bitcoind.dumpprivkey(self.redeemPubKey)
|
privkey = self.bitcoind.dumpprivkey(self.redeemPubKey)
|
||||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
preimage = secret.encode('utf-8')
|
preimage = secret.encode('utf-8')
|
||||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, redeemScript])
|
txin.scriptSig = CScript([sig, privkey.pub, preimage,
|
||||||
|
OP_TRUE, redeemScript])
|
||||||
|
|
||||||
# print("txin.scriptSig", b2x(txin.scriptSig))
|
# print("txin.scriptSig", b2x(txin.scriptSig))
|
||||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
|
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
VerifyScript(txin.scriptSig, txin_scriptPubKey,
|
||||||
|
tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
print("Script verified, sending raw transaction...")
|
print("Script verified, sending raw transaction...")
|
||||||
txid = self.bitcoind.sendrawtransaction(tx)
|
txid = self.bitcoind.sendrawtransaction(tx)
|
||||||
fund_tx = str(fundtx['outpoint'])
|
fund_tx = str(fundtx['outpoint'])
|
||||||
redeem_tx = b2x(lx(b2x(txid)))
|
redeem_tx = b2x(lx(b2x(txid)))
|
||||||
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
||||||
|
|
||||||
def refund(self, contract):
|
def refund(self, contract):
|
||||||
fundtx = self.find_transaction_to_address(contract.p2sh)
|
fundtx = self.find_transaction_to_address(contract.p2sh)
|
||||||
|
@ -202,7 +222,9 @@ class bitcoinProxy():
|
||||||
|
|
||||||
redeemScript = CScript(x(contract.redeemScript))
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
txin = CMutableTxIn(fundtx['outpoint'])
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey())
|
txout = CMutableTxOut(fundtx['amount'] - FEE,
|
||||||
|
refundPubKey.to_scriptPubKey())
|
||||||
|
|
||||||
# Create the unsigned raw transaction.
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
# Set nSequence and nLockTime
|
# Set nSequence and nLockTime
|
||||||
|
@ -211,15 +233,18 @@ class bitcoinProxy():
|
||||||
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
sighash = SignatureHash(redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
privkey = self.bitcoind.dumpprivkey(refundPubKey)
|
privkey = self.bitcoind.dumpprivkey(refundPubKey)
|
||||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
|
|
||||||
# Sign without secret
|
# Sign without secret
|
||||||
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||||
|
|
||||||
# txin.nSequence = 2185
|
# txin.nSequence = 2185
|
||||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
||||||
res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
res = VerifyScript(txin.scriptSig, txin_scriptPubKey,
|
||||||
|
tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
print("Script verified, sending raw transaction... (NOT)", res)
|
print("Script verified, sending raw transaction... (NOT)", res)
|
||||||
txid = self.bitcoind.sendrawtransaction(tx)
|
txid = self.bitcoind.sendrawtransaction(tx)
|
||||||
refund_tx = b2x(lx(b2x(txid)))
|
refund_tx = b2x(lx(b2x(txid)))
|
||||||
fund_tx = str(fundtx['outpoint'])
|
fund_tx = str(fundtx['outpoint'])
|
||||||
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
||||||
|
|
||||||
|
@ -229,12 +254,12 @@ class bitcoinProxy():
|
||||||
return scriptarray
|
return scriptarray
|
||||||
|
|
||||||
def find_redeemblocknum(self, contract):
|
def find_redeemblocknum(self, contract):
|
||||||
scriptarray = parse_script(contract.redeemScript)
|
scriptarray = self.parse_script(contract.redeemScript)
|
||||||
redeemblocknum = scriptarray[8]
|
redeemblocknum = scriptarray[8]
|
||||||
return int(redeemblocknum)
|
return int(redeemblocknum)
|
||||||
|
|
||||||
def find_redeemAddr(self, contract):
|
def find_redeemAddr(self, contract):
|
||||||
scriptarray = parse_script(contract.redeemScript)
|
scriptarray = self.parse_script(contract.redeemScript)
|
||||||
redeemer = scriptarray[6]
|
redeemer = scriptarray[6]
|
||||||
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
|
redeemAddr = P2PKHBitcoinAddress.from_bytes(x(redeemer))
|
||||||
return redeemAddr
|
return redeemAddr
|
||||||
|
|
248
xcat/cli.py
248
xcat/cli.py
|
@ -1,98 +1,132 @@
|
||||||
import argparse, textwrap
|
import argparse
|
||||||
from xcat.utils import *
|
import textwrap
|
||||||
import xcat.db as db
|
|
||||||
import xcat.userInput as userInput
|
|
||||||
from xcat.trades import *
|
|
||||||
from xcat.protocol import *
|
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from xcat.db import DB
|
||||||
|
import xcat.userInput as userInput
|
||||||
|
import xcat.utils as utils
|
||||||
|
from xcat.protocol import Protocol
|
||||||
|
from xcat.trades import Trade
|
||||||
|
|
||||||
|
|
||||||
def save_state(trade, tradeid):
|
def save_state(trade, tradeid):
|
||||||
save(trade)
|
db = DB()
|
||||||
|
utils.save(trade)
|
||||||
db.create(trade, tradeid)
|
db.create(trade, tradeid)
|
||||||
|
|
||||||
|
|
||||||
def checkSellStatus(tradeid):
|
def checkSellStatus(tradeid):
|
||||||
|
db = DB()
|
||||||
|
protocol = Protocol()
|
||||||
|
|
||||||
trade = db.get(tradeid)
|
trade = db.get(tradeid)
|
||||||
status = seller_check_status(trade)
|
status = seller_check_status(trade)
|
||||||
print("Trade status: {0}\n".format(status))
|
print("Trade status: {0}\n".format(status))
|
||||||
|
|
||||||
if status == 'init':
|
if status == 'init':
|
||||||
userInput.authorize_fund_sell(trade)
|
userInput.authorize_fund_sell(trade)
|
||||||
fund_tx = fund_sell_contract(trade)
|
fund_tx = protocol.fund_sell_contract(trade)
|
||||||
|
|
||||||
print("Sent fund_tx", fund_tx)
|
print("Sent fund_tx", fund_tx)
|
||||||
trade.sell.fund_tx = fund_tx
|
trade.sell.fund_tx = fund_tx
|
||||||
save_state(trade, tradeid)
|
save_state(trade, tradeid)
|
||||||
|
|
||||||
elif status == 'buyerFunded':
|
elif status == 'buyerFunded':
|
||||||
secret = db.get_secret(tradeid)
|
secret = db.get_secret(tradeid)
|
||||||
print("Retrieved secret to redeem funds for {0}: {1}".format(tradeid, secret))
|
print("Retrieved secret to redeem funds for "
|
||||||
txs = seller_redeem_p2sh(trade, secret)
|
"{0}: {1}".format(tradeid, secret))
|
||||||
|
txs = protocol.seller_redeem_p2sh(trade, secret)
|
||||||
if 'redeem_tx' in txs:
|
if 'redeem_tx' in txs:
|
||||||
trade.buy.redeem_tx = txs['redeem_tx']
|
trade.buy.redeem_tx = txs['redeem_tx']
|
||||||
print("Redeem tx: ", txs['redeem_tx'])
|
print("Redeem tx: ", txs['redeem_tx'])
|
||||||
if 'refund_tx' in txs:
|
if 'refund_tx' in txs:
|
||||||
trade.buy.redeem_tx = txs['refund_tx']
|
trade.buy.redeem_tx = txs['refund_tx']
|
||||||
print("Buyer refund tx: ", txs['refund_tx'])
|
print("Buyer refund tx: ", txs['refund_tx'])
|
||||||
txs = refund_contract(trade.sell) # Refund to seller
|
txs = protocol.refund_contract(trade.sell) # Refund to seller
|
||||||
print("Your refund txid: ", txs['refund_tx'])
|
print("Your refund txid: ", txs['refund_tx'])
|
||||||
save_state(trade, tradeid)
|
save_state(trade, tradeid)
|
||||||
cleanup(tradeid)
|
# Remove from db? Or just from temporary file storage
|
||||||
|
utils.cleanup(tradeid)
|
||||||
|
|
||||||
elif status == 'sellerFunded':
|
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))
|
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':
|
elif status == 'sellerRedeemed':
|
||||||
print("You have already redeemed the p2sh on the second chain of this trade.")
|
print("You have already redeemed the p2sh on the second chain of "
|
||||||
|
"this trade.")
|
||||||
|
|
||||||
|
|
||||||
def buyer_check_status(trade):
|
def buyer_check_status(trade):
|
||||||
sellState = check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
protocol = Protocol()
|
||||||
buyState = check_fund_status(trade.buy.currency, trade.buy.p2sh)
|
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':
|
if sellState == 'funded' and buyState == 'empty':
|
||||||
return 'sellerFunded' # step1
|
return 'sellerFunded' # step1
|
||||||
# TODO: Find funding txid. How does buyer get seller redeemed tx?
|
# TODO: Find funding txid. How does buyer get seller redeemed tx?
|
||||||
elif sellState == 'funded' and hasattr(trade.buy, 'fund_tx'):
|
elif sellState == 'funded' and hasattr(trade.buy, 'fund_tx'):
|
||||||
return 'sellerRedeemed' # step3
|
return 'sellerRedeemed' # step3
|
||||||
elif sellState == 'funded' and buyState == 'funded':
|
elif sellState == 'funded' and buyState == 'funded':
|
||||||
return 'buyerFunded' # step2
|
return 'buyerFunded' # step2
|
||||||
elif sellState == 'empty' and buyState == 'empty':
|
elif sellState == 'empty' and buyState == 'empty':
|
||||||
if hasattr(trade.sell, 'redeem_tx'):
|
if hasattr(trade.sell, 'redeem_tx'):
|
||||||
return 'buyerRedeemed' # step4
|
return 'buyerRedeemed' # step4
|
||||||
else:
|
else:
|
||||||
return 'init'
|
return 'init'
|
||||||
|
|
||||||
|
|
||||||
def seller_check_status(trade):
|
def seller_check_status(trade):
|
||||||
sellState = check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
protocol = Protocol()
|
||||||
buyState = check_fund_status(trade.buy.currency, trade.buy.p2sh)
|
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':
|
if sellState == 'funded' and buyState == 'empty':
|
||||||
return 'sellerFunded' # step1
|
return 'sellerFunded' # step1
|
||||||
elif sellState == 'funded' and hasattr(trade.buy, 'redeem_tx'):
|
elif sellState == 'funded' and hasattr(trade.buy, 'redeem_tx'):
|
||||||
return 'sellerRedeemed' # step3
|
return 'sellerRedeemed' # step3
|
||||||
# TODO: How does seller get buyer funded tx?
|
# TODO: How does seller get buyer funded tx?
|
||||||
elif sellState == 'funded' and buyState == 'funded':
|
elif sellState == 'funded' and buyState == 'funded':
|
||||||
return 'buyerFunded' # step2
|
return 'buyerFunded' # step2
|
||||||
elif sellState == 'empty' and buyState == 'empty':
|
elif sellState == 'empty' and buyState == 'empty':
|
||||||
if hasattr(trade.buy, 'redeem_tx'):
|
if hasattr(trade.buy, 'redeem_tx'):
|
||||||
return 'buyerRedeemed' # step4
|
return 'buyerRedeemed' # step4
|
||||||
else:
|
else:
|
||||||
return 'init' # step0
|
return 'init' # step0
|
||||||
|
|
||||||
|
|
||||||
def checkBuyStatus(tradeid):
|
def checkBuyStatus(tradeid):
|
||||||
|
db = DB()
|
||||||
|
protocol = Protocol()
|
||||||
trade = db.get(tradeid)
|
trade = db.get(tradeid)
|
||||||
status = buyer_check_status(trade)
|
status = buyer_check_status(trade)
|
||||||
print("Trade status: {0}\n".format(status))
|
print("Trade status: {0}\n".format(status))
|
||||||
if status == 'init':
|
if status == 'init':
|
||||||
print("Trade has not yet started, waiting for seller to fund the sell p2sh.")
|
print("Trade has not yet started, waiting for seller to fund the "
|
||||||
|
"sell p2sh.")
|
||||||
elif status == 'buyerRedeemed':
|
elif status == 'buyerRedeemed':
|
||||||
print("This trade is complete, both sides redeemed.")
|
print("This trade is complete, both sides redeemed.")
|
||||||
elif status == 'sellerFunded':
|
elif status == 'sellerFunded':
|
||||||
print("One active trade available, fulfilling buyer contract...")
|
print("One active trade available, fulfilling buyer contract...")
|
||||||
input("Type 'enter' to allow this program to send funds on your behalf.")
|
input("Type 'enter' to allow this program to send funds on your "
|
||||||
|
"behalf.")
|
||||||
print("Trade commitment", trade.commitment)
|
print("Trade commitment", trade.commitment)
|
||||||
# if verify_p2sh(trade):
|
# if verify_p2sh(trade):
|
||||||
fund_tx = fund_contract(trade.buy)
|
fund_tx = protocol.fund_contract(trade.buy)
|
||||||
print("\nYou sent this funding tx: ", fund_tx)
|
print("\nYou sent this funding tx: ", fund_tx)
|
||||||
trade.buy.fund_tx = fund_tx
|
trade.buy.fund_tx = fund_tx
|
||||||
save_state(trade, tradeid)
|
save_state(trade, tradeid)
|
||||||
elif status == 'sellerRedeemed':
|
elif status == 'sellerRedeemed':
|
||||||
secret = find_secret_from_fundtx(trade.buy.currency, trade.buy.p2sh, trade.buy.fund_tx)
|
secret = protocol.find_secret_from_fundtx(trade.buy.currency,
|
||||||
if secret != None:
|
trade.buy.p2sh,
|
||||||
|
trade.buy.fund_tx)
|
||||||
|
if secret is not None:
|
||||||
print("Found secret on blockchain in seller's redeem tx: ", secret)
|
print("Found secret on blockchain in seller's redeem tx: ", secret)
|
||||||
txs = redeem_p2sh(trade.sell, secret)
|
txs = protocol.redeem_p2sh(trade.sell, secret)
|
||||||
if 'redeem_tx' in txs:
|
if 'redeem_tx' in txs:
|
||||||
trade.sell.redeem_tx = txs['redeem_tx']
|
trade.sell.redeem_tx = txs['redeem_tx']
|
||||||
print("Redeem txid: ", trade.sell.redeem_tx)
|
print("Redeem txid: ", trade.sell.redeem_tx)
|
||||||
|
@ -105,18 +139,22 @@ def checkBuyStatus(tradeid):
|
||||||
# Search if tx has been refunded from p2sh
|
# Search if tx has been refunded from p2sh
|
||||||
print("Secret not found in redeemtx")
|
print("Secret not found in redeemtx")
|
||||||
|
|
||||||
|
|
||||||
# Import a trade in hex, and save to db
|
# Import a trade in hex, and save to db
|
||||||
def importtrade(tradeid, hexstr=''):
|
def importtrade(tradeid, hexstr=''):
|
||||||
trade = x2s(hexstr)
|
protocol = Protocol()
|
||||||
trade = db.instantiate(trade)
|
trade = utils.x2s(hexstr)
|
||||||
import_addrs(trade)
|
trade = Trade(trade)
|
||||||
|
protocol.import_addrs(trade)
|
||||||
print(trade.toJSON())
|
print(trade.toJSON())
|
||||||
save_state(trade, tradeid)
|
save_state(trade, tradeid)
|
||||||
|
|
||||||
|
|
||||||
def wormhole_importtrade():
|
def wormhole_importtrade():
|
||||||
res = subprocess.call('wormhole receive', shell=True)
|
res = subprocess.call('wormhole receive', shell=True)
|
||||||
if res == 0:
|
if res == 0:
|
||||||
tradeid = input("Enter filename of received trade data to import (printed on line above): ")
|
tradeid = input("Enter filename of received trade data to import "
|
||||||
|
"(printed on line above): ")
|
||||||
with open(tradeid) as infile:
|
with open(tradeid) as infile:
|
||||||
hexstr = infile.readline().strip()
|
hexstr = infile.readline().strip()
|
||||||
importtrade(tradeid, hexstr)
|
importtrade(tradeid, hexstr)
|
||||||
|
@ -125,12 +163,14 @@ def wormhole_importtrade():
|
||||||
else:
|
else:
|
||||||
print("Importing trade using magic-wormhole failed.")
|
print("Importing trade using magic-wormhole failed.")
|
||||||
|
|
||||||
|
|
||||||
# Export a trade by its tradeid
|
# Export a trade by its tradeid
|
||||||
def exporttrade(tradeid, wormhole=False):
|
def exporttrade(tradeid, wormhole=False):
|
||||||
trade = db.get(tradeid)
|
db = DB()
|
||||||
hexstr = s2x(trade.toJSON())
|
trade = db.get(tradeid)
|
||||||
|
hexstr = utils.s2x(trade.toJSON())
|
||||||
if wormhole:
|
if wormhole:
|
||||||
tradefile = os.path.join(ROOT_DIR, '.tmp/{0}'.format(tradeid))
|
tradefile = os.path.join(utils.ROOT_DIR, '.tmp/{0}'.format(tradeid))
|
||||||
print(tradefile)
|
print(tradefile)
|
||||||
with open(tradefile, '+w') as outfile:
|
with open(tradefile, '+w') as outfile:
|
||||||
outfile.write(hexstr)
|
outfile.write(hexstr)
|
||||||
|
@ -140,56 +180,83 @@ def exporttrade(tradeid, wormhole=False):
|
||||||
print(hexstr)
|
print(hexstr)
|
||||||
return hexstr
|
return hexstr
|
||||||
|
|
||||||
|
|
||||||
def findtrade(tradeid):
|
def findtrade(tradeid):
|
||||||
|
db = DB()
|
||||||
trade = db.get(tradeid)
|
trade = db.get(tradeid)
|
||||||
print(trade.toJSON())
|
print(trade.toJSON())
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
|
|
||||||
def find_role(contract):
|
def find_role(contract):
|
||||||
|
protocol = Protocol()
|
||||||
# When regtest created both addrs on same machine, role is both.
|
# When regtest created both addrs on same machine, role is both.
|
||||||
if is_myaddr(contract.initiator) and is_myaddr(contract.fulfiller):
|
if protocol.is_myaddr(contract.initiator):
|
||||||
return 'test'
|
if protocol.is_myaddr(contract.fulfiller):
|
||||||
elif is_myaddr(contract.initiator):
|
return 'test'
|
||||||
return 'initiator'
|
else:
|
||||||
|
return 'initiator'
|
||||||
else:
|
else:
|
||||||
return 'fulfiller'
|
if protocol.is_myaddr(contract.fulfiller):
|
||||||
|
return 'fulfiller'
|
||||||
|
else:
|
||||||
|
raise ValueError('You are not a participant in this contract.')
|
||||||
|
|
||||||
|
|
||||||
def checktrade(tradeid):
|
def checktrade(tradeid):
|
||||||
|
db = DB()
|
||||||
print("In checktrade")
|
print("In checktrade")
|
||||||
trade = db.get(tradeid)
|
trade = db.get(tradeid)
|
||||||
if find_role(trade.sell) == 'test':
|
if find_role(trade.sell) == 'test':
|
||||||
input("Is this a test? Both buyer and seller addresses are yours, press 'enter' to test.")
|
input("Is this a test? Both buyer and seller addresses are yours, "
|
||||||
|
"press 'enter' to test.")
|
||||||
checkSellStatus(tradeid)
|
checkSellStatus(tradeid)
|
||||||
checkBuyStatus(tradeid)
|
checkBuyStatus(tradeid)
|
||||||
checkSellStatus(tradeid)
|
checkSellStatus(tradeid)
|
||||||
checkBuyStatus(tradeid)
|
checkBuyStatus(tradeid)
|
||||||
elif find_role(trade.sell) == 'initiator':
|
elif find_role(trade.sell) == 'initiator':
|
||||||
print("You are the seller in this trade.")
|
print("You are the seller in this trade.")
|
||||||
role = 'seller'
|
# role = 'seller'
|
||||||
checkSellStatus(tradeid)
|
checkSellStatus(tradeid)
|
||||||
else:
|
else:
|
||||||
print("You are the buyer in this trade.")
|
print("You are the buyer in this trade.")
|
||||||
role = 'buyer'
|
# role = 'buyer'
|
||||||
checkBuyStatus(tradeid)
|
checkBuyStatus(tradeid)
|
||||||
|
|
||||||
|
|
||||||
def newtrade(tradeid, **kwargs):
|
def newtrade(tradeid, **kwargs):
|
||||||
|
protocol = Protocol()
|
||||||
print("Creating new XCAT trade...")
|
print("Creating new XCAT trade...")
|
||||||
erase_trade()
|
utils.erase_trade()
|
||||||
tradeid, trade= initialize_trade(tradeid, conf=kwargs['conf'], network=kwargs['network'])
|
|
||||||
|
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)
|
||||||
print("New trade created: {0}".format(trade))
|
print("New trade created: {0}".format(trade))
|
||||||
trade = seller_init(tradeid, trade, network=kwargs['network'])
|
|
||||||
print("\nUse 'xcat exporttrade [tradeid]' to export the trade and sent to the buyer.\n")
|
trade = protocol.seller_init(tradeid, trade, network=network)
|
||||||
|
print("\nUse 'xcat exporttrade [tradeid]' to export the trade and sent "
|
||||||
|
"to the buyer.\n")
|
||||||
|
|
||||||
save_state(trade, tradeid)
|
save_state(trade, tradeid)
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
|
|
||||||
def listtrades():
|
def listtrades():
|
||||||
|
db = DB()
|
||||||
print("Trades")
|
print("Trades")
|
||||||
trades = db.dump()
|
trade_list = db.dump()
|
||||||
for trade in trades:
|
for trade in trade_list:
|
||||||
print("{0}: {1}".format(trade[0], trade[1]))
|
print("{0}: {1}".format(trade[0], trade[1]))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
|
parser = argparse.ArgumentParser(
|
||||||
|
formatter_class=argparse.RawTextHelpFormatter,
|
||||||
description=textwrap.dedent('''\
|
description=textwrap.dedent('''\
|
||||||
== Trades ==
|
== Trades ==
|
||||||
newtrade "tradeid" - create a new trade
|
newtrade "tradeid" - create a new trade
|
||||||
|
@ -199,72 +266,105 @@ def main():
|
||||||
findtrade "tradeid" - find a trade by the tradeid
|
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")
|
parser.add_argument(
|
||||||
parser.add_argument("-d", "--debug", action="store_true", help="Enable debug mode. Defaults to false")
|
"command", action="store", help="list commands")
|
||||||
parser.add_argument("-w", "--wormhole", action="store_true", help="Transfer trade data through magic-wormhole")
|
parser.add_argument(
|
||||||
parser.add_argument("-c", "--conf", action="store", help="Use default trade data in conf file.")
|
"arguments", action="store", nargs="*", help="add arguments")
|
||||||
parser.add_argument("-n", "--network", action="store", help="Set network to regtest or mainnet. Defaults to testnet while in alpha.")
|
parser.add_argument(
|
||||||
# parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process")
|
"-w", "--wormhole", action="store_true",
|
||||||
|
help="Transfer trade data through magic-wormhole")
|
||||||
|
parser.add_argument(
|
||||||
|
"-c", "--conf", action="store",
|
||||||
|
help="Use default trade data in conf file.")
|
||||||
|
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()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.debug:
|
if hasattr(args, 'debug'):
|
||||||
numeric_level = getattr(logging, 'DEBUG', None)
|
numeric_level = getattr(logging, 'DEBUG', None)
|
||||||
logging.basicConfig(format='%(levelname)s: %(message)s', level=numeric_level)
|
logging.basicConfig(format='%(levelname)s: %(message)s',
|
||||||
|
level=numeric_level)
|
||||||
else:
|
else:
|
||||||
logging.basicConfig(format='%(levelname)s: %(message)s', level='INFO')
|
logging.basicConfig(format='%(levelname)s: %(message)s',
|
||||||
|
level='INFO')
|
||||||
|
|
||||||
if args.network:
|
if hasattr(args, 'network'):
|
||||||
NETWORK = args.network
|
NETWORK = args.network
|
||||||
else:
|
else:
|
||||||
NETWORK = 'testnet'
|
NETWORK = 'testnet'
|
||||||
|
|
||||||
command = args.command
|
command = args.command
|
||||||
|
|
||||||
if command == 'importtrade':
|
if command == 'importtrade':
|
||||||
if args.wormhole:
|
if args.wormhole:
|
||||||
wormhole_importtrade()
|
wormhole_importtrade()
|
||||||
else:
|
else:
|
||||||
if len(args.arguments) != 2: throw("Usage: importtrade [tradeid] [hexstring]")
|
if len(args.arguments) != 2:
|
||||||
|
utils.throw("Usage: importtrade [tradeid] [hexstring]")
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
hexstr = args.arguments[1]
|
hexstr = args.arguments[1]
|
||||||
importtrade(tradeid, hexstr)
|
importtrade(tradeid, hexstr)
|
||||||
|
|
||||||
elif command == 'exporttrade':
|
elif command == 'exporttrade':
|
||||||
if len(args.arguments) < 1: throw("Usage: exporttrade [tradeid]")
|
if len(args.arguments) < 1:
|
||||||
|
utils.throw("Usage: exporttrade [tradeid]")
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
exporttrade(tradeid, args.wormhole)
|
exporttrade(tradeid, args.wormhole)
|
||||||
|
|
||||||
elif command == "findtrade":
|
elif command == "findtrade":
|
||||||
if len(args.arguments) < 1: throw("Usage: findtrade [tradeid]")
|
if len(args.arguments) < 1:
|
||||||
|
utils.throw("Usage: findtrade [tradeid]")
|
||||||
print("Finding trade")
|
print("Finding trade")
|
||||||
key = args.arguments[0]
|
key = args.arguments[0]
|
||||||
findtrade(key)
|
findtrade(key)
|
||||||
|
|
||||||
elif command == 'checktrade':
|
elif command == 'checktrade':
|
||||||
if len(args.arguments) < 1: throw("Usage: checktrade [tradeid]")
|
if len(args.arguments) < 1:
|
||||||
|
utils.throw("Usage: checktrade [tradeid]")
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
checktrade(tradeid)
|
checktrade(tradeid)
|
||||||
|
|
||||||
elif command == 'listtrades':
|
elif command == 'listtrades':
|
||||||
listtrades()
|
listtrades()
|
||||||
|
|
||||||
# TODO: function to tell if tradeid already exists for newtrade
|
# TODO: function to tell if tradeid already exists for newtrade
|
||||||
|
|
||||||
elif command == 'newtrade':
|
elif command == 'newtrade':
|
||||||
if len(args.arguments) < 1: throw("Usage: newtrade [tradeid]")
|
if len(args.arguments) < 1:
|
||||||
|
utils.throw("Usage: newtrade [tradeid]")
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
if args.conf == None:
|
if args.conf is None:
|
||||||
newtrade(tradeid, network=NETWORK, conf='cli')
|
newtrade(tradeid, network=NETWORK, conf='cli')
|
||||||
else:
|
else:
|
||||||
newtrade(tradeid, network=NETWORK, conf=args.conf)
|
newtrade(tradeid, network=NETWORK, conf=args.conf)
|
||||||
|
|
||||||
elif command == "daemon":
|
elif command == "daemon":
|
||||||
#TODO: not implemented
|
# TODO: not implemented
|
||||||
print("Run as daemon process")
|
print("Run as daemon process")
|
||||||
|
|
||||||
# Ad hoc testing of workflow starts here
|
# Ad hoc testing of workflow starts here
|
||||||
|
|
||||||
elif command == "step1":
|
elif command == "step1":
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
checkSellStatus(tradeid)
|
checkSellStatus(tradeid)
|
||||||
|
|
||||||
elif command == "step2":
|
elif command == "step2":
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
checkBuyStatus(tradeid)
|
checkBuyStatus(tradeid)
|
||||||
|
|
||||||
elif command == "step3":
|
elif command == "step3":
|
||||||
# generate(31)
|
# protocol = Protocol()
|
||||||
|
# protocol.generate(31)
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
checkSellStatus(tradeid)
|
checkSellStatus(tradeid)
|
||||||
|
|
||||||
elif command == "step4":
|
elif command == "step4":
|
||||||
# generate(1)
|
# generate(1)
|
||||||
tradeid = args.arguments[0]
|
tradeid = args.arguments[0]
|
||||||
|
|
124
xcat/db.py
124
xcat/db.py
|
@ -1,77 +1,77 @@
|
||||||
import plyvel
|
import plyvel
|
||||||
from xcat.utils import *
|
|
||||||
import binascii
|
|
||||||
import sys
|
|
||||||
import json
|
import json
|
||||||
import ast
|
import xcat.utils as utils
|
||||||
from xcat.trades import *
|
from xcat.trades import Trade
|
||||||
|
|
||||||
db = plyvel.DB('/tmp/xcatDB', create_if_missing=True)
|
|
||||||
preimageDB = plyvel.DB('/tmp/preimageDB', create_if_missing=True)
|
|
||||||
|
|
||||||
#############################################
|
class DB():
|
||||||
######## Trades stored by tradeid ###########
|
|
||||||
#############################################
|
|
||||||
|
|
||||||
# Takes dict or obj, saves json str as bytes
|
def __init__(self):
|
||||||
def create(trade, tradeid):
|
self.db = plyvel.DB('/tmp/xcatDB', create_if_missing=True)
|
||||||
if type(trade) == dict:
|
self.preimageDB = plyvel.DB('/tmp/preimageDB', create_if_missing=True)
|
||||||
trade = json.dumps(trade)
|
|
||||||
else:
|
|
||||||
trade = trade.toJSON()
|
|
||||||
db.put(b(tradeid), b(trade))
|
|
||||||
|
|
||||||
# Uses the funding txid as the key to save trade
|
#############################################
|
||||||
def createByFundtx(trade):
|
######## Trades stored by tradeid ###########
|
||||||
trade = trade.toJSON()
|
#############################################
|
||||||
# # Save trade by initiating txid
|
|
||||||
jt = json.loads(trade)
|
|
||||||
txid = jt['sell']['fund_tx']
|
|
||||||
db.put(b(txid), b(trade))
|
|
||||||
|
|
||||||
def get(tradeid):
|
# Takes dict or obj, saves json str as bytes
|
||||||
rawtrade = db.get(b(tradeid))
|
def create(self, trade, tradeid):
|
||||||
tradestr = str(rawtrade, 'utf-8')
|
if isinstance(trade, dict):
|
||||||
trade = instantiate(tradestr)
|
trade = json.dumps(trade, sort_keys=True, indent=4)
|
||||||
return trade
|
elif isinstance(trade, Trade):
|
||||||
|
trade = trade.toJSON()
|
||||||
|
else:
|
||||||
|
raise ValueError('Expected dictionary or Trade object')
|
||||||
|
self.db.put(utils.b(tradeid), utils.b(trade))
|
||||||
|
|
||||||
def instantiate(trade):
|
# Uses the funding txid as the key to save trade
|
||||||
if type(trade) == str:
|
def createByFundtx(self, trade):
|
||||||
tradestr = json.loads(trade)
|
if isinstance(trade, dict):
|
||||||
trade = Trade(buy=Contract(tradestr['buy']), sell=Contract(tradestr['sell']), commitment=tradestr['commitment'])
|
txid = trade['sell']['fund_tx']
|
||||||
|
trade = json.dumps(trade, sort_keys=True, indent=4)
|
||||||
|
elif isinstance(trade, Trade):
|
||||||
|
txid = trade.sell.fund_tx
|
||||||
|
trade = trade.toJSON()
|
||||||
|
else:
|
||||||
|
raise ValueError('Expected dictionary or Trade object')
|
||||||
|
self.db.put(utils.b(txid), utils.b(trade))
|
||||||
|
|
||||||
|
def get(self, tradeid):
|
||||||
|
rawtrade = self.db.get(utils.b(tradeid))
|
||||||
|
tradestr = str(rawtrade, 'utf-8')
|
||||||
|
trade = Trade(fromJSON=tradestr)
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
###### Preimages stored by tradeid ##########
|
###### Preimages stored by tradeid ##########
|
||||||
#############################################
|
#############################################
|
||||||
|
|
||||||
# Stores secret locally in key/value store by tradeid
|
# Stores secret locally in key/value store by tradeid
|
||||||
def save_secret(tradeid, secret):
|
def save_secret(self, tradeid, secret):
|
||||||
res = preimageDB.put(b(tradeid), b(secret))
|
self.preimageDB.put(utils.b(tradeid), utils.b(secret))
|
||||||
|
|
||||||
def get_secret(tradeid):
|
def get_secret(self, tradeid):
|
||||||
secret = preimageDB.get(b(tradeid))
|
secret = self.preimageDB.get(utils.b(tradeid))
|
||||||
secret = str(secret, 'utf-8')
|
secret = str(secret, 'utf-8')
|
||||||
return secret
|
return secret
|
||||||
|
|
||||||
|
#############################################
|
||||||
|
########## Dump or view db entries ##########
|
||||||
|
#############################################
|
||||||
|
|
||||||
#############################################
|
def dump(self):
|
||||||
########## Dump or view db entries ##########
|
results = []
|
||||||
#############################################
|
with self.db.iterator() as it:
|
||||||
|
for k, v in it:
|
||||||
|
j = json.loads(str(v, 'utf-8'))
|
||||||
|
results.append((str(k, 'utf-8'), j))
|
||||||
|
return results
|
||||||
|
|
||||||
def dump():
|
def print_entries(self):
|
||||||
results = []
|
it = self.db.iterator()
|
||||||
with db.iterator() as it:
|
with self.db.iterator() as it:
|
||||||
for k, v in it:
|
for k, v in it:
|
||||||
j = json.loads(x2s(b2x(v)))
|
j = json.loads(utils.x2s(utils.b2x(v)))
|
||||||
results.append((str(k, 'utf-8'), j))
|
print("Key:", k)
|
||||||
return results
|
print('val: ', j)
|
||||||
|
# print('sell: ', j['sell'])
|
||||||
def print_entries():
|
|
||||||
it = db.iterator()
|
|
||||||
with db.iterator() as it:
|
|
||||||
for k, v in it:
|
|
||||||
j = json.loads(x2s(b2x(v)))
|
|
||||||
print("Key:", k)
|
|
||||||
print('val: ', j)
|
|
||||||
# print('sell: ', j['sell'])
|
|
||||||
|
|
396
xcat/protocol.py
396
xcat/protocol.py
|
@ -1,219 +1,237 @@
|
||||||
import json
|
import logging
|
||||||
import os, sys
|
|
||||||
from pprint import pprint
|
|
||||||
from xcat.utils import *
|
|
||||||
from xcat.trades import Contract, Trade
|
|
||||||
import xcat.userInput as userInput
|
import xcat.userInput as userInput
|
||||||
import xcat.db as db
|
import xcat.utils as utils
|
||||||
from xcat.xcatconf import *
|
from xcat.xcatconf import ADDRS
|
||||||
|
from xcat.trades import Contract, Trade
|
||||||
from xcat.bitcoinRPC import bitcoinProxy
|
from xcat.bitcoinRPC import bitcoinProxy
|
||||||
from xcat.zcashRPC import zcashProxy
|
from xcat.zcashRPC import zcashProxy
|
||||||
import logging
|
from xcat.db import DB
|
||||||
|
|
||||||
bitcoinRPC = bitcoinProxy()
|
|
||||||
zcashRPC = zcashProxy()
|
|
||||||
|
|
||||||
def generate(num):
|
class Protocol():
|
||||||
bitcoinRPC.generate(num)
|
|
||||||
zcashRPC.generate(num)
|
|
||||||
|
|
||||||
def is_myaddr(address):
|
def __init__(self):
|
||||||
# Handle different network prefixes
|
self.bitcoinRPC = bitcoinProxy()
|
||||||
if address[:1] == 'm':
|
self.zcashRPC = zcashProxy()
|
||||||
status = bitcoinRPC.validateaddress(address)
|
|
||||||
else:
|
|
||||||
status = zcashRPC.validateaddress(address)
|
|
||||||
logging.debug("Address status: ", status)
|
|
||||||
if status['isvalid'] is False:
|
|
||||||
raise ValueError("Invalid address: %s" % address)
|
|
||||||
elif 'ismine' in status:
|
|
||||||
status = status['ismine']
|
|
||||||
return status
|
|
||||||
|
|
||||||
def find_secret_from_fundtx(currency, p2sh, fundtx):
|
def generate(self, num):
|
||||||
if currency == 'bitcoin':
|
self.bitcoinRPC.generate(num)
|
||||||
secret = bitcoinRPC.find_secret(p2sh, fundtx)
|
self.zcashRPC.generate(num)
|
||||||
elif currency == 'zcash':
|
|
||||||
secret = zcashRPC.find_secret(p2sh, fundtx)
|
|
||||||
else:
|
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
|
||||||
return secret
|
|
||||||
|
|
||||||
def import_addrs(trade):
|
def is_myaddr(self, address):
|
||||||
check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
# Handle differnt network prefixes
|
||||||
check_fund_status(trade.buy.currency, trade.buy.p2sh)
|
if address[:1] == 'm':
|
||||||
|
status = self.bitcoinRPC.validateaddress(address)
|
||||||
|
else:
|
||||||
|
status = self.zcashRPC.validateaddress(address)
|
||||||
|
|
||||||
def check_p2sh(currency, address):
|
logging.debug("Address status: ", status)
|
||||||
if currency == 'bitcoin':
|
|
||||||
print("Checking funds in Bitcoin p2sh")
|
|
||||||
return bitcoinRPC.check_funds(address)
|
|
||||||
elif currency == 'zcash':
|
|
||||||
print("Checking funds in Zcash p2sh")
|
|
||||||
return zcashRPC.check_funds(address)
|
|
||||||
else:
|
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
|
||||||
|
|
||||||
def check_fund_status(currency, address):
|
if not status['isvalid']:
|
||||||
if currency == 'bitcoin':
|
raise ValueError("Invalid address: %s" % address)
|
||||||
print("Checking funds in Bitcoin p2sh")
|
elif 'ismine' in status:
|
||||||
return bitcoinRPC.get_fund_status(address)
|
status = status['ismine']
|
||||||
elif currency == 'zcash':
|
# print("Address {0} is mine: {1}".format(address, status))
|
||||||
print("Checking funds in Zcash p2sh")
|
return status
|
||||||
return zcashRPC.get_fund_status(address)
|
|
||||||
else:
|
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
|
||||||
|
|
||||||
# TODO: function to calculate appropriate locktimes between chains
|
def find_secret_from_fundtx(self, currency, p2sh, fundtx):
|
||||||
# def verify_p2sh(trade):
|
if currency == 'bitcoin':
|
||||||
# htlc = create_htlc(trade.buy.currency, trade.buy.fulfiller, trade.buy.initiator, trade.commitment, trade.buy.locktime)
|
secret = self.bitcoinRPC.find_secret(p2sh, fundtx)
|
||||||
# buyer_p2sh = htlc['p2sh']
|
elif currency == 'zcash':
|
||||||
# print("Buyer p2sh:", buyer_p2sh)
|
secret = self.zcashRPC.find_secret(p2sh, fundtx)
|
||||||
# If the two p2sh match...
|
else:
|
||||||
# if buyer_p2sh == trade.buy.p2sh:
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
# else:
|
return secret
|
||||||
# print("Compiled p2sh for htlc does not match what seller sent.")
|
|
||||||
|
|
||||||
def create_htlc(currency, funder, redeemer, commitment, locktime):
|
def import_addrs(self, trade):
|
||||||
if currency == 'bitcoin':
|
self.check_fund_status(trade.sell.currency, trade.sell.p2sh)
|
||||||
sell_p2sh = bitcoinRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
self.check_fund_status(trade.buy.currency, trade.buy.p2sh)
|
||||||
elif currency == 'zcash':
|
|
||||||
sell_p2sh = zcashRPC.hashtimelockcontract(funder, redeemer, commitment, locktime)
|
|
||||||
else:
|
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
|
||||||
return sell_p2sh
|
|
||||||
|
|
||||||
def fund_htlc(currency, p2sh, amount):
|
def check_p2sh(self, currency, address):
|
||||||
if currency == 'bitcoin':
|
if currency == 'bitcoin':
|
||||||
txid = bitcoinRPC.fund_htlc(p2sh, amount)
|
print("Checking funds in Bitcoin p2sh")
|
||||||
elif currency == 'zcash':
|
return self.bitcoinRPC.check_funds(address)
|
||||||
txid = zcashRPC.fund_htlc(p2sh, amount)
|
elif currency == 'zcash':
|
||||||
else:
|
print("Checking funds in Zcash p2sh")
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
return self.zcashRPC.check_funds(address)
|
||||||
return txid
|
else:
|
||||||
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
|
|
||||||
def redeem_p2sh(contract, secret):
|
def check_fund_status(self, currency, address):
|
||||||
currency = contract.currency
|
if currency == 'bitcoin':
|
||||||
if currency == 'bitcoin':
|
print("Checking funds in Bitcoin p2sh")
|
||||||
res = bitcoinRPC.redeem_contract(contract, secret)
|
return self.bitcoinRPC.get_fund_status(address)
|
||||||
elif currency == 'zcash':
|
elif currency == 'zcash':
|
||||||
res = zcashRPC.redeem_contract(contract, secret)
|
print("Checking funds in Zcash p2sh")
|
||||||
else:
|
return self.zcashRPC.get_fund_status(address)
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
else:
|
||||||
return res
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
|
|
||||||
def refund_contract(contract):
|
# TODO: function to calculate appropriate locktimes between chains
|
||||||
currency = contract.currency
|
# def verify_p2sh(trade):
|
||||||
if currency == 'bitcoin':
|
# htlc = self.create_htlc(
|
||||||
res = bitcoinRPC.refund(contract)
|
# trade.buy.currency, trade.buy.fulfiller,
|
||||||
elif currency == 'zcash':
|
# trade.buy.initiator, trade.commitment,
|
||||||
res = zcashRPC.refund(contract)
|
# trade.buy.locktime)
|
||||||
else:
|
# buyer_p2sh = htlc['p2sh']
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
# print("Buyer p2sh:", buyer_p2sh)
|
||||||
return res
|
# If the two p2sh match...
|
||||||
|
# if buyer_p2sh == trade.buy.p2sh:
|
||||||
|
# else:
|
||||||
|
# print("Compiled p2sh for htlc does not match what seller sent.")
|
||||||
|
|
||||||
def parse_secret(currency, txid):
|
def create_htlc(self, currency, funder, redeemer, commitment, locktime):
|
||||||
if currency == 'bitcoin':
|
if currency == 'bitcoin':
|
||||||
secret = bitcoinRPC.parse_secret(txid)
|
sell_p2sh = self.bitcoinRPC.hashtimelockcontract(
|
||||||
elif currency == 'zcash':
|
funder, redeemer, commitment, locktime)
|
||||||
secret = zcashRPC.parse_secret(txid)
|
elif currency == 'zcash':
|
||||||
else:
|
sell_p2sh = self.zcashRPC.hashtimelockcontract(
|
||||||
raise ValueError("Currency not recognized: ", currency)
|
funder, redeemer, commitment, locktime)
|
||||||
return secret
|
else:
|
||||||
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
|
return sell_p2sh
|
||||||
|
|
||||||
def fund_contract(contract):
|
def fund_htlc(self, currency, p2sh, amount):
|
||||||
txid = fund_htlc(contract.currency, contract.p2sh, contract.amount)
|
if currency == 'bitcoin':
|
||||||
return txid
|
txid = self.bitcoinRPC.fund_htlc(p2sh, amount)
|
||||||
|
elif currency == 'zcash':
|
||||||
|
txid = self.zcashRPC.fund_htlc(p2sh, amount)
|
||||||
|
else:
|
||||||
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
|
return txid
|
||||||
|
|
||||||
def fund_sell_contract(trade):
|
def fund_contract(self, contract):
|
||||||
sell = trade.sell
|
txid = self.fund_htlc(
|
||||||
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
contract.currency, contract.p2sh, contract.amount)
|
||||||
setattr(trade.sell, 'fund_tx', txid)
|
return txid
|
||||||
save(trade)
|
|
||||||
return txid
|
|
||||||
|
|
||||||
def create_sell_p2sh(trade, commitment, locktime):
|
def fund_sell_contract(self, trade):
|
||||||
# CREATE SELL CONTRACT
|
sell = trade.sell
|
||||||
sell = trade.sell
|
txid = self.fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
||||||
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, commitment, locktime)
|
setattr(trade.sell, 'fund_tx', txid)
|
||||||
print("sell contract", contract)
|
utils.save(trade)
|
||||||
setattr(trade.sell, 'p2sh', contract['p2sh'])
|
return txid
|
||||||
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):
|
def create_sell_p2sh(self, trade, commitment, locktime):
|
||||||
## CREATE BUY CONTRACT
|
# CREATE SELL CONTRACT
|
||||||
buy = trade.buy
|
sell = trade.sell
|
||||||
print("\nNow creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
|
contract = self.create_htlc(sell.currency, sell.initiator,
|
||||||
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, commitment, locktime)
|
sell.fulfiller, commitment, locktime)
|
||||||
print("Buy contract", buy_contract)
|
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'])
|
||||||
|
utils.save(trade)
|
||||||
|
|
||||||
setattr(trade.buy, 'p2sh', buy_contract['p2sh'])
|
def create_buy_p2sh(self, trade, commitment, locktime):
|
||||||
setattr(trade.buy, 'redeemScript', buy_contract['redeemScript'])
|
# CREATE BUY CONTRACT
|
||||||
setattr(trade.buy, 'redeemblocknum', buy_contract['redeemblocknum'])
|
buy = trade.buy
|
||||||
setattr(trade.buy, 'locktime', buy_contract['locktime'])
|
print("\nNow creating buy contract on the {0} blockchain where you "
|
||||||
print("\nNow contact the buyer and tell them to send funds to this p2sh: {0}\n".format(trade.buy.p2sh))
|
"will wait for the buyer to send funds...".format(buy.currency))
|
||||||
|
buy_contract = self.create_htlc(
|
||||||
|
buy.currency, buy.fulfiller, buy.initiator, commitment, locktime)
|
||||||
|
print("Buy contract", buy_contract)
|
||||||
|
|
||||||
save(trade)
|
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))
|
||||||
|
|
||||||
#### Main functions related to user flow from command line
|
utils.save(trade)
|
||||||
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 initialize_trade(tradeid, **kwargs):
|
def redeem_p2sh(self, contract, secret):
|
||||||
trade = Trade()
|
currency = contract.currency
|
||||||
conf = kwargs['conf']
|
if currency == 'bitcoin':
|
||||||
if conf == 'cli':
|
res = self.bitcoinRPC.redeem_contract(contract, secret)
|
||||||
init_addrs = userInput.get_initiator_addresses()
|
elif currency == 'zcash':
|
||||||
fulfill_addrs = userInput.get_fulfiller_addresses()
|
res = self.zcashRPC.redeem_contract(contract, secret)
|
||||||
amounts = userInput.get_trade_amounts()
|
else:
|
||||||
print("AMOUNTS", amounts)
|
raise ValueError('Currency not recognized: %s' % currency)
|
||||||
else:
|
return res
|
||||||
init_addrs = ADDRS[conf]['initiator']
|
|
||||||
fulfill_addrs = ADDRS[conf]['fulfiller']
|
|
||||||
amounts = ADDRS[conf]['amounts']
|
|
||||||
|
|
||||||
sell = amounts['sell']
|
def refund_contract(self, contract):
|
||||||
buy = amounts['buy']
|
currency = contract.currency
|
||||||
sell_currency = sell['currency']
|
if currency == 'bitcoin':
|
||||||
buy_currency = buy['currency']
|
res = self.bitcoinRPC.refund(contract)
|
||||||
sell['initiator'] = init_addrs[sell_currency]
|
elif currency == 'zcash':
|
||||||
buy['initiator'] = init_addrs[buy_currency]
|
res = self.zcashRPC.refund(contract)
|
||||||
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
else:
|
||||||
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
raise ValueError('Currency not recognized: %s', currency)
|
||||||
|
return res
|
||||||
|
|
||||||
# initializing contract classes with addresses, currencies, and amounts
|
def parse_secret(self, currency, txid):
|
||||||
trade.sell = Contract(sell)
|
if currency == 'bitcoin':
|
||||||
trade.buy = Contract(buy)
|
secret = self.bitcoinRPC.parse_secret(txid)
|
||||||
print(trade.sell.__dict__)
|
elif currency == 'zcash':
|
||||||
print(trade.buy.__dict__)
|
secret = self.zcashRPC.parse_secret(txid)
|
||||||
return tradeid, trade
|
else:
|
||||||
|
raise ValueError('Currency not recognized: %s', currency)
|
||||||
|
return secret
|
||||||
|
|
||||||
def seller_init(tradeid, trade, network):
|
def seller_redeem_p2sh(self, trade, secret):
|
||||||
secret = generate_password()
|
buy = trade.buy
|
||||||
db.save_secret(tradeid, secret)
|
userInput.authorize_seller_redeem(buy)
|
||||||
print("Generated a secret preimage to lock funds. This will only be stored locally: {0}".format(secret))
|
|
||||||
|
|
||||||
hash_of_secret = sha256(secret)
|
if trade.sell.get_status() == 'redeemed':
|
||||||
# TODO: Implement locktimes and mock block passage of time
|
print("You already redeemed the funds and acquired "
|
||||||
sell_locktime = 20
|
"{0} {1}".format(buy.amount, buy.currency))
|
||||||
buy_locktime = 10 # Must be more than first tx
|
exit()
|
||||||
print("Creating pay-to-script-hash for sell contract...")
|
else:
|
||||||
|
# Seller redeems buyer's funded tx (contract in p2sh)
|
||||||
|
txs = self.redeem_p2sh(trade.buy, secret)
|
||||||
|
print("You have redeemed "
|
||||||
|
"{0} {1}!".format(buy.amount, buy.currency))
|
||||||
|
return txs
|
||||||
|
|
||||||
# create the p2sh addrs
|
def initialize_trade(self, tradeid, **kwargs):
|
||||||
create_sell_p2sh(trade, hash_of_secret, sell_locktime)
|
trade = Trade()
|
||||||
create_buy_p2sh(trade, hash_of_secret, buy_locktime)
|
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']
|
||||||
|
|
||||||
trade.commitment = b2x(hash_of_secret)
|
sell = amounts['sell']
|
||||||
print("TRADE after seller init: {0}".format(trade.toJSON()))
|
buy = amounts['buy']
|
||||||
return trade
|
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(self, tradeid, trade, network):
|
||||||
|
db = DB()
|
||||||
|
secret = utils.generate_password()
|
||||||
|
db.save_secret(tradeid, secret)
|
||||||
|
print("\nGenerated a secret preimage to lock funds. This will only "
|
||||||
|
"be stored locally: {0}".format(secret))
|
||||||
|
|
||||||
|
hash_of_secret = utils.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
|
||||||
|
self.create_sell_p2sh(trade, hash_of_secret, sell_locktime)
|
||||||
|
self.create_buy_p2sh(trade, hash_of_secret, buy_locktime)
|
||||||
|
|
||||||
|
trade.commitment = utils.b2x(hash_of_secret)
|
||||||
|
print("TRADE after seller init {0}".format(trade.toJSON()))
|
||||||
|
return trade
|
||||||
|
|
|
@ -1,36 +1,45 @@
|
||||||
import unittest
|
import unittest
|
||||||
import xcat.cli as cli
|
import xcat.cli as cli
|
||||||
import xcat.db as db
|
import xcat.tests.utils as testutils
|
||||||
from xcat.tests.utils import mktrade
|
from xcat.db import DB
|
||||||
from xcat.trades import Trade, Contract
|
from xcat.protocol import Protocol
|
||||||
|
from xcat.trades import Trade # , Contract
|
||||||
|
|
||||||
|
|
||||||
class SimpleTestCase(unittest.TestCase):
|
class SimpleTestCase(unittest.TestCase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.trade = mktrade()
|
self.trade = testutils.mktrade()
|
||||||
|
|
||||||
def test_exporttrade(self):
|
def test_exporttrade(self):
|
||||||
self.__class__.hexstr = cli.exporttrade('test')
|
self.__class__.hexstr = cli.exporttrade('test')
|
||||||
self.assertTrue(int(self.hexstr, 16))
|
self.assertTrue(int(self.hexstr, 16))
|
||||||
|
|
||||||
def test_importtrade(self):
|
def test_importtrade(self):
|
||||||
trade = cli.importtrade('test', self.__class__.hexstr)
|
# trade = cli.importtrade('test', self.__class__.hexstr)
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class CliTest(SimpleTestCase):
|
class CliTest(SimpleTestCase):
|
||||||
|
|
||||||
def test_findtrade(self):
|
def test_findtrade(self):
|
||||||
trade = cli.findtrade('test')
|
# trade = cli.findtrade('test')
|
||||||
|
pass
|
||||||
|
|
||||||
def test_newtrade(self):
|
def test_newtrade(self):
|
||||||
trade = cli.newtrade('new', conf='regtest')
|
trade = cli.newtrade('new', conf='regtest')
|
||||||
self.assertTrue(isinstance(trade, Trade))
|
self.assertTrue(isinstance(trade, Trade))
|
||||||
|
|
||||||
def test_fundsell(self):
|
def test_fundsell(self):
|
||||||
|
db = DB()
|
||||||
|
protocol = Protocol()
|
||||||
|
|
||||||
trade = db.get('new')
|
trade = db.get('new')
|
||||||
|
|
||||||
status = cli.seller_check_status(trade)
|
status = cli.seller_check_status(trade)
|
||||||
print("Trade status: {0}\n".format(status))
|
print("Trade status: {0}\n".format(status))
|
||||||
self.assertEqual(status, 'init')
|
self.assertEqual(status, 'init')
|
||||||
fund_tx = cli.fund_sell_contract(trade)
|
|
||||||
|
fund_tx = protocol.fund_sell_contract(trade)
|
||||||
print("Sent fund_tx", fund_tx)
|
print("Sent fund_tx", fund_tx)
|
||||||
|
|
||||||
# def test_fundbuy(self):
|
# def test_fundbuy(self):
|
||||||
|
|
|
@ -1,21 +1,25 @@
|
||||||
import xcat.database as db
|
import unittest
|
||||||
import unittest, json
|
|
||||||
import xcat.trades as trades
|
import xcat.trades as trades
|
||||||
|
from xcat.db import DB
|
||||||
|
|
||||||
|
|
||||||
class DatabaseTest(unittest.TestCase):
|
class DatabaseTest(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.data = {"sell": {"amount": 0.1, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967022a04b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "redeemblocknum": 1066, "currency": "bitcoin", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "p2sh": "2MuYSQ1uQ4CJg5Y5QL2vMmVPHNJ2KT5aJ6f", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "fund_tx": "5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d59511566a2fdb"}, "buy": {"amount": 0.2, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b167023f0db17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "redeemblocknum": 3391, "currency": "zcash", "locktime": 10, "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "p2sh": "t2HP59RpfR34nBCWH4VVD497tkc2ikzgniP", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY"}, "commitment": "03d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b6"}
|
self.data = {"sell": {"amount": 0.1, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967022a04b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "redeemblocknum": 1066, "currency": "bitcoin", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "p2sh": "2MuYSQ1uQ4CJg5Y5QL2vMmVPHNJ2KT5aJ6f", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "fund_tx": "5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d59511566a2fdb"}, "buy": {"amount": 0.2, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b167023f0db17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "redeemblocknum": 3391, "currency": "zcash", "locktime": 10, "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "p2sh": "t2HP59RpfR34nBCWH4VVD497tkc2ikzgniP", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY"}, "commitment": "03d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b6"}
|
||||||
self.sell = trades.Contract(self.data['sell'])
|
self.sell = trades.Contract(self.data['sell'])
|
||||||
|
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
|
db = DB()
|
||||||
sell = trades.Contract(self.data['sell'])
|
sell = trades.Contract(self.data['sell'])
|
||||||
buy = trades.Contract(self.data['buy'])
|
buy = trades.Contract(self.data['buy'])
|
||||||
trade = trades.Trade(sell, buy, commitment=self.data['commitment'])
|
trade = trades.Trade(sell, buy, commitment=self.data['commitment'])
|
||||||
db.create(trade, 'test')
|
db.create(trade, 'test')
|
||||||
|
|
||||||
def test_get(self):
|
def test_get(self):
|
||||||
trade = db.get('test')
|
# trade = db.get('test')
|
||||||
print("Trade")
|
print("Trade")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,133 @@
|
||||||
|
import unittest
|
||||||
|
import unittest.mock as mock
|
||||||
|
from xcat.bitcoinRPC import bitcoinProxy
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('xcat.bitcoinRPC.bitcoin.rpc')
|
||||||
|
class TestBitcoinProxy(unittest.TestCase):
|
||||||
|
"""Test case for the bitcoinProxy class."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
logging.disable(logging.CRITICAL)
|
||||||
|
|
||||||
|
@mock.patch('xcat.bitcoinRPC.bitcoin.SelectParams')
|
||||||
|
def test_init_with_testnet(self, mock_SP, mock_rpc):
|
||||||
|
"""Test bitcoinProxy.__init__"""
|
||||||
|
|
||||||
|
proxy = bitcoinProxy(network='testnet')
|
||||||
|
|
||||||
|
mock_rpc.Proxy.assert_called_with(timeout=900)
|
||||||
|
mock_SP.assert_called_with('testnet')
|
||||||
|
self.assertIsInstance(proxy, bitcoinProxy)
|
||||||
|
|
||||||
|
@mock.patch('xcat.bitcoinRPC.bitcoin.SelectParams')
|
||||||
|
def test_init_with_no_network(self, mock_SP, mock_rpc):
|
||||||
|
"""Test bitcoinProxy.__init__"""
|
||||||
|
|
||||||
|
proxy = bitcoinProxy()
|
||||||
|
|
||||||
|
mock_rpc.Proxy.assert_called_with(timeout=900)
|
||||||
|
mock_SP.assert_called_with('regtest')
|
||||||
|
self.assertIsInstance(proxy, bitcoinProxy)
|
||||||
|
|
||||||
|
def test_init_with_invalid(self, mock_rpc):
|
||||||
|
"""Test bitcoinProxy.__init__"""
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
proxy = bitcoinProxy(network='invalid input')
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Allowed networks are regtest, testnet, mainnet.'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context_two:
|
||||||
|
proxy = bitcoinProxy(network=819.3)
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Allowed networks are regtest, testnet, mainnet.'
|
||||||
|
in str(context_two.exception))
|
||||||
|
|
||||||
|
def test_init_with_invalid_timeout(self, mock_rpc):
|
||||||
|
"""Test bitcoinProxy.__init__"""
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
proxy = bitcoinProxy(timeout='invalid input')
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Timeout should be a positive integer.'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context_two:
|
||||||
|
proxy = bitcoinProxy(timeout=-381)
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Timeout should be a positive integer.'
|
||||||
|
in str(context_two.exception))
|
||||||
|
|
||||||
|
def test_validateaddress(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_secret(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_parse_secret(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_keys(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_privkey(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_hashtimelockcontract(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_fund_htlc(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_check_funds(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_fund_status(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_search_p2sh(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_tx_details(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_redeem_contract(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_redeem(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_refund(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_parse_script(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_redeemblocknum(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_redeemAddr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_refundAddr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_transaction_to_address(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_new_bitcoin_addr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_generate(self, mock_rpc):
|
||||||
|
pass
|
|
@ -0,0 +1,116 @@
|
||||||
|
import unittest
|
||||||
|
import unittest.mock as mock
|
||||||
|
import xcat.cli as cli
|
||||||
|
# from xcat.tests.utils import test_trade
|
||||||
|
# from xcat.trades import Trade
|
||||||
|
|
||||||
|
|
||||||
|
class TestCLI(unittest.TestCase):
|
||||||
|
|
||||||
|
@mock.patch('xcat.cli.DB')
|
||||||
|
@mock.patch('xcat.cli.utils')
|
||||||
|
def test_save_state(self, mock_utils, mock_db):
|
||||||
|
cli.save_state('fake_trade', 'fake_id')
|
||||||
|
|
||||||
|
mock_utils.save.assert_called_with('fake_trade')
|
||||||
|
mock_db.return_value.create.assert_called_with('fake_trade', 'fake_id')
|
||||||
|
|
||||||
|
def test_checkSellStatus(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_buyer_check_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_seller_check_status(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_checkBuyStatus(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_importtrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_wormhole_importtrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_exporttrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_findtrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@mock.patch('xcat.cli.Protocol')
|
||||||
|
def test_find_role_test(self, mock_protocol):
|
||||||
|
mock_protocol().is_myaddr = lambda k: k == 'me'
|
||||||
|
|
||||||
|
test_contract = mock.MagicMock()
|
||||||
|
test_contract.initiator = 'me'
|
||||||
|
test_contract.fulfiller = 'me'
|
||||||
|
|
||||||
|
res = cli.find_role(test_contract)
|
||||||
|
|
||||||
|
self.assertEqual(res, 'test')
|
||||||
|
|
||||||
|
@mock.patch('xcat.cli.Protocol')
|
||||||
|
def test_find_role_initiator(self, mock_protocol):
|
||||||
|
mock_protocol().is_myaddr = lambda k: k == 'me'
|
||||||
|
|
||||||
|
test_contract = mock.MagicMock()
|
||||||
|
test_contract.initiator = 'me'
|
||||||
|
test_contract.fulfiller = 'you'
|
||||||
|
|
||||||
|
res = cli.find_role(test_contract)
|
||||||
|
|
||||||
|
self.assertEqual(res, 'initiator')
|
||||||
|
|
||||||
|
@mock.patch('xcat.cli.Protocol')
|
||||||
|
def test_find_role_fulfiller(self, mock_protocol):
|
||||||
|
mock_protocol().is_myaddr = lambda k: k == 'me'
|
||||||
|
|
||||||
|
test_contract = mock.MagicMock()
|
||||||
|
test_contract.initiator = 'you'
|
||||||
|
test_contract.fulfiller = 'me'
|
||||||
|
|
||||||
|
res = cli.find_role(test_contract)
|
||||||
|
|
||||||
|
self.assertEqual(res, 'fulfiller')
|
||||||
|
|
||||||
|
@mock.patch('xcat.cli.Protocol')
|
||||||
|
def test_find_role_error(self, mock_protocol):
|
||||||
|
mock_protocol().is_myaddr = lambda k: k == 'me'
|
||||||
|
|
||||||
|
test_contract = mock.MagicMock()
|
||||||
|
test_contract.initiator = 'you'
|
||||||
|
test_contract.fulfiller = 'you'
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
cli.find_role(test_contract)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'You are not a participant in this contract.'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
def test_checktrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_newtrade(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_listtrades(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_fundsell(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_fundbuy(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_seller_redeem(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_buyer_redeem(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
unittest.main()
|
|
@ -0,0 +1,98 @@
|
||||||
|
import unittest
|
||||||
|
import unittest.mock as mock
|
||||||
|
import json
|
||||||
|
import xcat.db as db
|
||||||
|
import xcat.tests.utils as utils
|
||||||
|
|
||||||
|
|
||||||
|
class TestDB(unittest.TestCase):
|
||||||
|
|
||||||
|
@mock.patch('xcat.db.plyvel')
|
||||||
|
def setUp(self, mock_plyvel):
|
||||||
|
self.db = db.DB()
|
||||||
|
|
||||||
|
def test_init(self):
|
||||||
|
self.assertIsInstance(self.db.db, mock.Mock)
|
||||||
|
self.assertIsInstance(self.db.preimageDB, mock.Mock)
|
||||||
|
|
||||||
|
def test_create_with_dict(self):
|
||||||
|
test_id = 'test trade id'
|
||||||
|
|
||||||
|
self.db.create(utils.test_trade_dict, test_id)
|
||||||
|
|
||||||
|
self.db.db.put.assert_called_with(
|
||||||
|
str.encode(test_id),
|
||||||
|
str.encode(str(utils.test_trade)))
|
||||||
|
|
||||||
|
def test_create_with_trade(self):
|
||||||
|
test_id = 'test trade id'
|
||||||
|
|
||||||
|
self.db.create(utils.test_trade, test_id)
|
||||||
|
|
||||||
|
self.db.db.put.assert_called_with(
|
||||||
|
str.encode(test_id),
|
||||||
|
str.encode(json.dumps(utils.test_trade_dict,
|
||||||
|
sort_keys=True,
|
||||||
|
indent=4)))
|
||||||
|
|
||||||
|
def test_create_with_error(self):
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
self.db.create('this is not valid input', 'trade_id')
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Expected dictionary or Trade object'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
def test_createByFundtx_with_dict(self):
|
||||||
|
self.db.createByFundtx(utils.test_trade_dict)
|
||||||
|
|
||||||
|
self.db.db.put.assert_called_with(
|
||||||
|
str.encode('5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d'
|
||||||
|
'59511566a2fdb'),
|
||||||
|
str.encode(str(utils.test_trade)))
|
||||||
|
|
||||||
|
def test_createByFundtx_with_trade(self):
|
||||||
|
self.db.createByFundtx(utils.test_trade)
|
||||||
|
|
||||||
|
self.db.db.put.assert_called_with(
|
||||||
|
str.encode('5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d'
|
||||||
|
'59511566a2fdb'),
|
||||||
|
str.encode(json.dumps(utils.test_trade_dict,
|
||||||
|
sort_keys=True,
|
||||||
|
indent=4)))
|
||||||
|
|
||||||
|
def test_createByFundtx_with_error(self):
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
self.db.createByFundtx('this is not valid input')
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Expected dictionary or Trade object'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
self.db.db.get.return_value = str.encode(utils.test_trade.toJSON())
|
||||||
|
|
||||||
|
trade = self.db.get('test')
|
||||||
|
|
||||||
|
self.assertEqual(trade, utils.test_trade)
|
||||||
|
|
||||||
|
def test_save_secret(self):
|
||||||
|
self.db.save_secret('my life', 'I like black liquorice')
|
||||||
|
|
||||||
|
self.db.preimageDB.put.assert_called_with(
|
||||||
|
str.encode('my life'),
|
||||||
|
str.encode('I like black liquorice'))
|
||||||
|
|
||||||
|
def test_get_secret(self):
|
||||||
|
self.db.preimageDB.get.return_value = str.encode(
|
||||||
|
'I like black liquorice')
|
||||||
|
|
||||||
|
secret = self.db.get_secret('my life')
|
||||||
|
|
||||||
|
self.assertEqual(secret, 'I like black liquorice')
|
||||||
|
|
||||||
|
def test_dump(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_print_entries(self):
|
||||||
|
pass
|
|
@ -0,0 +1,133 @@
|
||||||
|
import unittest
|
||||||
|
import unittest.mock as mock
|
||||||
|
from xcat.zcashRPC import zcashProxy
|
||||||
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
@mock.patch('xcat.zcashRPC.zcash.rpc')
|
||||||
|
class TestBitcoinProxy(unittest.TestCase):
|
||||||
|
"""Test case for the zcashProxy class."""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
logging.disable(logging.CRITICAL)
|
||||||
|
|
||||||
|
@mock.patch('xcat.zcashRPC.zcash.SelectParams')
|
||||||
|
def test_init_with_testnet(self, mock_SP, mock_rpc):
|
||||||
|
"""Test zcashProxy.__init__"""
|
||||||
|
|
||||||
|
proxy = zcashProxy(network='testnet')
|
||||||
|
|
||||||
|
mock_rpc.Proxy.assert_called_with(timeout=900)
|
||||||
|
mock_SP.assert_called_with('testnet')
|
||||||
|
self.assertIsInstance(proxy, zcashProxy)
|
||||||
|
|
||||||
|
@mock.patch('xcat.zcashRPC.zcash.SelectParams')
|
||||||
|
def test_init_with_no_network(self, mock_SP, mock_rpc):
|
||||||
|
"""Test zcashProxy.__init__"""
|
||||||
|
|
||||||
|
proxy = zcashProxy()
|
||||||
|
|
||||||
|
mock_rpc.Proxy.assert_called_with(timeout=900)
|
||||||
|
mock_SP.assert_called_with('regtest')
|
||||||
|
self.assertIsInstance(proxy, zcashProxy)
|
||||||
|
|
||||||
|
def test_init_with_invalid(self, mock_rpc):
|
||||||
|
"""Test zcashProxy.__init__"""
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
proxy = zcashProxy(network='invalid input')
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Allowed networks are regtest, testnet, mainnet.'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context_two:
|
||||||
|
proxy = zcashProxy(network=819.3)
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Allowed networks are regtest, testnet, mainnet.'
|
||||||
|
in str(context_two.exception))
|
||||||
|
|
||||||
|
def test_init_with_invalid_timeout(self, mock_rpc):
|
||||||
|
"""Test zcashProxy.__init__"""
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context:
|
||||||
|
proxy = zcashProxy(timeout='invalid input')
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Timeout should be a positive integer.'
|
||||||
|
in str(context.exception))
|
||||||
|
|
||||||
|
with self.assertRaises(ValueError) as context_two:
|
||||||
|
proxy = zcashProxy(timeout=-381)
|
||||||
|
self.assertIsNone(proxy)
|
||||||
|
|
||||||
|
self.assertTrue(
|
||||||
|
'Timeout should be a positive integer.'
|
||||||
|
in str(context_two.exception))
|
||||||
|
|
||||||
|
def test_validateaddress(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_secret(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_parse_secret(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_keys(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_privkey(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_hashtimelockcontract(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_fund_htlc(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_check_funds(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_fund_status(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_search_p2sh(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_get_tx_details(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_redeem_contract(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_redeem(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_refund(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_parse_script(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_redeemblocknum(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_redeemAddr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_refundAddr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_find_transaction_to_address(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_new_bitcoin_addr(self, mock_rpc):
|
||||||
|
pass
|
||||||
|
|
||||||
|
def test_generate(self, mock_rpc):
|
||||||
|
pass
|
|
@ -1,8 +1,36 @@
|
||||||
import xcat.db as db
|
from xcat.db import DB
|
||||||
|
from xcat.trades import Contract, Trade
|
||||||
|
|
||||||
|
test_trade_dict = {
|
||||||
|
"sell": {
|
||||||
|
"amount": 3.5,
|
||||||
|
"redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967022a04b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac",
|
||||||
|
"redeemblocknum": 1066,
|
||||||
|
"currency": "bitcoin",
|
||||||
|
"initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp",
|
||||||
|
"p2sh": "2MuYSQ1uQ4CJg5Y5QL2vMmVPHNJ2KT5aJ6f",
|
||||||
|
"fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z",
|
||||||
|
"fund_tx": "5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d59511566a2fdb"},
|
||||||
|
"buy": {
|
||||||
|
"amount": 1.2,
|
||||||
|
"redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b167023f0db17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac",
|
||||||
|
"redeemblocknum": 3391,
|
||||||
|
"currency": "zcash",
|
||||||
|
"locktime": 10,
|
||||||
|
"initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ",
|
||||||
|
"p2sh": "t2HP59RpfR34nBCWH4VVD497tkc2ikzgniP",
|
||||||
|
"fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY"},
|
||||||
|
"commitment": "03d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b6"}
|
||||||
|
|
||||||
|
test_sell_contract = Contract(test_trade_dict['sell'])
|
||||||
|
test_buy_contract = Contract(test_trade_dict['buy'])
|
||||||
|
test_trade = Trade(sell=test_sell_contract,
|
||||||
|
buy=test_buy_contract,
|
||||||
|
commitment=test_trade_dict['commitment'])
|
||||||
|
|
||||||
test_trade = {"sell": {"amount": 3.5, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967022a04b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "redeemblocknum": 1066, "currency": "bitcoin", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "p2sh": "2MuYSQ1uQ4CJg5Y5QL2vMmVPHNJ2KT5aJ6f", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "fund_tx": "5c5e91a89a08b2d6698f50c9fd9bb2fa22da6c74e226c3dd63d59511566a2fdb"}, "buy": {"amount": 1.2, "redeemScript": "63a82003d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b167023f0db17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "redeemblocknum": 3391, "currency": "zcash", "locktime": 10, "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "p2sh": "t2HP59RpfR34nBCWH4VVD497tkc2ikzgniP", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY"}, "commitment": "03d58daab37238604b3e57d4a8bdcffa401dc497a9c1aa4f08ffac81616c22b6"}
|
|
||||||
|
|
||||||
def mktrade():
|
def mktrade():
|
||||||
|
db = DB()
|
||||||
db.create(test_trade, 'test')
|
db.create(test_trade, 'test')
|
||||||
trade = db.get('test')
|
trade = db.get('test')
|
||||||
return trade
|
return trade
|
||||||
|
|
|
@ -1,21 +1,56 @@
|
||||||
import json
|
import json
|
||||||
|
|
||||||
class Trade(object):
|
|
||||||
def __init__(self, sell=None, buy=None, commitment=None):
|
class Trade():
|
||||||
'''Create a new trade with a sell contract and buy contract across two chains'''
|
def __init__(self, sell=None, buy=None, commitment=None,
|
||||||
self.sell = sell
|
fromJSON=None, fromDict=None):
|
||||||
self.buy = buy
|
'''Create a new trade with buy and sell contracts across two chains'''
|
||||||
self.commitment = commitment
|
|
||||||
|
if fromJSON is not None and fromDict is None:
|
||||||
|
if isinstance(fromJSON, str):
|
||||||
|
fromDict = json.loads(fromJSON)
|
||||||
|
else:
|
||||||
|
raise ValueError('Expected json string')
|
||||||
|
if fromDict is not None:
|
||||||
|
self.sell = Contract(fromDict['sell'])
|
||||||
|
self.buy = Contract(fromDict['buy'])
|
||||||
|
self.commitment = fromDict['commitment']
|
||||||
|
else:
|
||||||
|
self.sell = sell
|
||||||
|
self.buy = buy
|
||||||
|
self.commitment = commitment
|
||||||
|
|
||||||
def toJSON(self):
|
def toJSON(self):
|
||||||
return json.dumps(self, default=lambda o: o.__dict__,
|
return json.dumps(
|
||||||
sort_keys=True, indent=4)
|
self, default=lambda o: o.__dict__, sort_keys=True, indent=4)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.toJSON()
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return 'Trade:\n{0} {1} from {2}\nfor\n{3} {4} from {5}'.format(
|
||||||
|
self.sell.amount,
|
||||||
|
self.sell.currency,
|
||||||
|
self.sell.initiator,
|
||||||
|
self.buy.amount,
|
||||||
|
self.buy.currency,
|
||||||
|
self.buy.initiator)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
return (self.sell == other.sell
|
||||||
|
and self.buy == other.buy
|
||||||
|
and self.commitment == other.commitment)
|
||||||
|
|
||||||
|
|
||||||
|
class Contract():
|
||||||
|
|
||||||
|
allowed = ('fulfiller', 'initiator', 'currency', 'p2sh', 'amount',
|
||||||
|
'fund_tx', 'redeem_tx', 'secret', 'redeemScript',
|
||||||
|
'redeemblocknum', 'locktime')
|
||||||
|
|
||||||
class Contract(object):
|
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
allowed = ('fulfiller', 'initiator', 'currency', 'p2sh', 'amount', 'fund_tx', 'redeem_tx', 'secret', 'redeemScript', 'redeemblocknum', 'locktime')
|
|
||||||
for key in data:
|
for key in data:
|
||||||
if key in allowed:
|
if key in Contract.allowed:
|
||||||
setattr(self, key, data[key])
|
setattr(self, key, data[key])
|
||||||
|
|
||||||
def get_status(self):
|
def get_status(self):
|
||||||
|
@ -28,3 +63,14 @@ class Contract(object):
|
||||||
return 'funded'
|
return 'funded'
|
||||||
else:
|
else:
|
||||||
return 'empty'
|
return 'empty'
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
for key in Contract.allowed:
|
||||||
|
if key in self.__dict__:
|
||||||
|
if key not in other.__dict__:
|
||||||
|
return False
|
||||||
|
if self.__dict__[key] != other.__dict__[key]:
|
||||||
|
return False
|
||||||
|
if key in other.__dict__ and key not in self.__dict__:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
from xcat.utils import *
|
# from xcat.utils import *
|
||||||
from xcat.db import *
|
# from xcat.db import *
|
||||||
from xcat.bitcoinRPC import bitcoinProxy
|
from xcat.bitcoinRPC import bitcoinProxy
|
||||||
from xcat.zcashRPC import zcashProxy
|
from xcat.zcashRPC import zcashProxy
|
||||||
from xcat.xcatconf import *
|
# from xcat.xcatconf import *
|
||||||
|
|
||||||
|
|
||||||
def enter_trade_id():
|
def enter_trade_id():
|
||||||
tradeid = input("Enter a unique identifier for this trade: ")
|
tradeid = input("Enter a unique identifier for this trade: ")
|
||||||
return tradeid
|
return tradeid
|
||||||
|
|
||||||
|
|
||||||
def get_trade_amounts():
|
def get_trade_amounts():
|
||||||
amounts = {}
|
amounts = {}
|
||||||
sell_currency = input("Which currency would you like to trade out of (bitcoin or zcash)? ")
|
sell_currency = input("Which currency would you like to trade out of "
|
||||||
if sell_currency == '' or sell_currency == 'bitcoin' :
|
"(bitcoin or zcash)? ")
|
||||||
|
if sell_currency == '' or sell_currency == 'bitcoin':
|
||||||
sell_currency = 'bitcoin'
|
sell_currency = 'bitcoin'
|
||||||
buy_currency = 'zcash'
|
buy_currency = 'zcash'
|
||||||
elif sell_currency == 'zcash':
|
elif sell_currency == 'zcash':
|
||||||
|
@ -20,11 +23,13 @@ def get_trade_amounts():
|
||||||
else:
|
else:
|
||||||
raise ValueError('Mistyped or unspported cryptocurrency pair')
|
raise ValueError('Mistyped or unspported cryptocurrency pair')
|
||||||
print(sell_currency)
|
print(sell_currency)
|
||||||
sell_amt = input("How much {0} do you want to sell? ".format(sell_currency))
|
sell_amt = input("How much {0} do you "
|
||||||
|
"want to sell? ".format(sell_currency))
|
||||||
if sell_amt == '':
|
if sell_amt == '':
|
||||||
sell_amt = 0.01
|
sell_amt = 0.01
|
||||||
print(sell_amt)
|
print(sell_amt)
|
||||||
buy_amt = input("How much {0} do you want to receive in exchange? ".format(buy_currency))
|
buy_amt = input("How much {0} do you "
|
||||||
|
"want to receive in exchange? ".format(buy_currency))
|
||||||
if buy_amt == '':
|
if buy_amt == '':
|
||||||
buy_amt = 0.02
|
buy_amt = 0.02
|
||||||
print(buy_amt)
|
print(buy_amt)
|
||||||
|
@ -34,40 +39,66 @@ def get_trade_amounts():
|
||||||
amounts['buy'] = buy
|
amounts['buy'] = buy
|
||||||
return amounts
|
return amounts
|
||||||
|
|
||||||
|
|
||||||
def authorize_fund_sell(htlcTrade):
|
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))
|
print('To complete your sell, send {0} {1} to this p2sh: '
|
||||||
response = input("Type 'enter' to allow this program to send funds on your behalf.")
|
'{2}'.format(htlcTrade.sell.amount,
|
||||||
|
htlcTrade.sell.currency,
|
||||||
|
htlcTrade.sell.p2sh))
|
||||||
|
input("Type 'enter' to allow this program to send funds on your behalf.")
|
||||||
|
|
||||||
|
|
||||||
def get_initiator_addresses():
|
def get_initiator_addresses():
|
||||||
bitcoinRPC = bitcoinProxy()
|
bitcoinRPC = bitcoinProxy()
|
||||||
zcashRPC = zcashProxy()
|
zcashRPC = zcashProxy()
|
||||||
btc_addr = input("Enter your bitcoin address or press enter to generate one: ")
|
btc_addr = input("Enter your bitcoin address "
|
||||||
|
"or press enter to generate one: ")
|
||||||
btc_addr = bitcoinRPC.new_bitcoin_addr()
|
btc_addr = bitcoinRPC.new_bitcoin_addr()
|
||||||
print(btc_addr)
|
print(btc_addr)
|
||||||
zec_addr = input("Enter your zcash address or press enter to generate one: ")
|
zec_addr = input("Enter your zcash address "
|
||||||
|
"or press enter to generate one: ")
|
||||||
zec_addr = zcashRPC.new_zcash_addr()
|
zec_addr = zcashRPC.new_zcash_addr()
|
||||||
print(zec_addr)
|
print(zec_addr)
|
||||||
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
|
|
||||||
def get_fulfiller_addresses():
|
def get_fulfiller_addresses():
|
||||||
btc_addr = input("Enter the bitcoin address of the party you want to trade with: ")
|
btc_addr = input("Enter the bitcoin address of "
|
||||||
|
"the party you want to trade with: ")
|
||||||
if btc_addr == '':
|
if btc_addr == '':
|
||||||
btc_addr = "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b" # regtest
|
btc_addr = "mvc56qCEVj6p57xZ5URNC3v7qbatudHQ9b" # regtest
|
||||||
print(btc_addr)
|
print(btc_addr)
|
||||||
zec_addr = input("Enter the zcash address of the party you want to trade with: ")
|
|
||||||
|
zec_addr = input("Enter the zcash address of "
|
||||||
|
"the party you want to trade with: ")
|
||||||
if zec_addr == '':
|
if zec_addr == '':
|
||||||
zec_addr = "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc" # regtest
|
zec_addr = "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc" # regtest
|
||||||
print(zec_addr)
|
print(zec_addr)
|
||||||
|
|
||||||
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
addresses = {'bitcoin': btc_addr, 'zcash': zec_addr}
|
||||||
return addresses
|
return addresses
|
||||||
|
|
||||||
def authorize_buyer_fulfill(sell_p2sh_balance, sell_currency, buy_p2sh_balance, buy_currency):
|
|
||||||
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))
|
def authorize_buyer_fulfill(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))
|
buy_p2sh_balance, buy_currency):
|
||||||
|
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))
|
||||||
|
|
||||||
|
|
||||||
def authorize_seller_redeem(buy):
|
def authorize_seller_redeem(buy):
|
||||||
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))
|
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))
|
||||||
|
|
||||||
|
|
||||||
def authorize_buyer_redeem(trade):
|
def authorize_buyer_redeem(trade):
|
||||||
input("Seller funded the contract where you paid them in {0} to buy {1}, type 'enter' to redeem {2} {1} from {3}.".format(trade.buy.currency, trade.sell.currency, trade.sell.amount, trade.sell.p2sh))
|
input("Seller funded the contract where you paid them in {0} "
|
||||||
|
"to buy {1}, type 'enter' to redeem {2} {1} from "
|
||||||
|
"{3}.".format(trade.buy.currency, trade.sell.currency,
|
||||||
|
trade.sell.amount, trade.sell.p2sh))
|
||||||
|
|
|
@ -1,80 +1,102 @@
|
||||||
import hashlib, json, random, binascii
|
import hashlib
|
||||||
import xcat.trades as trades
|
import json
|
||||||
|
import random
|
||||||
|
import binascii
|
||||||
import os
|
import os
|
||||||
|
import xcat.trades as trades
|
||||||
|
|
||||||
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
ROOT_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
########### Data conversion utils ##########
|
########### Data conversion utils ##########
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
|
|
||||||
def b(string):
|
def b(string):
|
||||||
"""Convert a string to bytes"""
|
"""Convert a string to bytes"""
|
||||||
return str.encode(string)
|
return str.encode(string)
|
||||||
|
|
||||||
|
|
||||||
def x(h):
|
def x(h):
|
||||||
"""Convert a hex string to bytes"""
|
"""Convert a hex string to bytes"""
|
||||||
return binascii.unhexlify(h.encode('utf8'))
|
return binascii.unhexlify(h.encode('utf8'))
|
||||||
|
|
||||||
|
|
||||||
def b2x(b):
|
def b2x(b):
|
||||||
"""Convert bytes to a hex string"""
|
"""Convert bytes to a hex string"""
|
||||||
return binascii.hexlify(b).decode('utf8')
|
return binascii.hexlify(b).decode('utf8')
|
||||||
|
|
||||||
|
|
||||||
def x2s(hexstring):
|
def x2s(hexstring):
|
||||||
"""Convert hex to a utf-8 string"""
|
"""Convert hex to a utf-8 string"""
|
||||||
return binascii.unhexlify(hexstring).decode('utf-8')
|
return binascii.unhexlify(hexstring).decode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def s2x(string):
|
def s2x(string):
|
||||||
"""Convert a utf-8 string to hex"""
|
"""Convert a utf-8 string to hex"""
|
||||||
return b2x(b(string))
|
return b2x(b(string))
|
||||||
|
|
||||||
|
|
||||||
def hex2dict(hexstr):
|
def hex2dict(hexstr):
|
||||||
jsonstr = x2s(hexstr)
|
jsonstr = x2s(hexstr)
|
||||||
print(hexstr['fund_tx'])
|
print(hexstr['fund_tx'])
|
||||||
print(jsonstr)
|
print(jsonstr)
|
||||||
return json.loads(jsonstr)
|
return json.loads(jsonstr)
|
||||||
|
|
||||||
|
|
||||||
def jsonformat(trade):
|
def jsonformat(trade):
|
||||||
return {
|
return {
|
||||||
'sell': trade.sell.__dict__,
|
'sell': trade.sell.__dict__,
|
||||||
'buy': trade.buyContract.__dict__
|
'buy': trade.buyContract.__dict__
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
########### Preimage utils #################
|
########### Preimage utils #################
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
|
|
||||||
def generate_password():
|
def generate_password():
|
||||||
s = "1234567890abcdefghijklmnopqrstuvwxyz01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
s = ("1234567890abcdefghijklmnopqrstuvwxyz"
|
||||||
|
"01234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ")
|
||||||
passlen = 32
|
passlen = 32
|
||||||
p = "".join(random.sample(s,passlen))
|
p = "".join(random.sample(s, passlen))
|
||||||
return p
|
return p
|
||||||
|
|
||||||
|
|
||||||
def sha256(secret):
|
def sha256(secret):
|
||||||
preimage = secret.encode('utf8')
|
preimage = secret.encode('utf8')
|
||||||
h = hashlib.sha256(preimage).digest()
|
h = hashlib.sha256(preimage).digest()
|
||||||
return h
|
return h
|
||||||
|
|
||||||
|
|
||||||
############################################
|
############################################
|
||||||
######## Error handling for CLI ############
|
######## Error handling for CLI ############
|
||||||
############################################
|
############################################
|
||||||
|
|
||||||
|
|
||||||
def throw(err):
|
def throw(err):
|
||||||
print(err)
|
print(err)
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
|
||||||
#############################################
|
#############################################
|
||||||
######### xcat.json temp file #############
|
######### xcat.json temp file #############
|
||||||
#############################################
|
#############################################
|
||||||
|
|
||||||
|
|
||||||
tmp_dir = os.path.join(ROOT_DIR, '.tmp')
|
tmp_dir = os.path.join(ROOT_DIR, '.tmp')
|
||||||
if not os.path.exists(tmp_dir):
|
if not os.path.exists(tmp_dir):
|
||||||
os.makedirs(tmp_dir)
|
os.makedirs(tmp_dir)
|
||||||
xcatjson = os.path.join(tmp_dir, 'xcat.json')
|
xcatjson = os.path.join(tmp_dir, 'xcat.json')
|
||||||
|
|
||||||
|
|
||||||
def save_trade(trade):
|
def save_trade(trade):
|
||||||
with open(xcatjson, 'w+') as outfile:
|
with open(xcatjson, 'w+') as outfile:
|
||||||
json.dump(trade, outfile)
|
json.dump(trade, outfile)
|
||||||
|
|
||||||
|
|
||||||
def get_trade():
|
def get_trade():
|
||||||
with open(xcatjson) as data_file:
|
with open(xcatjson) as data_file:
|
||||||
xcatdb = json.load(data_file)
|
xcatdb = json.load(data_file)
|
||||||
|
@ -83,6 +105,7 @@ def get_trade():
|
||||||
trade = trades.Trade(sell, buy, commitment=xcatdb['commitment'])
|
trade = trades.Trade(sell, buy, commitment=xcatdb['commitment'])
|
||||||
return trade
|
return trade
|
||||||
|
|
||||||
|
|
||||||
def erase_trade():
|
def erase_trade():
|
||||||
try:
|
try:
|
||||||
with open(xcatjson, 'w') as outfile:
|
with open(xcatjson, 'w') as outfile:
|
||||||
|
@ -90,15 +113,17 @@ def erase_trade():
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
def save(trade):
|
def save(trade):
|
||||||
# print("Saving trade")
|
# print("Saving trade")
|
||||||
trade = {
|
trade = {
|
||||||
'sell': trade.sell.__dict__,
|
'sell': trade.sell.__dict__,
|
||||||
'buy': trade.buy.__dict__,
|
'buy': trade.buy.__dict__,
|
||||||
'commitment': trade.commitment
|
'commitment': trade.commitment
|
||||||
}
|
}
|
||||||
save_trade(trade)
|
save_trade(trade)
|
||||||
|
|
||||||
|
|
||||||
# Remove tmp files when trade is complete
|
# Remove tmp files when trade is complete
|
||||||
def cleanup(tradeid):
|
def cleanup(tradeid):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -17,11 +17,9 @@ ADDRS = {
|
||||||
"zcash": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"
|
"zcash": "tmTF7LMLjvEsGdcepWPUsh4vgJNrKMWwEyc"
|
||||||
},
|
},
|
||||||
"fulfiller": {
|
"fulfiller": {
|
||||||
"bitcoin": "mn2boR7rYq9DaAWWrVN5MazHKFyf7UhdyU",
|
"bitcoin": "mm2smEJjRN4xoijEfpb5XvYd8e3EYWezom",
|
||||||
"zcash": "tmErB22A1G74aq32aAh5AoqgQSJsAAAdT2p"
|
"zcash": "tmPwPdceaJAHQn7UiRCVnJ5tXBXHVqWMkis"
|
||||||
},
|
},
|
||||||
"amounts": {'buy': {'currency': 'zcash', 'amount': 0.02}, 'sell': {'currency': 'bitcoin', 'amount': 0.01}}
|
"amounts": {'buy': {'currency': 'zcash', 'amount': 0.02}, 'sell': {'currency': 'bitcoin', 'amount': 0.01}}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NETWORK = 'testnet'
|
|
||||||
|
|
|
@ -1,29 +1,40 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import zcash
|
||||||
|
import zcash.rpc
|
||||||
|
# import logging
|
||||||
|
# from zcash import SelectParams
|
||||||
|
from zcash.core import b2x, lx, x, COIN
|
||||||
|
from zcash.core import CMutableTransaction, CMutableTxOut, CMutableTxIn
|
||||||
|
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF
|
||||||
|
from zcash.core.script import OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG
|
||||||
|
from zcash.core.script import SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP
|
||||||
|
from zcash.core.script import OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
|
||||||
|
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
||||||
|
from zcash.wallet import CBitcoinAddress, P2PKHBitcoinAddress
|
||||||
|
from zcash.wallet import P2SHBitcoinAddress
|
||||||
|
|
||||||
|
from xcat.utils import x2s
|
||||||
|
|
||||||
if sys.version_info.major < 3:
|
if sys.version_info.major < 3:
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
import zcash
|
FEE = 0.001 * COIN
|
||||||
import zcash.rpc
|
|
||||||
from zcash import SelectParams
|
|
||||||
from zcash.core import b2x, lx, x, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
|
||||||
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, OP_FALSE, OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
|
|
||||||
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
|
||||||
from zcash.wallet import CBitcoinAddress, CBitcoinSecret, P2SHBitcoinAddress, P2PKHBitcoinAddress
|
|
||||||
import logging
|
|
||||||
|
|
||||||
from xcat.utils import x2s
|
|
||||||
|
|
||||||
FEE = 0.001*COIN
|
|
||||||
|
|
||||||
class zcashProxy():
|
class zcashProxy():
|
||||||
def __init__(self, network='regtest', timeout=900):
|
def __init__(self, network='regtest', timeout=900):
|
||||||
|
if network not in ['testnet', 'mainnet', 'regtest']:
|
||||||
|
raise ValueError('Allowed networks are regtest, testnet, mainnet.')
|
||||||
|
if not isinstance(timeout, int) or timeout < 1:
|
||||||
|
raise ValueError('Timeout should be a positive integer.')
|
||||||
|
|
||||||
self.network = network
|
self.network = network
|
||||||
self.timeout = timeout
|
self.timeout = timeout
|
||||||
|
|
||||||
SelectParams(self.network)
|
zcash.SelectParams(self.network)
|
||||||
self.zcashd = zcash.rpc.Proxy(timeout=self.timeout)
|
self.zcashd = zcash.rpc.Proxy(timeout=self.timeout)
|
||||||
|
|
||||||
def validateaddress(self, addr):
|
def validateaddress(self, addr):
|
||||||
|
@ -47,23 +58,34 @@ class zcashProxy():
|
||||||
print("Current blocknum on Zcash: ", blocknum)
|
print("Current blocknum on Zcash: ", blocknum)
|
||||||
redeemblocknum = blocknum + locktime
|
redeemblocknum = blocknum + locktime
|
||||||
print("Redeemblocknum on Zcash: ", redeemblocknum)
|
print("Redeemblocknum on Zcash: ", redeemblocknum)
|
||||||
# can rm op_dup and op_hash160 if you replace addrs with pubkeys (as raw hex/bin data?), and can rm last op_equalverify (for direct pubkey comparison)
|
# can rm op_dup and op_hash160 if you replace addrs with pubkeys
|
||||||
zec_redeemScript = CScript([OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
# (as raw hex/bin data?)
|
||||||
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
# can rm last op_equalverify (for direct pubkey comparison)
|
||||||
funderAddr, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
zec_redeemScript = CScript([
|
||||||
# print("Redeem script for p2sh contract on Zcash blockchain: ", b2x(zec_redeemScript))
|
OP_IF, OP_SHA256, commitment, OP_EQUALVERIFY, OP_DUP, OP_HASH160,
|
||||||
|
redeemerAddr, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY,
|
||||||
|
OP_DROP, OP_DUP, OP_HASH160, funderAddr, OP_ENDIF, OP_EQUALVERIFY,
|
||||||
|
OP_CHECKSIG])
|
||||||
|
# print("Redeem script for p2sh contract on Zcash blockchain: ",
|
||||||
|
# b2x(zec_redeemScript))
|
||||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(
|
||||||
|
txin_scriptPubKey)
|
||||||
p2sh = str(txin_p2sh_address)
|
p2sh = str(txin_p2sh_address)
|
||||||
print("p2sh computed: ", p2sh)
|
print("p2sh computed: ", p2sh)
|
||||||
# Import address as soon as you create it
|
# Import address as soon as you create it
|
||||||
self.zcashd.importaddress(p2sh, "", False)
|
self.zcashd.importaddress(p2sh, "", False)
|
||||||
# Returning all this to be saved locally in p2sh.json
|
# Returning all this to be saved locally in p2sh.json
|
||||||
return {'p2sh': p2sh, 'redeemblocknum': redeemblocknum, 'redeemScript': b2x(zec_redeemScript), 'redeemer': redeemer, 'funder': funder, 'locktime': locktime}
|
return {'p2sh': p2sh,
|
||||||
|
'redeemblocknum': redeemblocknum,
|
||||||
|
'redeemScript': b2x(zec_redeemScript),
|
||||||
|
'redeemer': redeemer,
|
||||||
|
'funder': funder,
|
||||||
|
'locktime': locktime}
|
||||||
|
|
||||||
def fund_htlc(self, p2sh, amount):
|
def fund_htlc(self, p2sh, amount):
|
||||||
send_amount = float(amount)*COIN
|
send_amount = float(amount) * COIN
|
||||||
# Import addr at same time as you fund
|
# Import addr at same time as you fund
|
||||||
self.zcashd.importaddress(p2sh, "", False)
|
self.zcashd.importaddress(p2sh, "", False)
|
||||||
fund_txid = self.zcashd.sendtoaddress(p2sh, send_amount)
|
fund_txid = self.zcashd.sendtoaddress(p2sh, send_amount)
|
||||||
|
@ -75,13 +97,13 @@ class zcashProxy():
|
||||||
self.zcashd.importaddress(p2sh, "", False)
|
self.zcashd.importaddress(p2sh, "", False)
|
||||||
# Get amount in address
|
# Get amount in address
|
||||||
amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
|
amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
|
||||||
amount = amount/COIN
|
amount = amount / COIN
|
||||||
return amount
|
return amount
|
||||||
|
|
||||||
def get_fund_status(self, p2sh):
|
def get_fund_status(self, p2sh):
|
||||||
self.zcashd.importaddress(p2sh, "", False)
|
self.zcashd.importaddress(p2sh, "", False)
|
||||||
amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
|
amount = self.zcashd.getreceivedbyaddress(p2sh, 0)
|
||||||
amount = amount/COIN
|
amount = amount / COIN
|
||||||
print("Amount in zcash p2sh: ", amount, p2sh)
|
print("Amount in zcash p2sh: ", amount, p2sh)
|
||||||
if amount > 0:
|
if amount > 0:
|
||||||
return 'funded'
|
return 'funded'
|
||||||
|
@ -108,7 +130,7 @@ class zcashProxy():
|
||||||
# print("TXINFO", decoded['vin'][0])
|
# print("TXINFO", decoded['vin'][0])
|
||||||
if('txid' in decoded['vin'][0]):
|
if('txid' in decoded['vin'][0]):
|
||||||
sendid = decoded['vin'][0]['txid']
|
sendid = decoded['vin'][0]['txid']
|
||||||
if (sendid == fundtx_input ):
|
if (sendid == fundtx_input):
|
||||||
print("Found funding tx: ", sendid)
|
print("Found funding tx: ", sendid)
|
||||||
return self.parse_secret(lx(tx['txid']))
|
return self.parse_secret(lx(tx['txid']))
|
||||||
print("Redeem transaction with secret not found")
|
print("Redeem transaction with secret not found")
|
||||||
|
@ -155,7 +177,7 @@ class zcashProxy():
|
||||||
def redeem_contract(self, contract, secret):
|
def redeem_contract(self, contract, secret):
|
||||||
# How to find redeemScript and redeemblocknum from blockchain?
|
# How to find redeemScript and redeemblocknum from blockchain?
|
||||||
p2sh = contract.p2sh
|
p2sh = contract.p2sh
|
||||||
#checking there are funds in the address
|
# checking there are funds in the address
|
||||||
amount = self.check_funds(p2sh)
|
amount = self.check_funds(p2sh)
|
||||||
if(amount == 0):
|
if(amount == 0):
|
||||||
print("Address ", p2sh, " not funded")
|
print("Address ", p2sh, " not funded")
|
||||||
|
@ -184,7 +206,8 @@ class zcashProxy():
|
||||||
print('redeemPubKey', redeemPubKey)
|
print('redeemPubKey', redeemPubKey)
|
||||||
zec_redeemScript = CScript(x(contract.redeemScript))
|
zec_redeemScript = CScript(x(contract.redeemScript))
|
||||||
txin = CMutableTxIn(fundtx['outpoint'])
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
txout = CMutableTxOut(fundtx['amount'] - FEE, redeemPubKey.to_scriptPubKey())
|
txout = CMutableTxOut(fundtx['amount'] - FEE,
|
||||||
|
redeemPubKey.to_scriptPubKey())
|
||||||
# Create the unsigned raw transaction.
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
||||||
|
@ -193,15 +216,17 @@ class zcashProxy():
|
||||||
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
|
||||||
print("SECRET", secret)
|
print("SECRET", secret)
|
||||||
preimage = secret.encode('utf-8')
|
preimage = secret.encode('utf-8')
|
||||||
txin.scriptSig = CScript([sig, privkey.pub, preimage, OP_TRUE, zec_redeemScript])
|
txin.scriptSig = CScript([sig, privkey.pub, preimage,
|
||||||
|
OP_TRUE, zec_redeemScript])
|
||||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
||||||
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
|
print('Raw redeem transaction hex: ', b2x(tx.serialize()))
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
VerifyScript(txin.scriptSig, txin_scriptPubKey,
|
||||||
|
tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
print("Script verified, sending raw redeem transaction...")
|
print("Script verified, sending raw redeem transaction...")
|
||||||
txid = self.zcashd.sendrawtransaction(tx)
|
txid = self.zcashd.sendrawtransaction(tx)
|
||||||
redeem_tx = b2x(lx(b2x(txid)))
|
redeem_tx = b2x(lx(b2x(txid)))
|
||||||
fund_tx = str(fundtx['outpoint'])
|
fund_tx = str(fundtx['outpoint'])
|
||||||
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
return {"redeem_tx": redeem_tx, "fund_tx": fund_tx}
|
||||||
|
|
||||||
def refund(self, contract):
|
def refund(self, contract):
|
||||||
fundtx = self.find_transaction_to_address(contract.p2sh)
|
fundtx = self.find_transaction_to_address(contract.p2sh)
|
||||||
|
@ -211,7 +236,9 @@ class zcashProxy():
|
||||||
|
|
||||||
redeemScript = CScript(x(contract.redeemScript))
|
redeemScript = CScript(x(contract.redeemScript))
|
||||||
txin = CMutableTxIn(fundtx['outpoint'])
|
txin = CMutableTxIn(fundtx['outpoint'])
|
||||||
txout = CMutableTxOut(fundtx['amount'] - FEE, refundPubKey.to_scriptPubKey())
|
txout = CMutableTxOut(fundtx['amount'] - FEE,
|
||||||
|
refundPubKey.to_scriptPubKey())
|
||||||
|
# Create the unsigned raw transaction.
|
||||||
tx = CMutableTransaction([txin], [txout])
|
tx = CMutableTransaction([txin], [txout])
|
||||||
# Set nSequence and nLockTime
|
# Set nSequence and nLockTime
|
||||||
txin.nSequence = 0
|
txin.nSequence = 0
|
||||||
|
@ -224,10 +251,11 @@ class zcashProxy():
|
||||||
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
txin.scriptSig = CScript([sig, privkey.pub, OP_FALSE, redeemScript])
|
||||||
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
txin_scriptPubKey = redeemScript.to_p2sh_scriptPubKey()
|
||||||
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
print('Raw redeem transaction hex: {0}'.format(b2x(tx.serialize())))
|
||||||
res = VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
res = VerifyScript(txin.scriptSig, txin_scriptPubKey,
|
||||||
|
tx, 0, (SCRIPT_VERIFY_P2SH,))
|
||||||
print("Script verified, sending raw transaction... (NOT)", res)
|
print("Script verified, sending raw transaction... (NOT)", res)
|
||||||
txid = self.zcashd.sendrawtransaction(tx)
|
txid = self.zcashd.sendrawtransaction(tx)
|
||||||
refund_tx = b2x(lx(b2x(txid)))
|
refund_tx = b2x(lx(b2x(txid)))
|
||||||
fund_tx = str(fundtx['outpoint'])
|
fund_tx = str(fundtx['outpoint'])
|
||||||
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
return {"refund_tx": refund_tx, "fund_tx": fund_tx}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue