Rm unnecessary files, rename sellContract to sell etc, save trades to leveldb
This commit is contained in:
parent
0df6fcd04c
commit
572de939b4
138
bitcoin-xcat.py
138
bitcoin-xcat.py
|
@ -1,138 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
|
||||||
# Copyright (C) 2017 The Zcash developers
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major < 3:
|
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import bitcoin
|
|
||||||
import bitcoin.rpc
|
|
||||||
from bitcoin import SelectParams
|
|
||||||
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
|
||||||
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
|
|
||||||
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
|
||||||
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
# SelectParams('testnet')
|
|
||||||
SelectParams('regtest')
|
|
||||||
bitcoind = bitcoin.rpc.Proxy()
|
|
||||||
FEE = 0.001*COIN
|
|
||||||
|
|
||||||
# ========================= BITCOIN ADDRESSES =========================
|
|
||||||
alice_address = input("Enter alice bitcoin address: (type 'enter' for demo)")
|
|
||||||
bob_address = input("Enter bob bitcoin address: (type 'enter' for demo)")
|
|
||||||
# alicepubkey = CBitcoinAddress('mshp4msfzc73ebg4VzwS6nAXj9t6KqX1wd')
|
|
||||||
# bobpubkey = CBitcoinAddress('myRh2T5Kg7QJfGLeRzriT5zs9aoek5Jbha')
|
|
||||||
# bitcoind.getnewaddress() returns CBitcoinAddress
|
|
||||||
bobpubkey = bitcoind.getnewaddress()
|
|
||||||
alicepubkey = bitcoind.getnewaddress()
|
|
||||||
print("alicepubkey", alicepubkey)
|
|
||||||
print("bobpubkey", bobpubkey)
|
|
||||||
# privkey of the bob, used to sign the redeemTx
|
|
||||||
bob_seckey = bitcoind.dumpprivkey(bobpubkey)
|
|
||||||
# privkey of alice, used to refund tx in case of timeout
|
|
||||||
alice_seckey = bitcoind.dumpprivkey(alicepubkey)
|
|
||||||
|
|
||||||
# ========================= HASHLOCK SECRET PREIMAGE =========================
|
|
||||||
secret = input("Alice: Enter secret to lock funds: (type 'enter' for demo)")
|
|
||||||
# preimage = secret.encode('UTF-8')
|
|
||||||
preimage = b'preimage'
|
|
||||||
h = hashlib.sha256(preimage).digest()
|
|
||||||
|
|
||||||
# ========================= LOCKTIME SCRIPT CREATION =========================
|
|
||||||
lockduration = 10
|
|
||||||
blocknum = bitcoind.getblockcount()
|
|
||||||
redeemblocknum = blocknum + lockduration
|
|
||||||
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
|
||||||
# satisfied for the funds to be spent.
|
|
||||||
btc_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
|
||||||
alicepubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
|
||||||
bobpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
|
||||||
print("Redeem script:", b2x(btc_redeemScript))
|
|
||||||
|
|
||||||
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
|
|
||||||
txin_scriptPubKey = btc_redeemScript.to_p2sh_scriptPubKey()
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
|
||||||
p2sh = str(txin_p2sh_address)
|
|
||||||
print('Bob -- Assuming Alice has created other tx on Zcash blockchain, send funds to this p2sh address:', p2sh)
|
|
||||||
|
|
||||||
## TODO: IMPORT ZCASH XCAT FUNCTIONS
|
|
||||||
|
|
||||||
|
|
||||||
# ========================= FUND BITCOIN P2SH =========================
|
|
||||||
response = input("Bob -- Type 'enter' to allow zbxcat to fund the Bitcoin p2sh on your behalf:")
|
|
||||||
send_amount = 1.0*COIN
|
|
||||||
fund_tx = bitcoind.sendtoaddress(txin_p2sh_address, send_amount)
|
|
||||||
print('Alice -- Bitcoin fund tx was sent, please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
|
|
||||||
|
|
||||||
# ========================= PART 2: BITCOIN P2SH FUNDED, REDEEM OR REFUND =========================
|
|
||||||
# Check that fund_tx is on the blockchain to the right address, then notify receiver
|
|
||||||
# Importing address so we can watch it
|
|
||||||
bitcoind.importaddress(p2sh)
|
|
||||||
# Get details of funding transaction
|
|
||||||
fund_txinfo = bitcoind.gettransaction(fund_tx)
|
|
||||||
fund_details = fund_txinfo['details'] # "fund_details" is an array, for now we can assume it only has one destination address
|
|
||||||
outputAddress = fund_details[0]['address']
|
|
||||||
fund_vout = fund_details[0]['vout']
|
|
||||||
if (outputAddress != p2sh):
|
|
||||||
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
|
|
||||||
quit()
|
|
||||||
# Check amount by inspecting imported address
|
|
||||||
output_amount = bitcoind.getreceivedbyaddress(outputAddress, 0)
|
|
||||||
if (output_amount < send_amount):
|
|
||||||
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
|
|
||||||
quit()
|
|
||||||
print("P2SH {0} successfully funded with {1}".format(p2sh, send_amount))
|
|
||||||
|
|
||||||
print('Alice -- the fund tx has been confirmed, now you can redeem your Bitcoin with the secret!')
|
|
||||||
|
|
||||||
|
|
||||||
# ========================= CHECKLOCKIME FOR BITCOIN TX1 =========================
|
|
||||||
# Mock the timeout period passing for tx1 (comment this out to proceed to redeemtx)
|
|
||||||
# bitcoind.generate(20)
|
|
||||||
|
|
||||||
# ========================= BITCOIN REFUND CONDITION =========================
|
|
||||||
# AFTER 24 HRS (by blocknum): If locktime for first tx has passed, tx1 is refunded to alice
|
|
||||||
if(bitcoind.getblockcount() >= redeemblocknum):
|
|
||||||
print("Bob -- Alice did not redeem within the timeout period, so refunding your bitcoin....... ")
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
|
|
||||||
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
|
|
||||||
txin.nSequence = 0
|
|
||||||
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
|
||||||
tx.nLockTime = redeemblocknum
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
|
||||||
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
|
|
||||||
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
txin.scriptSig = CScript([sig, bob_seckey.pub, OP_FALSE, btc_redeemScript])
|
|
||||||
print("Time lock has passed, Bob redeeming his own tx:")
|
|
||||||
print("Refund tx hex:", b2x(tx.serialize()))
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
txid = bitcoind.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
|
|
||||||
quit()
|
|
||||||
|
|
||||||
# ========================= BITCOIN REDEEM CONDITION =========================
|
|
||||||
# BEFORE 24 HRS (by blocknum): Alice redeems bitcoin tx bob funded
|
|
||||||
print("Alice -- Redeeming tx.....")
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
|
|
||||||
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
|
||||||
tx.nLockTime = redeemblocknum
|
|
||||||
sighash = SignatureHash(btc_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
txin.scriptSig = CScript([sig, alice_seckey.pub, preimage, OP_TRUE, btc_redeemScript])
|
|
||||||
print("Redeem tx hex:", b2x(tx.serialize()))
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
txid = bitcoind.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
|
100
btc-p2sh-htlc.py
100
btc-p2sh-htlc.py
|
@ -1,100 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
|
||||||
# Copyright (C) 2017 The Zcash developers
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major < 3:
|
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import bitcoin
|
|
||||||
import bitcoin.rpc
|
|
||||||
from bitcoin import SelectParams
|
|
||||||
from bitcoin.core import b2x, lx, b2lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
|
||||||
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
|
|
||||||
from bitcoin.core.script import OP_DROP, OP_CHECKLOCKTIMEVERIFY, OP_SHA256, OP_TRUE
|
|
||||||
from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
|
||||||
from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
# SelectParams('testnet')
|
|
||||||
# To get transactions not in your wallet, must set -txindex=1
|
|
||||||
SelectParams('regtest')
|
|
||||||
proxy = bitcoin.rpc.Proxy()
|
|
||||||
|
|
||||||
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
|
|
||||||
preimage = b'preimage'
|
|
||||||
h = hashlib.sha256(preimage).digest()
|
|
||||||
|
|
||||||
# proxy.getnewaddress() returns CBitcoinAddress
|
|
||||||
recipientpubkey = proxy.getnewaddress()
|
|
||||||
senderpubkey = proxy.getnewaddress()
|
|
||||||
# privkey of the recipient, used to sign the redeemTx
|
|
||||||
seckey = proxy.dumpprivkey(recipientpubkey)
|
|
||||||
|
|
||||||
lockduration = 10
|
|
||||||
blocknum = proxy.getblockcount()
|
|
||||||
redeemblocknum = blocknum + lockduration
|
|
||||||
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
|
||||||
# satisfied for the funds to be spent.
|
|
||||||
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
|
||||||
recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
|
||||||
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
|
||||||
|
|
||||||
print("redeem script:", b2x(txin_redeemScript))
|
|
||||||
|
|
||||||
# Create P2SH scriptPubKey from redeemScript.
|
|
||||||
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
|
|
||||||
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
|
|
||||||
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
|
||||||
p2sh = str(txin_p2sh_address)
|
|
||||||
print('Pay to:', p2sh)
|
|
||||||
|
|
||||||
# AUTOMATE Send funds to p2sh
|
|
||||||
amount = 1.0*COIN
|
|
||||||
# sendtoaddress return the id of the created tx
|
|
||||||
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
|
|
||||||
|
|
||||||
print('fund tx sent. It\'s id is:', b2x(lx(b2x(fund_tx))))
|
|
||||||
print('Now redeeming.........')
|
|
||||||
|
|
||||||
# AUTOMATE getting vout of funding tx
|
|
||||||
txinfo = proxy.gettransaction(fund_tx)
|
|
||||||
details = txinfo['details'][0]
|
|
||||||
vout = details['vout']
|
|
||||||
|
|
||||||
# Create the txin structure. scriptSig defaults to being empty.
|
|
||||||
# The input is the p2sh funding transaction txid, vout is its index
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, vout))
|
|
||||||
|
|
||||||
# Create the txout. Pays out to recipient, so uses recipient's pubkey
|
|
||||||
# Withdraw full amount minus fee
|
|
||||||
default_fee = 0.001*COIN
|
|
||||||
txout = CMutableTxOut(amount - default_fee, recipientpubkey.to_scriptPubKey())
|
|
||||||
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
|
||||||
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
|
|
||||||
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
|
|
||||||
# Now sign it. We have to append the type of signature we want to the end, in
|
|
||||||
# this case the usual SIGHASH_ALL.
|
|
||||||
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
|
|
||||||
# Set the scriptSig of our transaction input appropriately.
|
|
||||||
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
|
|
||||||
|
|
||||||
print("Redeem tx hex:", b2x(tx.serialize()))
|
|
||||||
|
|
||||||
# Verify the signature worked.
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
|
|
||||||
print("Now sending redeem transaction.......")
|
|
||||||
txid = proxy.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
|
39
cli.py
39
cli.py
|
@ -23,47 +23,46 @@ def parse_addrs(address):
|
||||||
return status
|
return status
|
||||||
|
|
||||||
def checkSellActions(trade):
|
def checkSellActions(trade):
|
||||||
if trade.buyContract.get_status() == 'funded':
|
if trade.buy.get_status() == 'funded':
|
||||||
seller_redeem(trade)
|
seller_redeem(trade)
|
||||||
elif trade.buyContract.get_status() == 'empty':
|
elif trade.buy.get_status() == 'empty':
|
||||||
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(trade.buyContract.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))
|
||||||
|
|
||||||
def checkBuyActions(trade):
|
def checkBuyActions(trade):
|
||||||
if trade.sellContract.get_status() == 'funded' and trade.buyContract.get_status() != 'redeemed':
|
if trade.sell.get_status() == 'funded' and trade.buy.get_status() != 'redeemed':
|
||||||
print("One active trade available, fulfilling buyer contract...")
|
print("One active trade available, fulfilling buyer contract...")
|
||||||
buyer_fulfill(trade)
|
buyer_fulfill(trade)
|
||||||
elif trade.buyContract.get_status() == 'redeemed':
|
elif trade.buy.get_status() == 'redeemed':
|
||||||
buyer_redeem(trade)
|
buyer_redeem(trade)
|
||||||
print("XCAT trade complete!")
|
print("XCAT trade complete!")
|
||||||
|
|
||||||
def instantiateTrade(trade):
|
def instantiateTrade(trade):
|
||||||
return Trade(buyContract=Contract(trade['buy']), sellContract=Contract(trade['sell']))
|
return Trade(buy=Contract(trade['buy']), sell=Contract(trade['sell']))
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
|
parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter,
|
||||||
description=textwrap.dedent('''\
|
description=textwrap.dedent('''\
|
||||||
== Contracts ==
|
== Trades ==
|
||||||
newtrade - create a new trade
|
newtrade - create a new trade
|
||||||
checktrades - check for actions to be taken on existing trades
|
checktrades - check for actions to be taken on existing trades
|
||||||
importcontract "hexstr" - import an existing trade from a hex string
|
importtrade "hexstr" - import an existing trade from a hex string
|
||||||
exportcontract - export the data of an existing xcat trade as a hex string
|
exporttrade - export the data of an existing xcat trade as a hex string
|
||||||
|
|
||||||
'''))
|
'''))
|
||||||
parser.add_argument("command", action="store", help="list commands")
|
parser.add_argument("command", action="store", help="list commands")
|
||||||
parser.add_argument("argument", action="store", nargs="*", help="add an argument")
|
parser.add_argument("argument", action="store", nargs="*", help="add an argument")
|
||||||
# parser.add_argument("-importcontract", type=str, action="store", help="import an existing trade from a hex string.")
|
# parser.add_argument("--daemon", "-d", action="store_true", help="Run as daemon process")
|
||||||
# parser.add_argument("-newtrade", action="store", help="create a new trade.")
|
|
||||||
# parser.add_argument("-checktrades", action="store", help="check status of existing trades")
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# how to hold state of role
|
# how to hold state of role
|
||||||
command = args.command
|
command = args.command
|
||||||
if command == 'importcontract':
|
if command == 'importtrade':
|
||||||
hexstr = args.argument[0]
|
hexstr = args.argument[0]
|
||||||
trade = x2s(hexstr)
|
trade = x2s(hexstr)
|
||||||
trade = instantiateTrade(ast.literal_eval(trade))
|
trade = instantiateTrade(ast.literal_eval(trade))
|
||||||
print(trade)
|
db.create(trade)
|
||||||
elif command == 'exportcontract':
|
# print(trade.toJ)
|
||||||
|
elif command == 'exporttrade':
|
||||||
trade = get_trade()
|
trade = get_trade()
|
||||||
hexstr = s2x(str(trade))
|
hexstr = s2x(str(trade))
|
||||||
print(trade)
|
print(trade)
|
||||||
|
@ -71,7 +70,7 @@ if __name__ == '__main__':
|
||||||
elif command == 'checktrades':
|
elif command == 'checktrades':
|
||||||
trade = get_trade()
|
trade = get_trade()
|
||||||
trade = instantiateTrade(trade)
|
trade = instantiateTrade(trade)
|
||||||
if find_role(trade.sellContract) == 'initiator':
|
if find_role(trade.sell) == 'initiator':
|
||||||
role = 'seller'
|
role = 'seller'
|
||||||
checkSellActions(trade)
|
checkSellActions(trade)
|
||||||
else:
|
else:
|
||||||
|
@ -80,6 +79,10 @@ if __name__ == '__main__':
|
||||||
elif command == 'newtrade':
|
elif command == 'newtrade':
|
||||||
erase_trade()
|
erase_trade()
|
||||||
role = 'seller'
|
role = 'seller'
|
||||||
htlcTrade = Trade()
|
|
||||||
print("Creating new XCAT trade...")
|
print("Creating new XCAT trade...")
|
||||||
seller_initiate(htlcTrade)
|
trade = seller_initiate(Trade())
|
||||||
|
# Save it to leveldb
|
||||||
|
db.create(trade)
|
||||||
|
elif command == "daemon":
|
||||||
|
#TODO: implement
|
||||||
|
print("Run as daemon process")
|
||||||
|
|
30
database.py
30
database.py
|
@ -2,22 +2,42 @@ import plyvel
|
||||||
from utils import *
|
from utils import *
|
||||||
import binascii
|
import binascii
|
||||||
import sys
|
import sys
|
||||||
|
import json
|
||||||
|
|
||||||
db = plyvel.DB('/tmp/testdb', create_if_missing=True)
|
db = plyvel.DB('/tmp/testdb', create_if_missing=True)
|
||||||
|
|
||||||
trade = get_trade()
|
trade = get_trade()
|
||||||
## txid we retrieve by
|
## txid we retrieve by
|
||||||
if trade and 'sell' in trade:
|
if trade and 'sell' in trade:
|
||||||
txid = trade['sell']['fund_tx']
|
if 'fund_tx' in trade['sell']:
|
||||||
|
txid = trade['sell']['fund_tx']
|
||||||
|
|
||||||
def create(hexstr):
|
# Takes object, saves json as bytes
|
||||||
trade = hex2dict(hexstr)
|
def create(trade):
|
||||||
txid = trade['sell']['fund_tx']
|
trade = trade.toJSON()
|
||||||
|
jt = json.loads(trade)
|
||||||
|
txid = jt['sell']['fund_tx']
|
||||||
# Save trade by initiating txid
|
# Save trade by initiating txid
|
||||||
db.put(b(txid), b(hexstr))
|
db.put(b(txid), b(trade))
|
||||||
|
|
||||||
def get(txid):
|
def get(txid):
|
||||||
return db.get(b(txid))
|
return db.get(b(txid))
|
||||||
|
|
||||||
|
# db.delete(b'hello')
|
||||||
|
|
||||||
# hexstr = get(txid)
|
# hexstr = get(txid)
|
||||||
# print(x2s(hexstr))
|
# print(x2s(hexstr))
|
||||||
|
#
|
||||||
|
def print_entries():
|
||||||
|
it = db.iterator()
|
||||||
|
with db.iterator() as it:
|
||||||
|
for k, v in it:
|
||||||
|
j = json.loads(x2s(b2x(v)))
|
||||||
|
print('val: ', j)
|
||||||
|
print('sell: ', j['sell'])
|
||||||
|
|
||||||
|
# print_entries()
|
||||||
|
txid = '1171aeda64eff388b3568fa4675d0ca78852911109bbe42e0ef11ad6bf1b159e'
|
||||||
|
entry = db.get(b(txid))
|
||||||
|
print(entry)
|
||||||
|
# print(it.next())
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
|
||||||
# Copyright (C) 2017 The Zcash developers
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major < 3:
|
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import bitcoin
|
|
||||||
import bitcoin.rpc
|
|
||||||
from bitcoin import SelectParams
|
|
||||||
from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
|
||||||
from bitcoin.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_ENDIF, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL
|
|
||||||
from bitcoin.core.script import 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
|
|
||||||
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
# SelectParams('testnet')
|
|
||||||
# To get transactions not in your wallet, must set -txindex=1
|
|
||||||
SelectParams('regtest')
|
|
||||||
proxy = bitcoin.rpc.Proxy()
|
|
||||||
|
|
||||||
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
|
|
||||||
preimage = b'preimage'
|
|
||||||
h = hashlib.sha256(preimage).digest()
|
|
||||||
|
|
||||||
# proxy.getnewaddress() returns CBitcoinAddress
|
|
||||||
recipientpubkey = proxy.getnewaddress()
|
|
||||||
senderpubkey = proxy.getnewaddress()
|
|
||||||
# privkey of the recipient, used to sign the redeemTx
|
|
||||||
seckey = proxy.dumpprivkey(senderpubkey)
|
|
||||||
#proxy.importprivkey(seckey)
|
|
||||||
lockduration = 10
|
|
||||||
blocknum = proxy.getblockcount()
|
|
||||||
print("current block num:", blocknum)
|
|
||||||
redeemblocknum = blocknum + lockduration
|
|
||||||
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
|
||||||
# satisfied for the funds to be spent.
|
|
||||||
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
|
||||||
recipientpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
|
||||||
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
|
||||||
print("redeem script:", b2x(txin_redeemScript))
|
|
||||||
|
|
||||||
# Create P2SH scriptPubKey from redeemScript.
|
|
||||||
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
|
|
||||||
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
|
|
||||||
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
|
||||||
p2sh = str(txin_p2sh_address)
|
|
||||||
print('Pay to:', p2sh)
|
|
||||||
|
|
||||||
# AUTOMATE Send funds to p2sh
|
|
||||||
amount = 1.0*COIN
|
|
||||||
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
|
|
||||||
|
|
||||||
print('Now redeeming.........')
|
|
||||||
|
|
||||||
print('Jumping forward till after timelock')
|
|
||||||
proxy.generate(lockduration)
|
|
||||||
print('block num is now:', proxy.getblockcount())
|
|
||||||
# AUTOMATE getting vout of funding tx
|
|
||||||
txinfo = proxy.gettransaction(fund_tx)
|
|
||||||
details = txinfo['details'][0]
|
|
||||||
vout = details['vout']
|
|
||||||
|
|
||||||
# Create the txin structure. scriptSig defaults to being empty.
|
|
||||||
# The input is the p2sh funding transaction txid, vout is its index
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, vout))
|
|
||||||
|
|
||||||
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
|
|
||||||
txin.nSequence = 0
|
|
||||||
|
|
||||||
# Create the txout. Pays out to recipient, so uses recipient's pubkey
|
|
||||||
# Withdraw full amount minus fee
|
|
||||||
default_fee = 0.001*COIN
|
|
||||||
txout = CMutableTxOut(amount - default_fee, senderpubkey.to_scriptPubKey())
|
|
||||||
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
|
||||||
tx.nLockTime=redeemblocknum
|
|
||||||
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
|
||||||
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
|
|
||||||
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
|
|
||||||
# Now sign it. We have to append the type of signature we want to the end, in
|
|
||||||
# this case the usual SIGHASH_ALL.
|
|
||||||
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
|
|
||||||
# Set the scriptSig of our transaction input appropriately.
|
|
||||||
txin.scriptSig = CScript([sig, seckey.pub, OP_FALSE, txin_redeemScript])
|
|
||||||
|
|
||||||
print("Redeem tx hex:", b2x(tx.serialize()))
|
|
||||||
|
|
||||||
# Verify the signature worked.
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
|
|
||||||
print("Now sending redeem transaction.......")
|
|
||||||
txid = proxy.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
|
|
@ -1 +1,2 @@
|
||||||
python-bitcoinlib
|
python-bitcoinlib
|
||||||
|
plyvel
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Hm0Mla5n
|
UYH0XxCs
|
36
test.py
36
test.py
|
@ -26,10 +26,10 @@ def initiate(trade):
|
||||||
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
||||||
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
||||||
# initializing contract classes with addresses, currencies, and amounts
|
# initializing contract classes with addresses, currencies, and amounts
|
||||||
trade.sellContract = Contract(sell)
|
trade.sell = Contract(sell)
|
||||||
trade.buyContract = Contract(buy)
|
trade.buy = Contract(buy)
|
||||||
print(trade.sellContract.__dict__)
|
print(trade.sell.__dict__)
|
||||||
print(trade.buyContract.__dict__)
|
print(trade.buy.__dict__)
|
||||||
|
|
||||||
secret = generate_password()
|
secret = generate_password()
|
||||||
print("Generating secret to lock funds:", secret)
|
print("Generating secret to lock funds:", secret)
|
||||||
|
@ -44,8 +44,8 @@ def initiate(trade):
|
||||||
create_buy_p2sh(trade, secret, buy_locktime)
|
create_buy_p2sh(trade, secret, buy_locktime)
|
||||||
|
|
||||||
def fulfill(trade):
|
def fulfill(trade):
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
sell = trade.sellContract
|
sell = trade.sell
|
||||||
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
||||||
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
||||||
|
|
||||||
|
@ -57,32 +57,32 @@ def fulfill(trade):
|
||||||
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
|
raise ValueError("Sell p2sh not funded, buyer cannot redeem")
|
||||||
|
|
||||||
def redeem_one(trade):
|
def redeem_one(trade):
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sell.get_status() == 'redeemed':
|
||||||
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
|
raise RuntimeError("Sell contract status was already redeemed before seller could redeem buyer's tx")
|
||||||
else:
|
else:
|
||||||
secret = get_secret()
|
secret = get_secret()
|
||||||
print("GETTING SECRET IN TEST:", secret)
|
print("GETTING SECRET IN TEST:", secret)
|
||||||
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
|
tx_type, txid = redeem_p2sh(trade.buy, secret)
|
||||||
print("\nTX Type", tx_type)
|
print("\nTX Type", tx_type)
|
||||||
setattr(trade.buyContract, tx_type, txid)
|
setattr(trade.buy, tx_type, txid)
|
||||||
save(trade)
|
save(trade)
|
||||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||||
|
|
||||||
def redeem_two(trade):
|
def redeem_two(trade):
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sell.get_status() == 'redeemed':
|
||||||
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
|
raise RuntimeError("Sell contract was redeemed before buyer could retrieve funds")
|
||||||
elif trade.buyContract.get_status() == 'refunded':
|
elif trade.buy.get_status() == 'refunded':
|
||||||
print("buyContract was refunded to buyer")
|
print("buy was refunded to buyer")
|
||||||
else:
|
else:
|
||||||
# Buy contract is where seller disclosed secret in redeeming
|
# Buy contract is where seller disclosed secret in redeeming
|
||||||
if trade.buyContract.currency == 'bitcoin':
|
if trade.buy.currency == 'bitcoin':
|
||||||
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = bXcat.parse_secret(trade.buy.redeem_tx)
|
||||||
else:
|
else:
|
||||||
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = zXcat.parse_secret(trade.buy.redeem_tx)
|
||||||
print("Found secret in seller's redeem tx", secret)
|
print("Found secret in seller's redeem tx", secret)
|
||||||
redeem_tx = redeem_p2sh(trade.sellContract, secret)
|
redeem_tx = redeem_p2sh(trade.sell, secret)
|
||||||
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
setattr(trade.sell, 'redeem_tx', redeem_tx)
|
||||||
save(trade)
|
save(trade)
|
||||||
|
|
||||||
def generate_blocks(num):
|
def generate_blocks(num):
|
||||||
|
|
12
trades.py
12
trades.py
|
@ -1,8 +1,14 @@
|
||||||
|
import json
|
||||||
|
|
||||||
class Trade(object):
|
class Trade(object):
|
||||||
def __init__(self, sellContract=None, buyContract=None):
|
def __init__(self, sell=None, buy=None):
|
||||||
'''Create a new trade with a sell contract and buy contract across two chains'''
|
'''Create a new trade with a sell contract and buy contract across two chains'''
|
||||||
self.sellContract = sellContract
|
self.sell = sell
|
||||||
self.buyContract = buyContract
|
self.buy = buy
|
||||||
|
|
||||||
|
def toJSON(self):
|
||||||
|
return json.dumps(self, default=lambda o: o.__dict__,
|
||||||
|
sort_keys=True, indent=4)
|
||||||
|
|
||||||
class Contract(object):
|
class Contract(object):
|
||||||
def __init__(self, data):
|
def __init__(self, data):
|
||||||
|
|
|
@ -40,7 +40,7 @@ def retrieve_password():
|
||||||
return secret
|
return secret
|
||||||
|
|
||||||
def authorize_fund_sell(htlcTrade):
|
def authorize_fund_sell(htlcTrade):
|
||||||
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sellContract.amount, htlcTrade.sellContract.currency, htlcTrade.sellContract.p2sh))
|
print('To complete your sell, send {0} {1} to this p2sh: {2}'.format(htlcTrade.sell.amount, htlcTrade.sell.currency, htlcTrade.sell.p2sh))
|
||||||
response = input("Type 'enter' to allow this program to send funds on your behalf.")
|
response = input("Type 'enter' to allow this program to send funds on your behalf.")
|
||||||
|
|
||||||
def get_initiator_addresses():
|
def get_initiator_addresses():
|
||||||
|
@ -75,4 +75,4 @@ 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.buyContract.currency, trade.sellContract.currency, trade.sellContract.amount, trade.sellContract.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))
|
||||||
|
|
5
utils.py
5
utils.py
|
@ -25,6 +25,7 @@ def s2x(string):
|
||||||
|
|
||||||
def hex2dict(hexstr):
|
def hex2dict(hexstr):
|
||||||
jsonstr = x2s(hexstr)
|
jsonstr = x2s(hexstr)
|
||||||
|
print(hexstr['fund_tx'])
|
||||||
print(jsonstr)
|
print(jsonstr)
|
||||||
return json.loads(jsonstr)
|
return json.loads(jsonstr)
|
||||||
|
|
||||||
|
@ -76,7 +77,7 @@ def save_secret(secret):
|
||||||
def save(trade):
|
def save(trade):
|
||||||
print("Saving trade")
|
print("Saving trade")
|
||||||
trade = {
|
trade = {
|
||||||
'sell': trade.sellContract.__dict__,
|
'sell': trade.sell.__dict__,
|
||||||
'buy': trade.buyContract.__dict__
|
'buy': trade.buy.__dict__
|
||||||
}
|
}
|
||||||
save_trade(trade)
|
save_trade(trade)
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
{"sell": {"currency": "bitcoin", "fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "amount": "0.5", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9147788b4511a25fba1092e67b307a6dcdb6da125d96702bd01b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac", "fund_tx": "6cca31678d7fe6461277cea7e0614844e62ccc3ae4e247ca77d62bdd741fabef", "p2sh": "2N3rA4r6VSx65FeBTcgWWcR9HaW69DYDbrp", "redeemblocknum": 445}, "buy": {"currency": "zcash", "fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "amount": "1.12", "refund_tx": "5e34f0c634fb6207e9c85ed52528629404d25db29b68931b4e0278377ee776d6", "redeemScript": "63a82033fb8f68c7e079a2a35cfcd8827279f8a55fa1be04c99debd8ed3e54954e08228876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b16702e301b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac", "fund_tx": "8c9f9581a8cab00836b54237b30a6d68125067e3ffe6ae04413da2bdc9146daa", "p2sh": "t2UkYvcnigZt7FFp86UvTZ78qdGeLXw7tet", "redeemblocknum": 483}}
|
{"buy": {"fulfiller": "tmTjZSg4pX2Us6V5HttiwFZwj464fD2ZgpY", "initiator": "tmFRXyju7ANM7A9mg75ZjyhFW1UJEhUPwfQ", "p2sh": "t2TcYheueGfEnT2SKYMLWNXKfyzrMLKc6y2", "redeemblocknum": 113817, "currency": "zcash", "amount": 1.2, "redeemScript": "63a820b67f875a86ea6be94a1f6e44857daa739df84421ba2402dd968185b7d58371b68876a9143ea29256c9d2888ca23de42a8b8e69ca2ec235b1670399bc01b17576a914c5acca6ef39c843c7a9c3ad01b2da95fe2edf5ba6888ac"}, "sell": {"fulfiller": "mrQzUGU1dwsWRx5gsKKSDPNtrsP65vCA3Z", "initiator": "myfFr5twPYNwgeXyjCmGcrzXtCmfmWXKYp", "fund_tx": "1171aeda64eff388b3568fa4675d0ca78852911109bbe42e0ef11ad6bf1b159e", "p2sh": "2NEy1foaQU3wkMu7yygW1GZYhikeSxq6ZCq", "redeemblocknum": 911, "currency": "bitcoin", "amount": 3.5, "redeemScript": "63a820b67f875a86ea6be94a1f6e44857daa739df84421ba2402dd968185b7d58371b68876a9147788b4511a25fba1092e67b307a6dcdb6da125d967028f03b17576a914c7043e62a7391596116f54f6a64c8548e97d3fd96888ac"}}
|
91
xcat.py
91
xcat.py
|
@ -32,41 +32,41 @@ def fund_htlc(currency, p2sh, amount):
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
def fund_buy_contract(trade):
|
def fund_buy_contract(trade):
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
txid = fund_htlc(buy.currency, buy.p2sh, buy.amount)
|
||||||
setattr(trade.buyContract, 'fund_tx', txid)
|
setattr(trade.buy, 'fund_tx', txid)
|
||||||
save(trade)
|
save(trade)
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
def fund_sell_contract(trade):
|
def fund_sell_contract(trade):
|
||||||
sell = trade.sellContract
|
sell = trade.sell
|
||||||
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
txid = fund_htlc(sell.currency, sell.p2sh, sell.amount)
|
||||||
setattr(trade.sellContract, 'fund_tx', txid)
|
setattr(trade.sell, 'fund_tx', txid)
|
||||||
save(trade)
|
save(trade)
|
||||||
return txid
|
return txid
|
||||||
|
|
||||||
def create_sell_p2sh(trade, secret, locktime):
|
def create_sell_p2sh(trade, secret, locktime):
|
||||||
# CREATE SELL CONTRACT
|
# CREATE SELL CONTRACT
|
||||||
sell = trade.sellContract
|
sell = trade.sell
|
||||||
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
|
contract = create_htlc(sell.currency, sell.initiator, sell.fulfiller, secret, locktime)
|
||||||
print("sell contract", contract)
|
print("sell contract", contract)
|
||||||
setattr(trade.sellContract, 'p2sh', contract['p2sh'])
|
setattr(trade.sell, 'p2sh', contract['p2sh'])
|
||||||
setattr(trade.sellContract, 'redeemScript', contract['redeemScript'])
|
setattr(trade.sell, 'redeemScript', contract['redeemScript'])
|
||||||
setattr(trade.sellContract, 'redeemblocknum', contract['redeemblocknum'])
|
setattr(trade.sell, 'redeemblocknum', contract['redeemblocknum'])
|
||||||
save(trade)
|
save(trade)
|
||||||
|
|
||||||
def create_buy_p2sh(trade, secret, locktime):
|
def create_buy_p2sh(trade, secret, locktime):
|
||||||
## CREATE BUY CONTRACT
|
## CREATE BUY CONTRACT
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
|
print("Now creating buy contract on the {0} blockchain where you will wait for the buyer to send funds...".format(buy.currency))
|
||||||
print("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
print("HTLC DETAILS", buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
||||||
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
buy_contract = create_htlc(buy.currency, buy.fulfiller, buy.initiator, secret, locktime)
|
||||||
print("Buy contract", buy_contract)
|
print("Buy contract", buy_contract)
|
||||||
|
|
||||||
setattr(trade.buyContract, 'p2sh', buy_contract['p2sh'])
|
setattr(trade.buy, 'p2sh', buy_contract['p2sh'])
|
||||||
setattr(trade.buyContract, 'redeemScript', buy_contract['redeemScript'])
|
setattr(trade.buy, 'redeemScript', buy_contract['redeemScript'])
|
||||||
setattr(trade.buyContract, 'redeemblocknum', buy_contract['redeemblocknum'])
|
setattr(trade.buy, 'redeemblocknum', buy_contract['redeemblocknum'])
|
||||||
print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buyContract.p2sh)
|
print("Now contact the buyer and tell them to send funds to this p2sh: ", trade.buy.p2sh)
|
||||||
|
|
||||||
save(trade)
|
save(trade)
|
||||||
|
|
||||||
|
@ -86,43 +86,43 @@ def print_trade(role):
|
||||||
#### Main functions determining user flow from command line
|
#### Main functions determining user flow from command line
|
||||||
def buyer_redeem(trade):
|
def buyer_redeem(trade):
|
||||||
userInput.authorize_buyer_redeem(trade)
|
userInput.authorize_buyer_redeem(trade)
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sell.get_status() == 'redeemed':
|
||||||
print("You already redeemed the funds and acquired {0} {1}".format(trade.sellContract.amount, trade.sellContract.currency))
|
print("You already redeemed the funds and acquired {0} {1}".format(trade.sell.amount, trade.sell.currency))
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
# Buyer redeems seller's funded tx
|
# Buyer redeems seller's funded tx
|
||||||
p2sh = trade.sellContract.p2sh
|
p2sh = trade.sell.p2sh
|
||||||
currency = trade.sellContract.currency
|
currency = trade.sell.currency
|
||||||
# Buy contract is where seller disclosed secret in redeeming
|
# Buy contract is where seller disclosed secret in redeeming
|
||||||
if trade.buyContract.currency == 'bitcoin':
|
if trade.buy.currency == 'bitcoin':
|
||||||
secret = bXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = bXcat.parse_secret(trade.buy.redeem_tx)
|
||||||
else:
|
else:
|
||||||
secret = zXcat.parse_secret(trade.buyContract.redeem_tx)
|
secret = zXcat.parse_secret(trade.buy.redeem_tx)
|
||||||
print("Found secret in seller's redeem tx", secret)
|
print("Found secret in seller's redeem tx", secret)
|
||||||
redeem_tx = redeem_p2sh(trade.sellContract, secret)
|
redeem_tx = redeem_p2sh(trade.sell, secret)
|
||||||
setattr(trade.sellContract, 'redeem_tx', redeem_tx)
|
setattr(trade.sell, 'redeem_tx', redeem_tx)
|
||||||
save(trade)
|
save(trade)
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
def seller_redeem(trade):
|
def seller_redeem(trade):
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
userInput.authorize_seller_redeem(buy)
|
userInput.authorize_seller_redeem(buy)
|
||||||
|
|
||||||
if trade.sellContract.get_status() == 'redeemed':
|
if trade.sell.get_status() == 'redeemed':
|
||||||
print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
|
print("You already redeemed the funds and acquired {0} {1}".format(buy.amount, buy.currency))
|
||||||
exit()
|
exit()
|
||||||
else:
|
else:
|
||||||
# Seller redeems buyer's funded tx (contract in p2sh)
|
# Seller redeems buyer's funded tx (contract in p2sh)
|
||||||
secret = userInput.retrieve_password()
|
secret = userInput.retrieve_password()
|
||||||
tx_type, txid = redeem_p2sh(trade.buyContract, secret)
|
tx_type, txid = redeem_p2sh(trade.buy, secret)
|
||||||
setattr(trade.buyContract, tx_type, txid)
|
setattr(trade.buy, tx_type, txid)
|
||||||
save(trade)
|
save(trade)
|
||||||
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
print("You have redeemed {0} {1}!".format(buy.amount, buy.currency))
|
||||||
print_trade('seller')
|
print_trade('seller')
|
||||||
|
|
||||||
def buyer_fulfill(trade):
|
def buyer_fulfill(trade):
|
||||||
buy = trade.buyContract
|
buy = trade.buy
|
||||||
sell = trade.sellContract
|
sell = trade.sell
|
||||||
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
buy_p2sh_balance = check_p2sh(buy.currency, buy.p2sh)
|
||||||
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
sell_p2sh_balance = check_p2sh(sell.currency, sell.p2sh)
|
||||||
|
|
||||||
|
@ -151,16 +151,16 @@ def seller_initiate(trade):
|
||||||
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
sell['fulfiller'] = fulfill_addrs[sell_currency]
|
||||||
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
buy['fulfiller'] = fulfill_addrs[buy_currency]
|
||||||
# initializing contract classes with addresses, currencies, and amounts
|
# initializing contract classes with addresses, currencies, and amounts
|
||||||
trade.sellContract = Contract(sell)
|
trade.sell = Contract(sell)
|
||||||
trade.buyContract = Contract(buy)
|
trade.buy = Contract(buy)
|
||||||
print(trade.sellContract.__dict__)
|
print(trade.sell.__dict__)
|
||||||
print(trade.buyContract.__dict__)
|
print(trade.buy.__dict__)
|
||||||
|
|
||||||
secret = userInput.create_password()
|
secret = userInput.create_password()
|
||||||
# TODO: Implement locktimes and mock block passage of time
|
# TODO: Implement locktimes and mock block passage of time
|
||||||
sell_locktime = 5
|
sell_locktime = 5
|
||||||
buy_locktime = 10 # Must be more than first tx
|
buy_locktime = 10 # Must be more than first tx
|
||||||
|
print("Creating pay-to-script-hash for sell contract...")
|
||||||
create_sell_p2sh(trade, secret, sell_locktime)
|
create_sell_p2sh(trade, secret, sell_locktime)
|
||||||
|
|
||||||
userInput.authorize_fund_sell(trade)
|
userInput.authorize_fund_sell(trade)
|
||||||
|
@ -169,7 +169,8 @@ def seller_initiate(trade):
|
||||||
print("Sent")
|
print("Sent")
|
||||||
|
|
||||||
create_buy_p2sh(trade, secret, buy_locktime)
|
create_buy_p2sh(trade, secret, buy_locktime)
|
||||||
print_trade('seller')
|
|
||||||
|
return trade
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
|
print("ZEC <-> BTC XCAT (Cross-Chain Atomic Transactions)")
|
||||||
|
@ -181,9 +182,9 @@ if __name__ == '__main__':
|
||||||
htlcTrade = Trade()
|
htlcTrade = Trade()
|
||||||
print("New empty trade")
|
print("New empty trade")
|
||||||
else:
|
else:
|
||||||
buyContract = Contract(trade['buy'])
|
buy = Contract(trade['buy'])
|
||||||
sellContract = Contract(trade['sell'])
|
sell = Contract(trade['sell'])
|
||||||
htlcTrade = Trade(buyContract=buyContract, sellContract=sellContract)
|
htlcTrade = Trade(buy=buy, sell=sell)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if sys.argv[1] == 'new':
|
if sys.argv[1] == 'new':
|
||||||
|
@ -206,29 +207,29 @@ if __name__ == '__main__':
|
||||||
print("Trade exists, run script as buyer or seller to complete trade.")
|
print("Trade exists, run script as buyer or seller to complete trade.")
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
if htlcTrade.buyContract is not None and htlcTrade.sellContract is not None:
|
if htlcTrade.buy is not None and htlcTrade.sell is not None:
|
||||||
if htlcTrade.sellContract.get_status() == 'redeemed' and htlcTrade.buyContract.get_status() == 'redeemed':
|
if htlcTrade.sell.get_status() == 'redeemed' and htlcTrade.buy.get_status() == 'redeemed':
|
||||||
print("This trade is already complete! Trade details:")
|
print("This trade is already complete! Trade details:")
|
||||||
pprint(trade)
|
pprint(trade)
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
if role == "seller":
|
if role == "seller":
|
||||||
if htlcTrade.sellContract == None:
|
if htlcTrade.sell == None:
|
||||||
seller_initiate(htlcTrade)
|
seller_initiate(htlcTrade)
|
||||||
elif htlcTrade.buyContract.get_status() == 'funded':
|
elif htlcTrade.buy.get_status() == 'funded':
|
||||||
seller_redeem(htlcTrade)
|
seller_redeem(htlcTrade)
|
||||||
elif htlcTrade.buyContract.get_status() == 'empty':
|
elif htlcTrade.buy.get_status() == 'empty':
|
||||||
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buyContract.currency))
|
print("Buyer has not yet funded the contract where you offered to buy {0}, please wait for them to complete their part.".format(htlcTrade.buy.currency))
|
||||||
else:
|
else:
|
||||||
# Need better way of preventing buyer from having secret
|
# Need better way of preventing buyer from having secret
|
||||||
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
|
# if 'status' not in trade['buy'] and trade['sell']['status'] == 'funded':
|
||||||
if htlcTrade.sellContract.get_status() == 'funded' and htlcTrade.buyContract.get_status() != 'redeemed':
|
if htlcTrade.sell.get_status() == 'funded' and htlcTrade.buy.get_status() != 'redeemed':
|
||||||
print("One active trade available, fulfilling buyer contract...")
|
print("One active trade available, fulfilling buyer contract...")
|
||||||
buyer_fulfill(htlcTrade)
|
buyer_fulfill(htlcTrade)
|
||||||
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
|
# How to monitor if txs are included in blocks -- should use blocknotify and a monitor daemon?
|
||||||
# p2sh = trade['buy']['p2sh']
|
# p2sh = trade['buy']['p2sh']
|
||||||
# check_blocks(p2sh)
|
# check_blocks(p2sh)
|
||||||
elif htlcTrade.buyContract.get_status() == 'redeemed':
|
elif htlcTrade.buy.get_status() == 'redeemed':
|
||||||
# Seller has redeemed buyer's tx, buyer can now redeem.
|
# Seller has redeemed buyer's tx, buyer can now redeem.
|
||||||
buyer_redeem(htlcTrade)
|
buyer_redeem(htlcTrade)
|
||||||
print("XCAT trade complete!")
|
print("XCAT trade complete!")
|
||||||
|
|
140
zcashcli.py
140
zcashcli.py
|
@ -1,140 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Based on spend-p2sh-txout.py from python-bitcoinlib.
|
|
||||||
# Copyright (C) 2017 The Zcash developers
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major < 3:
|
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import zcash
|
|
||||||
import zcash.rpc
|
|
||||||
from zcash import SelectParams
|
|
||||||
from zcash.core import b2x, lx, 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
|
|
||||||
import hashlib
|
|
||||||
import binascii
|
|
||||||
|
|
||||||
# SelectParams('testnet')
|
|
||||||
SelectParams('regtest')
|
|
||||||
zcashd = zcash.rpc.Proxy()
|
|
||||||
FEE = 0.001*COIN
|
|
||||||
|
|
||||||
# ========================= ZCASH ADDRESSES =========================
|
|
||||||
alice_address = input("Enter alice zcash address: (type 'enter' for demo)")
|
|
||||||
bob_address = input("Enter bob zcash address: (type 'enter' for demo)")
|
|
||||||
# These mock addresses come from regtest on the server
|
|
||||||
# alicepubkey = CBitcoinAddress('tmFUm31B9wzHWJ9jGe9L9Qb549zfC7zFsEK')
|
|
||||||
# bobpubkey = CBitcoinAddress('tmFm8R6b22485uDYm6dryC4f8R6oXUTUe5i')
|
|
||||||
# zcashd.getnewaddress() returns CBitcoinAddress
|
|
||||||
bobpubkey = zcashd.getnewaddress()
|
|
||||||
alicepubkey = zcashd.getnewaddress()
|
|
||||||
print("alicepubkey", alicepubkey)
|
|
||||||
print("bobpubkey", bobpubkey)
|
|
||||||
# privkey of the bob, used to sign the redeemTx
|
|
||||||
bob_seckey = zcashd.dumpprivkey(bobpubkey)
|
|
||||||
# privkey of alice, used to refund tx in case of timeout
|
|
||||||
alice_seckey = zcashd.dumpprivkey(alicepubkey)
|
|
||||||
print("bob_seckey", bob_seckey)
|
|
||||||
|
|
||||||
def get_keys(funder_address, redeemer_address):
|
|
||||||
# fundpubkey = CBitcoinAddress(funder_address)
|
|
||||||
# redeempubkey = CBitcoinAddress(redeemer_address)
|
|
||||||
fundpubkey = zcashd.getnewaddress()
|
|
||||||
redeempubkey = zcashd.getnewaddress()
|
|
||||||
return fundpubkey, redeempubkey
|
|
||||||
|
|
||||||
# ======= secret from Alice, other file ====
|
|
||||||
preimage = b'preimage'
|
|
||||||
h = hashlib.sha256(preimage).digest()
|
|
||||||
|
|
||||||
# ========================= LOCKTIME SCRIPT CREATION =========================
|
|
||||||
lockduration = 20 # Must be more than first tx
|
|
||||||
blocknum = zcashd.getblockcount()
|
|
||||||
redeemblocknum = blocknum + lockduration
|
|
||||||
zec_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
|
||||||
bobpubkey, OP_ELSE, redeemblocknum, OP_CHECKLOCKTIMEVERIFY, OP_DROP, OP_DUP, OP_HASH160,
|
|
||||||
alicepubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
|
||||||
print("TX2 Redeem script on Zcash blockchain:", b2x(zec_redeemScript))
|
|
||||||
|
|
||||||
# ========================= TX1: CREATE BITCOIN P2SH FROM SCRIPT =========================
|
|
||||||
txin_scriptPubKey = zec_redeemScript.to_p2sh_scriptPubKey()
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
|
||||||
p2sh = str(txin_p2sh_address)
|
|
||||||
print('Alice -- send funds to this p2sh address to initiate atomic swap:', p2sh)
|
|
||||||
|
|
||||||
response = input("Alice -- Type 'enter' to allow zbxcat to fund the Zcash p2sh on your behalf:")
|
|
||||||
send_amount = 10.0*COIN
|
|
||||||
fund_tx = zcashd.sendtoaddress(txin_p2sh_address, send_amount)
|
|
||||||
print('Bob -- Alice send fund tx to the Zcash p2sh. Please wait for confirmation. Txid:', b2x(lx(b2x(fund_tx))))
|
|
||||||
|
|
||||||
# ========================= CONFIRM ZCASH FUNDING TX TO P2SH =========================
|
|
||||||
zcashd.importaddress(p2sh)
|
|
||||||
|
|
||||||
fund_txinfo = zcashd.gettransaction(fund_tx)
|
|
||||||
fund_details = fund_txinfo['details'] # "fund_details" is an array, for now we can assume it only has one destination address
|
|
||||||
outputAddress = fund_details[0]['address']
|
|
||||||
fund_vout = fund_details[0]['vout']
|
|
||||||
if (outputAddress != p2sh):
|
|
||||||
print('Fund tx sent to wrong address! p2sh was {0}, funding tx was sent to {1}'.format(p2sh, outputAddress))
|
|
||||||
quit()
|
|
||||||
# Get amount in address
|
|
||||||
output_amount = zcashd.getreceivedbyaddress(outputAddress, 0)
|
|
||||||
if (output_amount < send_amount):
|
|
||||||
print('Fund tx too small! Amount sent was {0}, amount expected was {1}'.format(output_amount, send_amount))
|
|
||||||
quit()
|
|
||||||
|
|
||||||
print('Bob -- Alice Zcash funding tx confirmed, now send funds to the Bitcoin p2sh: (other file)')
|
|
||||||
|
|
||||||
# ========================= PART 2: ZCASH P2SH FUNDED, REDEEM OR REFUND =========================
|
|
||||||
|
|
||||||
# ================= AFTER 48 HRS: ALICE REFUNDS AFTER BOB TIMES OUT =========================
|
|
||||||
# Mock passage of time -- comment out to test normal redeem condition
|
|
||||||
# zcashd.generate(25)
|
|
||||||
|
|
||||||
if(zcashd.getblockcount() >= redeemblocknum):
|
|
||||||
print("Alice -- Bob did not redeem the Zcash you put in escrow within the timeout period, so refunding you..... ")
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
|
|
||||||
# The default nSequence of FFFFFFFF won't let you redeem when there's a CHECKTIMELOCKVERIFY
|
|
||||||
txin.nSequence = 0
|
|
||||||
txout = CMutableTxOut(send_amount - FEE, alicepubkey.to_scriptPubKey())
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
|
||||||
tx.nLockTime = redeemblocknum
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
|
||||||
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
|
|
||||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
sig = alice_seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
txin.scriptSig = CScript([sig, alice_seckey.pub, OP_FALSE, zec_redeemScript])
|
|
||||||
print("Time lock has passed, Alice redeeming her own tx:")
|
|
||||||
print("Refund tx hex:", b2x(tx.serialize()))
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
txid = zcashd.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted refund tx: ", b2x(lx(b2x(txid))))
|
|
||||||
quit()
|
|
||||||
|
|
||||||
# ================= BEFORE 48 HRS: BOB REDEEMS WITH ALICE'S REVEALED SECRET =========================
|
|
||||||
print("Bob -- Redeeming tx.....")
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, fund_vout))
|
|
||||||
txout = CMutableTxOut(send_amount - FEE, bobpubkey.to_scriptPubKey())
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
# nLockTime needs to be at least as large as parameter of CHECKLOCKTIMEVERIFY for script to verify
|
|
||||||
tx.nLockTime = redeemblocknum
|
|
||||||
sighash = SignatureHash(zec_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
sig = bob_seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
txin.scriptSig = CScript([sig, bob_seckey.pub, preimage, OP_TRUE, zec_redeemScript])
|
|
||||||
print("Redeem tx hex:", b2x(tx.serialize()))
|
|
||||||
|
|
||||||
print("txin.scriptSig", b2x(txin.scriptSig))
|
|
||||||
print("txin_scriptPubKey", b2x(txin_scriptPubKey))
|
|
||||||
print('tx', tx)
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
|
|
||||||
txid = zcashd.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted redeem tx: ", b2x(lx(b2x(txid))))
|
|
|
@ -1,90 +0,0 @@
|
||||||
#!/usr/bin/env python3
|
|
||||||
|
|
||||||
# Copyright (C) 2017 the python-zcashlib developers
|
|
||||||
|
|
||||||
import sys
|
|
||||||
if sys.version_info.major < 3:
|
|
||||||
sys.stderr.write('Sorry, Python 3.x required by this example.\n')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import zcash
|
|
||||||
import zcash.rpc
|
|
||||||
from zcash import SelectParams
|
|
||||||
from zcash.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160
|
|
||||||
from zcash.core.script import CScript, OP_DUP, OP_IF, OP_ELSE, OP_DROP, OP_ENDIF, OP_HASH160, OP_SHA256, OP_EQUAL, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL, SIGHASH_ANYONECANPAY, OP_TRUE
|
|
||||||
from zcash.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH
|
|
||||||
from zcash.wallet import CBitcoinAddress, CBitcoinSecret
|
|
||||||
import hashlib
|
|
||||||
|
|
||||||
SelectParams('regtest')
|
|
||||||
proxy = zcash.rpc.Proxy()
|
|
||||||
|
|
||||||
# The parameters needed for the htlc - hash preimage, sender/seller address, recipient/buyer address, num of blocks for timeout
|
|
||||||
preimage = b'preimage'
|
|
||||||
h = hashlib.sha256(preimage).digest()
|
|
||||||
|
|
||||||
# proxy.getnewaddress() returns CBitcoinAddress
|
|
||||||
recipientpubkey = proxy.getnewaddress()
|
|
||||||
senderpubkey = proxy.getnewaddress()
|
|
||||||
# privkey of the recipient, used to sign the redeemTx
|
|
||||||
seckey = proxy.dumpprivkey(recipientpubkey)
|
|
||||||
|
|
||||||
blocknum = 7
|
|
||||||
# Create a htlc redeemScript. Similar to a scriptPubKey the redeemScript must be
|
|
||||||
# satisfied for the funds to be spent.
|
|
||||||
txin_redeemScript = CScript([OP_IF, OP_SHA256, h, OP_EQUALVERIFY,OP_DUP, OP_HASH160,
|
|
||||||
recipientpubkey, OP_ELSE, blocknum, OP_DROP, OP_HASH160,
|
|
||||||
senderpubkey, OP_ENDIF,OP_EQUALVERIFY, OP_CHECKSIG])
|
|
||||||
print("redeem script:", b2x(txin_redeemScript))
|
|
||||||
|
|
||||||
# Create P2SH scriptPubKey from redeemScript.
|
|
||||||
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()
|
|
||||||
print("p2sh_scriptPubKey", b2x(txin_scriptPubKey))
|
|
||||||
|
|
||||||
# Convert the P2SH scriptPubKey to a base58 Bitcoin address
|
|
||||||
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
|
|
||||||
p2sh = str(txin_p2sh_address)
|
|
||||||
print('Pay to:', p2sh)
|
|
||||||
|
|
||||||
# AUTOMATE Send funds to p2sh
|
|
||||||
amount = 1.0*COIN
|
|
||||||
fund_tx = proxy.sendtoaddress(txin_p2sh_address, amount)
|
|
||||||
|
|
||||||
print('Now redeeming.........')
|
|
||||||
|
|
||||||
# AUTOMATE getting vout of funding tx
|
|
||||||
txinfo = proxy.gettransaction(fund_tx)
|
|
||||||
details = txinfo['details'][0]
|
|
||||||
vout = details['vout']
|
|
||||||
|
|
||||||
# Create the txin structure. scriptSig defaults to being empty.
|
|
||||||
# The input is the p2sh funding transaction txid, vout is its index
|
|
||||||
txin = CMutableTxIn(COutPoint(fund_tx, vout))
|
|
||||||
|
|
||||||
# Create the txout. Pays out to recipient, so uses recipient's pubkey
|
|
||||||
# Withdraw full amount minus fee
|
|
||||||
default_fee = 0.001*COIN
|
|
||||||
txout = CMutableTxOut(amount - default_fee, recipientpubkey.to_scriptPubKey())
|
|
||||||
|
|
||||||
# Create the unsigned raw transaction.
|
|
||||||
tx = CMutableTransaction([txin], [txout])
|
|
||||||
|
|
||||||
# Calculate the signature hash for that transaction. Note how the script we use
|
|
||||||
# is the redeemScript, not the scriptPubKey. EvalScript() will be evaluating the redeemScript
|
|
||||||
sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)
|
|
||||||
|
|
||||||
# Now sign it. We have to append the type of signature we want to the end, in
|
|
||||||
# this case the usual SIGHASH_ALL.
|
|
||||||
sig = seckey.sign(sighash) + bytes([SIGHASH_ALL])
|
|
||||||
|
|
||||||
# Set the scriptSig of our transaction input appropriately.
|
|
||||||
txin.scriptSig = CScript([ sig, seckey.pub, preimage, OP_TRUE, txin_redeemScript])
|
|
||||||
|
|
||||||
print("Redeem tx hex:", b2x(tx.serialize()))
|
|
||||||
|
|
||||||
# Verify the signature worked.
|
|
||||||
VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0, (SCRIPT_VERIFY_P2SH,))
|
|
||||||
|
|
||||||
print("Now sending redeem transaction.......")
|
|
||||||
txid = proxy.sendrawtransaction(tx)
|
|
||||||
print("Txid of submitted redeem tx: ", b2x(txid))
|
|
Loading…
Reference in New Issue