Example #1
0
    def test_MoneyRangeCustomParams(self):
        class CoreHighMaxClassDispatcher(CoreBitcoinClassDispatcher):
            ...

        class CoreHighMaxParams(CoreBitcoinParams, CoreBitcoinClass):
            @classgetter
            def MAX_MONEY(self):
                return 22000000 * self.COIN

        class WalletHighMaxClassDispatcher(WalletBitcoinClassDispatcher):
            ...

        class HighMaxParams(BitcoinMainnetParams):
            NAME = 'high_maxmoney'
            WALLET_DISPATCHER = WalletHighMaxClassDispatcher

        with ChainParams(HighMaxParams):
            self.assertFalse(MoneyRange(-1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(-1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(-1)
            self.assertTrue(MoneyRange(0))
            self.assertTrue(MoneyRange(100000))
            max_satoshi = coins_to_satoshi(22000000)
            self.assertTrue(
                MoneyRange(max_satoshi))  # Maximum money on Bitcoin network
            self.assertFalse(MoneyRange(max_satoshi + 1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(max_satoshi + 1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(max_satoshi + 1)
Example #2
0
    def test_p2wsh_signaturehash2(self):
        unsigned_tx = x(
            '0100000002e9b542c5176808107ff1df906f46bb1f2583b16112b95ee5380665ba7fcfc0010000000000ffffffff80e68831516392fcd100d186b3c2c7b95c80b53c77e77c35ba03a66b429a2a1b0000000000ffffffff0280969800000000001976a914de4b231626ef508c9a74a8517e6783c0546d6b2888ac80969800000000001976a9146648a8cd4531e1ec47f35916de8e259237294d1e88ac00000000'
        )
        value1 = coins_to_satoshi(0.16777215)
        scriptcode1 = CScript(
            x('0063ab68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac'
              ))
        value2 = coins_to_satoshi(0.16777215)
        scriptcode2 = CScript(
            x('68210392972e2eb617b2388771abe27235fd5ac44af8e61693261550447a4c3e39da98ac'
              ))

        self.assertEqual(
            SignatureHash(scriptcode1, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, value1,
                          SIGVERSION_WITNESS_V0),
            x('e9071e75e25b8a1e298a72f0d2e9f4f95a0f5cdf86a533cda597eb402ed13b3a'
              ))
        self.assertEqual(
            SignatureHash(scriptcode2, CTransaction.deserialize(unsigned_tx),
                          1, SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, value2,
                          SIGVERSION_WITNESS_V0),
            x('cd72f1f1a433ee9df816857fad88d8ebd97e09a75cd481583eb841c330275e54'
              ))
Example #3
0
def create_btc_spend_tx(dst_addr,
                        txid,
                        vout_n,
                        btc_contract,
                        spend_key=None,
                        branch_condition=True):

    # In real application, the fees should not be static, of course
    out_amount = (coins_to_satoshi(pre_agreed_amount) -
                  coins_to_satoshi(fixed_fee_amount))

    tx = CMutableTransaction(
        vin=[CTxIn(prevout=COutPoint(hash=lx(txid), n=vout_n))],
        vout=[
            CTxOut(nValue=out_amount, scriptPubKey=dst_addr.to_scriptPubKey())
        ])

    if branch_condition is True:
        cond = b'\x01'
    else:
        tx.vin[0].nSequence = bitcoin_contract_timeout
        cond = b''

    in_amount = coins_to_satoshi(pre_agreed_amount)
    # We used P2WSHCoinAddress to create the address that we sent bitcoin to,
    # so we know that we need to use SIGVERSION_WITNESS_V0
    sighash = btc_contract.sighash(tx,
                                   0,
                                   SIGHASH_ALL,
                                   amount=in_amount,
                                   sigversion=SIGVERSION_WITNESS_V0)

    spend_sig = spend_key.sign(sighash) + bytes([SIGHASH_ALL])

    # This is script witness, not script. The condition for OP_IF
    # in our script is directly encoded as data in the witness.
    # We cannot use OP_TRUE/OP_FALSE here. We use DATA guard is to ensure that.
    witness = CScriptWitness([spend_sig, DATA(cond), btc_contract])

    # empty scriptSig, because segwit
    tx.vin[0].scriptSig = CBitcoinScript([])
    # all data to check the spend conditions is in the witness
    tx.wit.vtxinwit[0] = CTxInWitness(witness)

    # Cannot use VerifyScript for now,
    # because it does not support CHECKSEQUENCEVERIFY yet
    #
    # from_addr = P2WSHBitcoinAddress.from_redeemScript(btc_contract)
    # VerifyScript(tx.vin[0].scriptSig, from_addr.to_scriptPubKey(),
    #              tx, 0, amount=in_amount)

    return tx
Example #4
0
def find_utxo_for_fee(say, die, rpc):
    """Find suitable utxo to pay the fee.
    Retrieve thekey to spend this utxo and add it to
    the returned dict."""

    # Find utxo to use for fee. In our simple example, only Alice pays the fee.
    # To be on a safe side, include only transactions
    # that are confirmed (1 as 'minconf' argument of listunspent)
    # and safe to spend (False as 'include_unsafe' # argument of listunspent)
    say('Searching for utxo for fee asset')
    utxo_list = rpc.listunspent(1, 9999999, [], False,
                                {'asset': fee_asset.to_hex()})
    utxo_list.sort(key=lambda u: u['amount'])
    for utxo in utxo_list:
        # To not deal with possibility of dust outputs,
        # just require fee utxo to be big enough
        if coins_to_satoshi(utxo['amount']) >= FIXED_FEE_SATOSHI * 2:
            utxo['key'] = CCoinKey(rpc.dumpprivkey(utxo['address']))
            if 'assetcommitment' not in utxo:
                # If UTXO is not blinded, Elements daemon will not
                # give us assetcommitment, so we need to generate it ourselves.
                asset = CAsset(lx(utxo['asset']))
                utxo['assetcommitment'] = b2x(asset.to_commitment())
            return utxo
    else:
        die('Cannot find utxo for fee that is >= {} satoshi'.format(
            FIXED_FEE_SATOSHI * 2))
Example #5
0
 def test_MoneyRange(self):
     self.assertFalse(MoneyRange(-1))
     with self.assertRaises(ValueError):
         coins_to_satoshi(-1)
     with self.assertRaises(ValueError):
         satoshi_to_coins(-1)
     self.assertTrue(MoneyRange(0))
     self.assertTrue(MoneyRange(100000))
     max_satoshi = coins_to_satoshi(21000000)
     self.assertTrue(
         MoneyRange(max_satoshi))  # Maximum money on Bitcoin network
     self.assertFalse(MoneyRange(max_satoshi + 1))
     with self.assertRaises(ValueError):
         coins_to_satoshi(max_satoshi + 1)
     with self.assertRaises(ValueError):
         satoshi_to_coins(max_satoshi + 1)
Example #6
0
    def check_unblind(self, unblinded_tx, unblinded_tx_raw, blinded_tx,
                      blinded_tx_raw, bundle, blinding_derivation_key):
        for n, bvout in enumerate(blinded_tx.vout):
            uvout = unblinded_tx.vout[n]

            if not uvout.nValue.is_explicit():
                # skip confidential vouts of partially-blinded txs
                continue

            self.assertEqual(bvout.scriptPubKey, uvout.scriptPubKey)
            if bvout.nAsset.is_explicit():
                self.assertTrue(bvout.nValue.is_explicit())
                self.assertEqual(bvout.nValue.to_amount(),
                                 uvout.nValue.to_amount())
                self.assertEqual(bvout.nAsset.to_asset().data,
                                 uvout.nAsset.to_asset().data)
                self.assertEqual(bvout.nNonce.commitment,
                                 uvout.nNonce.commitment)
            else:
                self.assertFalse(bvout.nValue.is_explicit())

                for fbk, spk_set in bundle['foreign_blinding_keys'].items():
                    if b2x(bvout.scriptPubKey) in spk_set:
                        blinding_key = uvout.scriptPubKey.derive_blinding_key(
                            CKey(lx(fbk)))
                        break
                else:
                    blinding_key = uvout.scriptPubKey.derive_blinding_key(
                        blinding_derivation_key)

                unblind_result = bvout.unblind_confidential_pair(
                    blinding_key=blinding_key,
                    rangeproof=blinded_tx.wit.vtxoutwit[n].rangeproof)

                self.assertFalse(unblind_result.error)
                self.assertEqual(uvout.nValue.to_amount(),
                                 unblind_result.amount)
                self.assertEqual(uvout.nAsset.to_asset().data,
                                 unblind_result.asset.data)
                descr = unblind_result.get_descriptor()

                self.assertIsInstance(descr, BlindingInputDescriptor)
                self.assertEqual(descr.amount, unblind_result.amount)
                self.assertEqual(descr.asset, unblind_result.asset)
                self.assertEqual(descr.blinding_factor,
                                 unblind_result.blinding_factor)
                self.assertEqual(descr.asset_blinding_factor,
                                 unblind_result.asset_blinding_factor)

                ub_info = bundle['unblinded_vout_info'][n]
                if len(ub_info):
                    self.assertEqual(coins_to_satoshi(ub_info['amount']),
                                     unblind_result.amount)
                    self.assertEqual(ub_info['asset'],
                                     unblind_result.asset.to_hex())
                    self.assertEqual(ub_info['blinding_factor'],
                                     unblind_result.blinding_factor.to_hex())
                    self.assertEqual(
                        ub_info['asset_blinding_factor'],
                        unblind_result.asset_blinding_factor.to_hex())
def sign_input(tx, input_index, utxo):
    """Sign an input of transaction.
    Single-signature signing with SIGHASH_ALL"""

    key = utxo['key']
    src_addr = CCoinAddress(utxo['address'])

    script_for_sighash = CScript(
        [OP_DUP, OP_HASH160,
         Hash160(key.pub), OP_EQUALVERIFY, OP_CHECKSIG])

    assert isinstance(src_addr, (P2PKHCoinAddress, P2SHCoinAddress,
                                 P2WPKHCoinAddress)),\
        'only p2pkh, p2wpkh and p2sh_p2wpkh addresses are supported'

    if isinstance(src_addr, P2PKHCoinAddress):
        sigversion = SIGVERSION_BASE
    else:
        sigversion = SIGVERSION_WITNESS_V0

    if 'amountcommitment' in utxo:
        amountcommitment = CConfidentialValue(x(utxo['amountcommitment']))
    else:
        amountcommitment = CConfidentialValue(coins_to_satoshi(utxo['amount']))

    sighash = script_for_sighash.sighash(tx,
                                         input_index,
                                         SIGHASH_ALL,
                                         amount=amountcommitment,
                                         sigversion=sigversion)

    sig = key.sign(sighash) + bytes([SIGHASH_ALL])

    if isinstance(src_addr, P2PKHCoinAddress):
        tx.vin[input_index].scriptSig = CScript(
            [CScript(sig), CScript(key.pub)])
        scriptpubkey = src_addr.to_scriptPubKey()
    elif isinstance(src_addr, P2WPKHCoinAddress):
        tx.vin[input_index].scriptSig = CScript()
        tx.wit.vtxinwit[input_index] = CTxInWitness(
            CScriptWitness([CScript(sig), CScript(key.pub)]))
        scriptpubkey = src_addr.to_scriptPubKey()
    else:
        # Assume that this is p2sh-wrapped p2wpkh address
        inner_scriptPubKey = CScript([0, Hash160(key.pub)])
        tx.vin[input_index].scriptSig = CScript([inner_scriptPubKey])
        tx.wit.vtxinwit[input_index] = CTxInWitness(
            CScriptWitness([CScript(sig), CScript(key.pub)]))
        scriptpubkey = inner_scriptPubKey.to_p2sh_scriptPubKey()

    VerifyScript(tx.vin[input_index].scriptSig,
                 scriptpubkey,
                 tx,
                 input_index,
                 amount=amountcommitment,
                 flags=(SCRIPT_VERIFY_P2SH, ))
    def check_sign(self, blinded_tx: CTransaction, signed_tx: CTransaction,
                   bundle: Dict[str, Any]) -> None:
        tx_to_sign = blinded_tx.to_mutable()
        for n, vin in enumerate(tx_to_sign.vin):
            utxo = bundle['vin_utxo'][n]
            amount = -1 if utxo['amount'] == -1 else coins_to_satoshi(
                utxo['amount'])

            scriptPubKey = CScript(x(utxo['scriptPubKey']))
            a = CCoinAddress(utxo['address'])
            if 'privkey' in utxo:
                privkey = CCoinKey(utxo['privkey'])
                assert isinstance(a, P2PKHCoinAddress),\
                    "only P2PKH is supported for single-sig"
                assert a == P2PKHElementsAddress.from_pubkey(privkey.pub)
                assert scriptPubKey == a.to_scriptPubKey()
                sighash = SignatureHash(scriptPubKey,
                                        tx_to_sign,
                                        n,
                                        SIGHASH_ALL,
                                        amount=amount,
                                        sigversion=SIGVERSION_BASE)
                sig = privkey.sign(sighash) + bytes([SIGHASH_ALL])
                tx_to_sign.vin[n].scriptSig = CScript(
                    [CScript(sig), CScript(privkey.pub)])
            else:
                pk_list = [CCoinKey(pk) for pk in utxo['privkey_list']]
                redeem_script_data = [utxo['num_p2sh_participants']]
                redeem_script_data.extend([pk.pub for pk in pk_list])
                redeem_script_data.extend([len(pk_list), OP_CHECKMULTISIG])
                redeem_script = CScript(redeem_script_data)
                assert isinstance(a, P2SHCoinAddress),\
                    "only P2SH is supported for multi-sig."
                assert scriptPubKey == redeem_script.to_p2sh_scriptPubKey()
                assert a == P2SHElementsAddress.from_scriptPubKey(
                    redeem_script.to_p2sh_scriptPubKey())
                sighash = SignatureHash(redeem_script,
                                        tx_to_sign,
                                        n,
                                        SIGHASH_ALL,
                                        amount=amount,
                                        sigversion=SIGVERSION_BASE)
                sigs = [
                    pk.sign(sighash) + bytes([SIGHASH_ALL]) for pk in pk_list
                ]
                tx_to_sign.vin[n].scriptSig = CScript([b''] + sigs +
                                                      [redeem_script])

            VerifyScript(tx_to_sign.vin[n].scriptSig,
                         scriptPubKey,
                         tx_to_sign,
                         n,
                         amount=amount)

        self.assertEqual(tx_to_sign.serialize(), signed_tx.serialize())
Example #9
0
    def test_p2wpkh_signaturehash(self):
        unsigned_tx = x(
            '0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000'
        )
        scriptpubkey = CScript(
            x('00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1'))
        value = coins_to_satoshi(6)

        address = CBech32BitcoinAddress.from_scriptPubKey(scriptpubkey)
        self.assertEqual(
            SignatureHash(address.to_redeemScript(),
                          CTransaction.deserialize(unsigned_tx), 1,
                          SIGHASH_ALL, value, SIGVERSION_WITNESS_V0),
            x('c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670'
              ))
Example #10
0
    def test_p2sh_p2wpkh_signaturehash(self):
        unsigned_tx = x(
            '0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000'
        )
        scriptpubkey = CScript(
            x('001479091972186c449eb1ded22b78e40d009bdf0089'))
        value = coins_to_satoshi(10)

        address = CCoinAddress.from_scriptPubKey(scriptpubkey)
        self.assertEqual(
            SignatureHash(address.to_redeemScript(),
                          CTransaction.deserialize(unsigned_tx), 0,
                          SIGHASH_ALL, value, SIGVERSION_WITNESS_V0),
            x('64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6'
              ))
Example #11
0
    def test_p2sh_p2wsh_signaturehash(self):
        unsigned_tx = x(
            '010000000136641869ca081e70f394c6948e8af409e18b619df2ed74aa106c1ca29787b96e0100000000ffffffff0200e9a435000000001976a914389ffce9cd9ae88dcc0631e88a821ffdbe9bfe2688acc0832f05000000001976a9147480a33f950689af511e6e84c138dbbd3c3ee41588ac00000000'
        )

        value = coins_to_satoshi(9.87654321)
        witnessscript = CScript(
            x('56210307b8ae49ac90a048e9b53357a2354b3334e9c8bee813ecb98e99a7e07e8c3ba32103b28f0c28bfab54554ae8c658ac5c3e0ce6e79ad336331f78c428dd43eea8449b21034b8113d703413d57761b8b9781957b8c0ac1dfe69f492580ca4195f50376ba4a21033400f6afecb833092a9a21cfdf1ed1376e58c5d1f47de74683123987e967a8f42103a6d48b1131e94ba04d9737d61acdaa1322008af9602b3b14862c07a1789aac162102d8b661b0b3302ee2f162b09e07a55ad5dfbe673a9f01d9f0c19617681024306b56ae'
              ))

        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_ALL, value, SIGVERSION_WITNESS_V0),
            x('185c0be5263dce5b4bb50a047973c1b6272bfbd0103a89444597dc40b248ee7c'
              ))
        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_NONE, value, SIGVERSION_WITNESS_V0),
            x('e9733bc60ea13c95c6527066bb975a2ff29a925e80aa14c213f686cbae5d2f36'
              ))
        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_SINGLE, value, SIGVERSION_WITNESS_V0),
            x('1e1f1c303dc025bd664acb72e583e933fae4cff9148bf78c157d1e8f78530aea'
              ))
        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_ALL | SIGHASH_ANYONECANPAY, value,
                          SIGVERSION_WITNESS_V0),
            x('2a67f03e63a6a422125878b40b82da593be8d4efaafe88ee528af6e5a9955c6e'
              ))
        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_NONE | SIGHASH_ANYONECANPAY, value,
                          SIGVERSION_WITNESS_V0),
            x('781ba15f3779d5542ce8ecb5c18716733a5ee42a6f51488ec96154934e2c890a'
              ))
        self.assertEqual(
            SignatureHash(witnessscript, CTransaction.deserialize(unsigned_tx),
                          0, SIGHASH_SINGLE | SIGHASH_ANYONECANPAY, value,
                          SIGVERSION_WITNESS_V0),
            x('511e8e52ed574121fc1b654970395502128263f62662e076dc6baf05c2e6a99b'
              ))
Example #12
0
    def test_MoneyRangeCustomParams(self) -> None:
        class CoreHighMaxClassDispatcher(CoreBitcoinClassDispatcher):
            ...

        class CoreHighMaxClass(CoreBitcoinClass,
                               metaclass=CoreHighMaxClassDispatcher):
            ...

        class CoreHighMaxParams(CoreBitcoinParams, CoreHighMaxClass):
            @classgetter
            def MAX_MONEY(self) -> int:
                return 10**100 * self.COIN

        class WalletHighMaxClassDispatcher(WalletBitcoinClassDispatcher,
                                           depends=[
                                               CoreHighMaxClassDispatcher
                                           ]):
            ...

        class HighMaxParams(BitcoinMainnetParams):
            NAME = 'high_maxmoney'
            WALLET_DISPATCHER = WalletHighMaxClassDispatcher

        with self.assertRaises(ValueError):
            coins_to_satoshi(10**100)

        with ChainParams(HighMaxParams):
            self.assertFalse(MoneyRange(-1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(-1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(-1)
            self.assertTrue(MoneyRange(0))
            self.assertTrue(MoneyRange(100000))
            max_satoshi = coins_to_satoshi(10**100)
            self.assertEqual(satoshi_to_coins(max_satoshi), 10**100)
            self.assertTrue(MoneyRange(max_satoshi))
            self.assertFalse(MoneyRange(max_satoshi + 1))
            with self.assertRaises(ValueError):
                coins_to_satoshi(max_satoshi + 1)
            with self.assertRaises(ValueError):
                satoshi_to_coins(max_satoshi + 1)
Example #13
0
    def test_p2wsh_signaturehash1(self):
        unsigned_tx = x(
            '0100000002fe3dc9208094f3ffd12645477b3dc56f60ec4fa8e6f5d67c565d1c6b9216b36e0000000000ffffffff0815cf020f013ed6cf91d29f4202e8a58726b1ac6c79da47c23d1bee0a6925f80000000000ffffffff0100f2052a010000001976a914a30741f8145e5acadf23f751864167f32e0963f788ac00000000'
        )
        value2 = coins_to_satoshi(49)
        scriptcode1 = CScript(
            x('21026dccc749adc2a9d0d89497ac511f760f45c47dc5ed9cf352a58ac706453880aeadab210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac'
              ))
        # This is the same script with everything up to the last executed OP_CODESEPARATOR, including that
        # OP_CODESEPARATOR removed
        scriptcode2 = CScript(
            x('210255a9626aebf5e29c0e6538428ba0d1dcf6ca98ffdf086aa8ced5e0d0215ea465ac'
              ))

        self.assertEqual(
            SignatureHash(scriptcode1, CTransaction.deserialize(unsigned_tx),
                          1, SIGHASH_SINGLE, value2, SIGVERSION_WITNESS_V0),
            x('82dde6e4f1e94d02c2b7ad03d2115d691f48d064e9d52f58194a6637e4194391'
              ))
        self.assertEqual(
            SignatureHash(scriptcode2, CTransaction.deserialize(unsigned_tx),
                          1, SIGHASH_SINGLE, value2, SIGVERSION_WITNESS_V0),
            x('fef7bd749cce710c5c052bd796df1af0d935e59cea63736268bcbe2d2134fc47'
              ))
Example #14
0
# Create the txin structure, which includes the outpoint. The scriptSig
# defaults to being empty.
txin = CMutableTxIn(COutPoint(txid, vout))

# We also need the scriptPubKey of the output we're spending because
# SignatureHash() replaces the transaction scriptSig's with it.
#
# Here we'll create that scriptPubKey from scratch using the pubkey that
# corresponds to the secret key we generated above.
txin_scriptPubKey = \
    P2PKHBitcoinAddress.from_pubkey(seckey.pub).to_scriptPubKey()

# Create the txout. This time we create the scriptPubKey from a Bitcoin
# address.
txout = CMutableTxOut(
    coins_to_satoshi(0.001),
    CBitcoinAddress('1C7zdTfnkzmr13HfA2vNm5SJYRK6nEKyq8').to_scriptPubKey())

# Create the unsigned transaction.
tx = CMutableTransaction([txin], [txout])

# Calculate the signature hash for that transaction.
sighash = SignatureHash(txin_scriptPubKey, 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])
Example #15
0
import sys

PATH_TO_FUNCTIONAL = "~/bitcoin/test/functional"
PATH_TO_BITCOINTX = "~/python-bitcointx"
TEST_DATADIR = "tests/data"

sys.path.insert(0, PATH_TO_BITCOINTX)
sys.path.insert(0, PATH_TO_FUNCTIONAL)

from bitcointx import select_chain_params
from bitcointx.core import coins_to_satoshi

select_chain_params("bitcoin/regtest")

MIN_FEE = coins_to_satoshi(0.00000169)
TIMELOCK = 3
PORTION_TO_VAULT = 0.1
Example #16
0
def alice(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Elements-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

    # Default chain for Alice will be Elements
    # To handle bitcoin-related objects, either
    # `with ChainParams(bitcoin_chain_name):` have to be used, or
    # concrete classes, like CBitcoinAddress, CBitcoinTransaction, etc.
    select_chain_params(elements_chain_name)

    # Let's create the shared blinding key
    blinding_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for btc spend
    alice_btc_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for the 'timeout' branch of the contract
    alice_elt_exit_key = CKey.from_secret_bytes(os.urandom(32))

    say('Sending pubkeys to Bob')
    send('pubkeys', (alice_btc_key.pub, alice_elt_exit_key.pub))

    say('Sending the blinding key to Bob')
    send('blinding_key', blinding_key.secret_bytes)

    (contract_pubkey_raw, bob_elt_pubkey_raw,
     bob_btc_exit_pub_raw) = recv('pubkeys')

    say("Pubkey of the key to be revealed: {}".format(
        b2x(contract_pubkey_raw)))
    say("Bob's Elements-side pubkey: {}".format(b2x(bob_elt_pubkey_raw)))

    contract_pubkey = CPubKey(contract_pubkey_raw)

    key_to_reveal_pub = CPubKey.add(contract_pubkey, blinding_key.pub)

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_pubkey_raw,
                                    alice_elt_exit_key.pub)

    elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    confidential_contract_addr = P2SHCoinConfidentialAddress.from_unconfidential(
        elt_contract_addr, blinding_key.pub)
    assert isinstance(confidential_contract_addr, CElementsConfidentialAddress)

    say("Created Elemets-side swap contract, size: {}".format(
        len(elt_contract)))
    say("Contract address:\n\tconfidential: {}\n\tunconfidential: {}".format(
        confidential_contract_addr, elt_contract_addr))

    btc_txid = recv('btc_txid')

    combined_btc_spend_pubkey = CPubKey.add(contract_pubkey, alice_btc_key.pub)
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_pub_raw)

    tx_json = btc_rpc.getrawtransaction(btc_txid, 1)

    if tx_json['confirmations'] < 6:
        die('Transaction does not have enough confirmations')

    # We use ChainParams, and not P2WSHBitcoinAddress here,
    # because bitcoin_chain_name might be 'bitcoin/regtest', for example,
    # and then the address would need to be P2WSHBitcoinRegtestAddress.
    # with ChainParams we leverage the 'frontend class' magic, P2WSHCoinAddress
    # will give us appropriate instance.
    with ChainParams(bitcoin_chain_name):
        btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)
        say('Looking for this address in transaction {} in Bitcoin'.format(
            btc_txid))

    # CTransaction subclasses do not change between mainnet/testnet/regtest,
    # so we can directly use CBitcoinTransaction.
    # That might not be true for other chains, though.
    # You might also want to use CTransaction within `with ChainParams(...):`
    btc_tx = CBitcoinTransaction.deserialize(x(tx_json['hex']))

    for n, vout in enumerate(btc_tx.vout):
        if vout.scriptPubKey == btc_contract_addr.to_scriptPubKey():
            say("Found the address at output {}".format(n))
            btc_vout_n = n
            break
    else:
        die('Did not find contract address in transaction')

    if vout.nValue != coins_to_satoshi(pre_agreed_amount):
        die('the amount {} found at the output in the offered transaction '
            'does not match the expected amount {}'.format(
                satoshi_to_coins(vout.nValue), pre_agreed_amount))

    say('Bitcoin amount match expected values')

    say('Sending {} to {}'.format(pre_agreed_amount,
                                  confidential_contract_addr))
    contract_txid = elt_rpc.sendtoaddress(str(confidential_contract_addr),
                                          pre_agreed_amount)

    def alice_last_wish_func():
        try_reclaim_elt(say, elt_rpc, contract_txid, elt_contract,
                        alice_elt_exit_key, blinding_key, die)

    last_wish_func = alice_last_wish_func

    wait_confirm(say, 'Elements', contract_txid, die, elt_rpc, num_confirms=2)

    send('elt_txid', contract_txid)

    sr_txid = wait_spend_reveal_transaction(say, contract_txid, die, elt_rpc)

    say('Got txid for spend-reveal transaction from Bob ({})'.format(sr_txid))

    tx_json = elt_rpc.getrawtransaction(sr_txid, 1)

    wait_confirm(say, 'Elements', sr_txid, die, elt_rpc, num_confirms=2)

    sr_tx = CTransaction.deserialize(x(tx_json['hex']))

    for n, vin in enumerate(sr_tx.vin):
        if vin.prevout.hash == lx(contract_txid)\
                and vin.scriptSig[-(len(elt_contract)):] == elt_contract:
            say('Transaction input {} seems to contain a script '
                'we can recover the key from'.format(n))
            reveal_script_iter = iter(vin.scriptSig)
            break
    else:
        die('Spend-reveal transaction does not have input that spends '
            'the contract output')

    next(reveal_script_iter)  # skip Bob's spend signature

    try:
        # 2 skipped bytes are tag and len
        sig_s = ecdsa.util.string_to_number(next(reveal_script_iter)[2:])
    except (ValueError, StopIteration):
        die('Reveal script is invalid')

    k, r = get_known_k_r()
    order = ecdsa.SECP256k1.order
    mhash = ecdsa.util.string_to_number(hashlib.sha256(b'\x01').digest())
    r_inverse = ecdsa.numbertheory.inverse_mod(r, order)

    for s in (-sig_s, sig_s):
        secret_exponent = (((s * k - mhash) % order) * r_inverse) % order

        recovered_key = CKey.from_secret_bytes(
            ecdsa.util.number_to_string(secret_exponent, order))

        if recovered_key.pub == key_to_reveal_pub:
            break
    else:
        die('Key recovery failed. Should not happen - the sig was already '
            'verified when transaction was accepted into mempool. '
            'Must be a bug.')

    say('recovered key pubkey: {}'.format(b2x(recovered_key.pub)))
    contract_key = CKey.sub(recovered_key, blinding_key)
    say('recovered unblined key pubkey: {}'.format(b2x(contract_key.pub)))
    combined_btc_spend_key = CKey.add(contract_key, alice_btc_key)

    say('Successfully recovered the key. Can now spend Bitcoin from {}'.format(
        btc_contract_addr))

    with ChainParams(bitcoin_chain_name):
        dst_addr = CCoinAddress(btc_rpc.getnewaddress())
        btc_claim_tx = create_btc_spend_tx(dst_addr,
                                           btc_txid,
                                           btc_vout_n,
                                           btc_contract,
                                           spend_key=combined_btc_spend_key)

    say('Sending my Bitcoin-claim transaction')
    btc_claim_txid = btc_rpc.sendrawtransaction(b2x(btc_claim_tx.serialize()))

    wait_confirm(say, 'Bitcoin', btc_claim_txid, die, btc_rpc, num_confirms=3)

    say('Got my Bitcoin. Swap successful!')
Example #17
0
def claim_funds_back(say, utxos, die, rpc):
    """Try to claim our funds by sending our UTXO to our own addresses"""

    # The transaction-building code here does not introduce anything new
    # compared to the code in participant functions, so it will not be
    # commented too much.

    input_descriptors = []
    # It is better to prepare the claw-back transaction beforehand, to avoid
    # the possibility of unexpected problems arising at the critical time when
    # we need to send claw-back tx ASAP, but that would clutter the earlier
    # part of the example with details that are not very relevant there.
    tx = CMutableTransaction()
    for utxo in utxos:
        tx.vin.append(
            CTxIn(prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])))
        input_descriptors.append(
            BlindingInputDescriptor(
                asset=CAsset(lx(utxo['asset'])),
                amount=coins_to_satoshi(utxo['amount']),
                blinding_factor=Uint256(lx(utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

    asset_amounts = {}
    # If some assets are the same, we want them to be sent to one address
    for idesc in input_descriptors:
        if idesc.asset == fee_asset:
            amount = idesc.amount - FIXED_FEE_SATOSHI
            assert amount >= FIXED_FEE_SATOSHI  # enforced at find_utxo_for_fee
        else:
            amount = idesc.amount

        asset_amounts[idesc.asset] = amount

    output_pubkeys = []
    for asset, amount in asset_amounts.items():
        dst_addr, _ = get_dst_addr(None, rpc)
        tx.vout.append(
            CTxOut(nValue=CConfidentialValue(amount),
                   nAsset=CConfidentialAsset(asset),
                   scriptPubKey=dst_addr.to_scriptPubKey()))
        output_pubkeys.append(dst_addr.blinding_pubkey)

    # Add the explicit fee output
    tx.vout.append(
        CTxOut(nValue=CConfidentialValue(FIXED_FEE_SATOSHI),
               nAsset=CConfidentialAsset(fee_asset)))
    # Add dummy pubkey for non-blinded fee output
    output_pubkeys.append(CPubKey())

    # We used immutable objects for transaction components like CTxIn,
    # just for our convenience. Convert them all to mutable.
    tx = tx.to_immutable().to_mutable()

    # And blind the combined transaction
    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys)

    assert (not blind_result.error
            and blind_result.num_successfully_blinded == len(utxos))

    for n, utxo in enumerate(utxos):
        sign_input(tx, n, utxo)

    # It is possible that Bob has actually sent the swap transaction.
    # We will get an error if our node has received this transaction.
    # In real application, we might handle this case, too, but
    # here we will just ignore it.
    txid = rpc.sendrawtransaction(b2x(tx.serialize()))

    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, txid, die, rpc)
Example #18
0
def alice(say, recv, send, die, rpc):
    """A function that implements the logic
    of the first participant of an asset atomic swap"""

    # Issue two asset that we are going to swap to Bob's 1 asset
    asset1_str, asset1_utxo = issue_asset(say, 1.0, rpc)
    asset2_str, asset2_utxo = issue_asset(say, 1.0, rpc)

    # We will need to pay a fee in an asset suitable for this
    fee_utxo = find_utxo_for_fee(say, die, rpc)

    say('Getting change address for fee asset')
    # We don't care for blinding key of change - the node will
    # have it, anyway, and we don't need to unblind the change.
    fee_change_addr, _ = get_dst_addr(say, rpc)

    say('Will use utxo {}:{} (amount: {}) for fee, change will go to {}'.
        format(fee_utxo['txid'], fee_utxo['vout'], fee_utxo['amount'],
               fee_change_addr))

    say('Setting up communication with Bob')

    # Tell Bob that we are ready to communicate
    send('ready')

    # To avoid mempool synchronization problems,
    # in our example Alice is the one in charge of generating test blocks.
    # Bob gives alice txid of his transaction that he wants to be confirmed.
    bob_txid = recv('wait-txid-confirm')

    # Make sure asset issuance transactions are confirmed
    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, asset1_utxo['txid'], die, rpc)
    wait_confirm(say, asset2_utxo['txid'], die, rpc)
    wait_confirm(say, bob_txid, die, rpc)

    # Make sure Bob is alive and ready to communicate, and send
    # him an offer for two assets
    say('Sending offer to Bob')
    my_offers = [
        AtomicSwapOffer(asset=asset1_str,
                        amount=coins_to_satoshi(asset1_utxo['amount'])),
        AtomicSwapOffer(asset=asset2_str,
                        amount=coins_to_satoshi(asset2_utxo['amount']))
    ]
    send('offer', my_offers)

    bob_offer = recv('offer')

    print_asset_balances(say, my_offers + [bob_offer], rpc)

    say('Bob responded with his offer: {}'.format(bob_offer))

    # We unconditionally accept Bob's offer - his asset is
    # equally worthless as ours :-)

    # Generate an address for Bob to send his asset to.
    dst_addr, blinding_key = get_dst_addr(say, rpc)

    say('Sending my address and assetcommitments for my UTXOs to Bob')
    # Send Bob our address, and the assetcommitments of our UTXOs
    # (but not any other information about our UTXO),
    # so he can construct and blind a partial transaction that
    # will spend his own UTXO, to send his asset to our address.
    assetcommitments = [
        asset1_utxo['assetcommitment'], asset2_utxo['assetcommitment'],
        fee_utxo['assetcommitment']
    ]

    send('addr_and_assetcommitments', (str(dst_addr), assetcommitments))

    partial_tx_bytes = recv('partial_blinded_tx')

    say('Got partial blinded tx of size {} bytes from Bob'.format(
        len(partial_tx_bytes)))

    partial_tx = CTransaction.deserialize(partial_tx_bytes)

    if len(partial_tx.vout) != 1:
        die('unexpected number of outputs in tx from Bob: expected 1, got {}'.
            format(len(partial_tx.vout)))

    result = partial_tx.vout[0].unblind_confidential_pair(
        blinding_key, partial_tx.wit.vtxoutwit[0].rangeproof)

    if result.error:
        die('cannot unblind output that should have been directed to us: {}'.
            format(result.error))

    if result.asset.to_hex() != bob_offer.asset:
        die("asset in partial transaction from Bob {} is not the same "
            "as asset in Bob's initial offer ({})".format(
                result.asset.to_hex(), bob_offer.asset))

    if result.amount != bob_offer.amount:
        die("amount in partial transaction from Bob {} is not the same "
            "as amount in Bob's initial offer ({})".format(
                result.amount, bob_offer.amount))

    say("Asset and amount in partial transaction matches Bob's offer")

    bob_addr_list, bob_assetcommitment = recv('addr_list_and_assetcommitment')

    if len(bob_addr_list) != len(my_offers):
        die('unexpected address list lenth from Bob. expected {}, got {}'.
            format(len(my_offers), len(bob_addr_list)))

    say("Bob's addresses to receive my assets: {}".format(bob_addr_list))

    # Convert Bob's addresses to address objects.
    # If Bob passes invalid address, we die with with exception.
    bob_addr_list = [CCoinAddress(a) for a in bob_addr_list]

    # Add our own inputs and outputs to Bob's partial tx

    # Create new mutable transaction from partial_tx
    tx = partial_tx.to_mutable()

    # We have assetcommitment for the first input,
    # other data is not needed for it.
    # initialize first elements of the arrays with empty/negative data.
    input_descriptors = [
        BlindingInputDescriptor(asset=CAsset(),
                                amount=-1,
                                blinding_factor=Uint256(),
                                asset_blinding_factor=Uint256())
    ]

    # First output is already blinded, fill the slot with empty data
    output_pubkeys = [CPubKey()]

    # But assetcommitments array should start with Bob's asset commitment
    assetcommitments = [x(bob_assetcommitment)]

    # We will add our inputs for asset1 and asset2, and also an input
    # that will be used to pay the fee.

    # Note that the order is important: Bob blinded his transaction
    # with assetcommitments in the order we send them to him,
    # and we should add our inputs in the same order.
    utxos_to_add = (asset1_utxo, asset2_utxo, fee_utxo)

    # Add inputs for asset1 and asset2 and fee_asset and prepare input data
    # for blinding
    for utxo in utxos_to_add:
        # When we create CMutableTransaction and pass CTxIn,
        # it will be converted to CMutableTxIn. But if we append
        # to tx.vin or tx.vout, we need to use mutable versions
        # of the txin/txout classes, or else blinding or signing
        # will fail with error, unable to modify the instances.
        # COutPoint is not modified, though, so we can leave it
        # immutable.
        tx.vin.append(
            CMutableTxIn(
                prevout=COutPoint(hash=lx(utxo['txid']), n=utxo['vout'])))
        input_descriptors.append(
            BlindingInputDescriptor(
                asset=CAsset(lx(utxo['asset'])),
                amount=coins_to_satoshi(utxo['amount']),
                blinding_factor=Uint256(lx(utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

        # If we are supplying asset blinders and assetblinders for
        # particular input, assetcommitment data for that input do
        # not need to be correct. But if we are supplying assetcommitments
        # at all (auxiliary_generators argument to tx.blind()),
        # then all the elements of that array must have correct
        # type (bytes) and length (33). This is a requirement of the original
        # Elements Core API, and python-elementstx requires this, too.
        assetcommitments.append(b'\x00' * 33)

    # Add outputs to give Bob all our assets, and fill output pubkeys
    # for blinding the outputs to Bob's addresses
    for n, offer in enumerate(my_offers):
        tx.vout.append(
            CMutableTxOut(nValue=CConfidentialValue(offer.amount),
                          nAsset=CConfidentialAsset(CAsset(lx(offer.asset))),
                          scriptPubKey=bob_addr_list[n].to_scriptPubKey()))
        output_pubkeys.append(bob_addr_list[n].blinding_pubkey)

    # Add change output for fee asset
    fee_change_amount = (coins_to_satoshi(fee_utxo['amount']) -
                         FIXED_FEE_SATOSHI)
    tx.vout.append(
        CMutableTxOut(nValue=CConfidentialValue(fee_change_amount),
                      nAsset=CConfidentialAsset(fee_asset),
                      scriptPubKey=fee_change_addr.to_scriptPubKey()))
    output_pubkeys.append(fee_change_addr.blinding_pubkey)

    # Add fee output.
    # Note that while we use CConfidentialAsset and CConfidentialValue
    # to specify value and asset, they are not in fact confidential here
    # - they are explicit, because we pass explicit values at creation.
    # You can check if they are explicit or confidential
    # with nValue.is_explicit(). If they are explicit, you can access
    # the unblinded values with nValue.to_amount() and nAsset.to_asset()
    tx.vout.append(
        CMutableTxOut(nValue=CConfidentialValue(FIXED_FEE_SATOSHI),
                      nAsset=CConfidentialAsset(fee_asset)))
    # Add dummy pubkey for non-blinded fee output
    output_pubkeys.append(CPubKey())

    # Our transaction lacks txin witness instances for the added inputs,
    # and txout witness instances for added outputs.
    # If transaction already have witness data attached, transaction
    # serialization code will require in/out witness array length
    # to be equal to vin/vout array length
    # Therefore we need to add dummy txin and txout witnesses for each
    # input and output that we added to transaction
    # we added one input and one output per asset, and an additional
    # input/change-output for fee asset.
    for _ in utxos_to_add:
        tx.wit.vtxinwit.append(CMutableTxInWitness())
        tx.wit.vtxoutwit.append(CMutableTxOutWitness())

    # And one extra dummy txout witness for fee output
    tx.wit.vtxoutwit.append(CMutableTxOutWitness())

    # And blind the combined transaction
    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys,
                            auxiliary_generators=assetcommitments)

    # The blinding must succeed!
    if blind_result.error:
        die('blind failed: {}'.format(blind_result.error))

    # And must blind exactly three outputs (two to Bob, one fee asset change)
    if blind_result.num_successfully_blinded != 3:
        die('blinded {} outputs, expected to be 3'.format(
            blind_result.num_successfully_blinded))

    say('Successfully blinded the combined transaction, will now sign')

    # Sign two new asset inputs, and fee asset input
    for n, utxo in enumerate(utxos_to_add):
        # We specify input_index as 1+n because we skip first (Bob's) input
        sign_input(tx, 1 + n, utxo)

    say('Signed my inputs, sending partially-signed transaction to Bob')

    send('partially_signed_tx', tx.serialize())

    # Note that at this point both participants can still opt out of the swap:
    # Alice by double-spending her inputs to the transaction,
    # and Bob by not signing or not broadcasting the transaction.
    # Bob still have tiny advantage, because
    # he can pretend to have 'difficulties' in broadcasting and try to exploit
    # Alice's patience. If Alice does not reclaim her funds in the case Bob's
    # behaviour deviates from expected, then Bob will have free option to
    # exectute the swap at the time convenient to him.

    # Get the swap transaction from Bob.
    # Bob is expected to broadcast this transaction, and could just send txid
    # here, but then there would be a period of uncertainty: if Alice do not
    # see the txid at her own node, she does not know if this is because Bob
    # did not actually broadcast, and is just taking his time watching asset
    # prices, or the transaction just takes long time to propagate. If the
    # protocol requires Bob to send the transaction, the timeout required for
    # Alice to wait can be defined much more certainly.
    try:
        signed_tx_raw = recv('final-signed-tx', timeout=ALICE_PATIENCE_LIMIT)
        signed_tx = CTransaction.deserialize(x(signed_tx_raw))
        # Check that this transaction spends the same inputs as the transacton
        # previously agreed upon
        for n, vin in enumerate(signed_tx.vin):
            if vin.prevout != tx.vin[n].prevout:
                die('Inputs of transaction received from Bob do not match '
                    'the agreed-upon transaction')
        # Send the transaction from our side
        txid = rpc.sendrawtransaction(b2x(signed_tx.serialize()))
    except Exception as e:
        # If there is any problem, including communication timeout or invalid
        # communication, or invalid transaction encoding, then Alice will try
        # to claim her funds back, so Bob won't have an option to execute the
        # swap at the time convenient to him. He should execute it immediately.
        say('Unexpected problem on receiving final signed transaction '
            'from Bob: {}'.format(e))
        say('This is suspicious. I will try to reclaim my funds now')
        claim_funds_back(say, utxos_to_add, die, rpc)
        say("Claimed my funds back. Screw Bob!")
        sys.exit(0)

    # Make sure the final transaction is confirmed
    rpc.generatetoaddress(1, rpc.getnewaddress())
    wait_confirm(say, txid, die, rpc)

    # Check that everything went smoothly
    balance = coins_to_satoshi(rpc.getbalance("*", 1, False, bob_offer.asset))
    if balance != bob_offer.amount:
        die('something went wrong, balance of Bob\'s asset after swap '
            'should be {} satoshi, but it is {} satoshi'.format(
                balance, bob_offer.amount))

    print_asset_balances(say, my_offers + [bob_offer], rpc)

    # Wait for alice to politely end the conversation
    send('thanks-goodbye')

    say('Asset atomic swap completed successfully')
Example #19
0
def bob(say, recv, send, die, btc_rpc, elt_rpc):
    """A function that implements the logic
    of the Bitcoin-side participant
    of confidential cross-chain atomic swap"""

    global last_wish_func

    # Default chain for Bob will be Bitcoin
    # To handle bitcoin-related objects, either
    # `with ChainParams(elements_chain_name):` have to be used, or
    # concrete classes, like CElementsAddress, CElementsTransaction, etc.
    select_chain_params(bitcoin_chain_name)

    say('Waiting for blinding key from Alice')
    alice_btc_pub_raw, alice_elt_exit_pub_raw = recv('pubkeys')

    blinding_key = CKey.from_secret_bytes(recv('blinding_key'))
    say("Pubkey for blinding key: {}".format(b2x(blinding_key.pub)))

    # Let's create the key that would lock the coins on Bitcoin side
    contract_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for Elements side
    bob_elt_spend_key = CKey.from_secret_bytes(os.urandom(32))
    # And the key for 'timeout' case on btc side
    bob_btc_exit_key = CKey.from_secret_bytes(os.urandom(32))

    key_to_reveal_pub = CPubKey.add(contract_key.pub, blinding_key.pub)
    say("The pubkey of the combined key to be revealed: {}".format(
        b2x(key_to_reveal_pub)))

    say('Sending my pubkeys to Alice')
    send('pubkeys',
         (contract_key.pub, bob_elt_spend_key.pub, bob_btc_exit_key.pub))

    combined_btc_spend_pubkey = CPubKey.add(contract_key.pub,
                                            CPubKey(alice_btc_pub_raw))

    say('combined_btc_spend_pubkey: {}'.format(b2x(combined_btc_spend_pubkey)))
    btc_contract = make_btc_contract(combined_btc_spend_pubkey,
                                     bob_btc_exit_key.pub)

    btc_contract_addr = P2WSHCoinAddress.from_redeemScript(btc_contract)

    say("Created Bitcoin-side swap contract, size: {}".format(
        len(btc_contract)))
    say("Contract address: {}".format(btc_contract_addr))

    say('Sending {} to {}'.format(pre_agreed_amount, btc_contract_addr))
    btc_txid = btc_rpc.sendtoaddress(str(btc_contract_addr), pre_agreed_amount)

    def bob_last_wish_func():
        try_reclaim_btc(say, btc_rpc, btc_txid, btc_contract, bob_btc_exit_key,
                        die)

    last_wish_func = bob_last_wish_func

    wait_confirm(say, 'Bitcoin', btc_txid, die, btc_rpc, num_confirms=6)

    send('btc_txid', btc_txid)
    elt_txid = recv('elt_txid')

    elt_contract = make_elt_cntract(key_to_reveal_pub, bob_elt_spend_key.pub,
                                    alice_elt_exit_pub_raw)

    with ChainParams(elements_chain_name):
        elt_contract_addr = P2SHCoinAddress.from_redeemScript(elt_contract)

    say('Got Elements contract address from Alice: {}'.format(
        elt_contract_addr))
    say('Looking for this address in transaction {} in Elements'.format(
        elt_txid))

    tx_json = elt_rpc.getrawtransaction(elt_txid, 1)

    if tx_json['confirmations'] < 2:
        die('Transaction does not have enough confirmations')

    elt_commit_tx = CElementsTransaction.deserialize(x(tx_json['hex']))

    vout_n, unblind_result = find_and_unblind_vout(say, elt_commit_tx,
                                                   elt_contract_addr,
                                                   blinding_key, die)

    if unblind_result.amount != coins_to_satoshi(pre_agreed_amount):
        die('the amount {} found at the output in the offered transaction '
            'does not match the expected amount {}'.format(
                satoshi_to_coins(unblind_result.amount), pre_agreed_amount))

    say('The asset and amount match expected values. lets spend it.')

    with ChainParams(elements_chain_name):
        dst_addr = CCoinAddress(elt_rpc.getnewaddress())
        assert isinstance(dst_addr, CCoinConfidentialAddress)

        say('I will claim my Elements-BTC to {}'.format(dst_addr))

        elt_claim_tx = create_elt_spend_tx(
            dst_addr,
            elt_txid,
            vout_n,
            elt_contract,
            die,
            spend_key=bob_elt_spend_key,
            contract_key=contract_key,
            blinding_key=blinding_key,
            blinding_factor=unblind_result.blinding_factor,
            asset_blinding_factor=unblind_result.asset_blinding_factor)

        # Cannot use VerifyScript for now,
        # because it does not support CHECKSIGFROMSTACK yet
        #
        # VerifyScript(tx.vin[0].scriptSig,
        #              elt_contract_addr.to_scriptPubKey(),
        #              tx, 0, amount=amount)

    say('Sending my spend-reveal transaction')
    sr_txid = elt_rpc.sendrawtransaction(b2x(elt_claim_tx.serialize()))

    wait_confirm(say, 'Elements', sr_txid, die, elt_rpc, num_confirms=2)

    say('Got my Elements-BTC. Swap successful (at least for me :-)')
Example #20
0
def bob(say, recv, send, die, rpc):
    """A function that implements the logic
    of the second participant of an asset atomic swap"""

    # Issue an asset that we are going to swap
    asset_str, asset_utxo = issue_asset(say, 1.0, rpc)
    asset_amount_satoshi = coins_to_satoshi(asset_utxo['amount'])

    say('Setting up communication with Alice')

    # Wait for Alice to start communication
    recv('ready')
    # To avoid mempool synchronization problems in two-node regtest setup,
    # in our example Alice is the one in charge of generating test blocks.
    # Send txid of asset issuance to alice so she can ensure it is confirmed.
    send('wait-txid-confirm', asset_utxo['txid'])

    say('Waiting for Alice to send us an offer array')

    alice_offers = recv('offer')

    # We unconditionally accept Alice's offer - her assets are
    # equally worthless as our asset :-)

    say("Alice's offers are {}, sending my offer".format(alice_offers))

    my_offer = AtomicSwapOffer(amount=asset_amount_satoshi, asset=asset_str)

    send('offer', my_offer)

    say('Waiting for Alice\'s address and assetcommitments')

    alice_addr_str, alice_assetcommitments = recv('addr_and_assetcommitments')

    print_asset_balances(say, alice_offers + [my_offer], rpc)

    # Convert Alice's address to address object.
    # If Alice passes invalid address, we die with we die with exception.
    alice_addr = CCoinAddress(alice_addr_str)

    say('Alice\'s address: {}'.format(alice_addr))
    say('Alice\'s assetcommitments: {}'.format(alice_assetcommitments))

    # Create asset commitments array. First goes our own asset commitment,
    # because our UTXO will be first.
    assetcommitments = [x(asset_utxo['assetcommitment'])]
    for ac in alice_assetcommitments:
        # If Alice sends non-hex data, we will die while converting.
        assetcommitments.append(x(ac))

    # Let's create our part of the transaction. We need to create
    # mutable transaction, because blind() method only works for mutable.
    partial_tx = CMutableTransaction(
        vin=[
            CTxIn(prevout=COutPoint(hash=lx(asset_utxo['txid']),
                                    n=asset_utxo['vout']))
        ],
        vout=[
            CTxOut(nValue=CConfidentialValue(asset_amount_satoshi),
                   nAsset=CConfidentialAsset(CAsset(lx(asset_str))),
                   scriptPubKey=alice_addr.to_scriptPubKey())
        ])

    # Blind our part of transaction, specifying assetcommitments
    # (Incliding those received from Alice) as auxiliary_generators.

    # Note that we could get the blinding factors if we retrieve
    # the transaction that we spend from, deserialize it, and unblind
    # the output that we are going to spend.
    # We could do everything here (besides issuing the asset and sending
    # the transactions) without using Elements RPC, if we get our data
    # from files or database, etc. But to simplify our demonstration,
    # we will use the values we got from RPC.

    # See 'spend-to-confidential-address.py' example for the code
    # that does the unblinding itself, and uses the unblinded values
    # to create a spending transaction.

    blind_result = partial_tx.blind(
        input_descriptors=[
            BlindingInputDescriptor(
                asset=CAsset(lx(asset_utxo['asset'])),
                amount=asset_amount_satoshi,
                blinding_factor=Uint256(lx(asset_utxo['amountblinder'])),
                asset_blinding_factor=Uint256(lx(asset_utxo['assetblinder'])))
        ],
        output_pubkeys=[alice_addr.blinding_pubkey],
        auxiliary_generators=assetcommitments)

    # The blinding must succeed!
    if blind_result.error:
        die('blind failed: {}'.format(blind_result.error))

    # And must blind exactly one output
    if blind_result.num_successfully_blinded != 1:
        die('blinded {} outputs, expected to be 1'.format(
            blind_result.num_successfully_blinded))

    say('Successfully blinded partial transaction, sending it to Alice')

    send('partial_blinded_tx', partial_tx.serialize())

    say("Generating addresses to receive Alice's assets")
    # Generate as many destination addresses as there are assets
    # in Alice's offer. Record blinding keys for the addresses.
    our_addrs = []
    blinding_keys = []
    for _ in alice_offers:
        addr, blinding_key = get_dst_addr(say, rpc)
        our_addrs.append(str(addr))
        blinding_keys.append(blinding_key)

    say("Sending my addresses and assetcommitment to Alice")
    send('addr_list_and_assetcommitment',
         (our_addrs, asset_utxo['assetcommitment']))

    semi_signed_tx_bytes = recv('partially_signed_tx')

    say('Got partially signed tx of size {} bytes from Alice'.format(
        len(semi_signed_tx_bytes)))

    semi_signed_tx = CTransaction.deserialize(semi_signed_tx_bytes)

    # Transaction should have 3 extra outputs - one output to Alice,
    # fee output, and fee asset change output
    if len(semi_signed_tx.vout) != len(alice_offers) + 3:
        die('unexpected number of outputs in tx from Alice: '
            'expected {}, got {}'.format(
                len(alice_offers) + 3, len(semi_signed_tx.vout)))

    if not semi_signed_tx.vout[-1].is_fee():
        die('Last output in tx from Alice '
            'is expected to be fee output, but it is not')

    # Unblind outputs that should be directed to us and check
    # that they match the offer. We use n+1 as output index
    # because we skip our own output, which is at index 0.
    for n, offer in enumerate(alice_offers):
        result = semi_signed_tx.vout[n + 1].unblind_confidential_pair(
            blinding_keys[n], semi_signed_tx.wit.vtxoutwit[n + 1].rangeproof)

        if result.error:
            die('cannot unblind output {} that should have been '
                'directed to us: {}'.format(n + 1, result.error))

        if result.asset.to_hex() != offer.asset:
            die("asset at position {} (vout {}) in partial transaction "
                "from Alice {} is not the same as asset in Alice's "
                "initial offer ({})".format(n, n + 1, result.asset.to_hex(),
                                            offer.asset))

        if result.amount != offer.amount:
            die("amount at position {} (vout {}) in partial transaction "
                "from Alice {} is not the same as amount in Alice's "
                "initial offer ({})".format(n, n + 1, result.amount,
                                            offer.amount))

    say("Assets and amounts in partially signed transaction "
        "match Alice's offer")

    # Signing will change the tx, so i
    tx = semi_signed_tx.to_mutable()

    # Our input is at index 0
    sign_input(tx, 0, asset_utxo)

    # Note that at this point both participants can still opt out of the swap:
    # Bob by not broadcasting the transaction, and Alice by double-spending
    # her inputs to the transaction. Bob still have tiny advantage, because
    # he can pretend to have 'difficulties' in broadcasting and try to exploit
    # Alice's patience

    say('Signed the transaction from my side, ready to send')

    tx_hex = b2x(tx.serialize())

    if bob_be_sneaky:
        say('Hey! I am now in control of the final transaction. '
            'I have the option to exectue the swap or abort. ')
        say('Why not wait a bit and watch asset prices, and execute '
            'the swap only if it is profitable')
        say('I will reduce my risk a bit by doing that.')
        # Bob takes his time and is not sending the final
        # transaction to Alice for some time...
        time.sleep(ALICE_PATIENCE_LIMIT + 2)
        say('OK, I am willing to execute the swap now')

    # Send the final transaction to Alice, so she can be sure that
    # we is not cheating
    send('final-signed-tx', tx_hex)

    txid = rpc.sendrawtransaction(tx_hex)

    say('Sent with txid {}'.format(txid))

    # Wait for alice to politely end the conversation
    recv('thanks-goodbye')
    print_asset_balances(say, alice_offers + [my_offer], rpc)

    for i, offer in enumerate(alice_offers):
        balance = coins_to_satoshi(rpc.getbalance("*", 1, False, offer.asset))
        if balance != offer.amount:
            die('something went wrong, asset{} balance after swap should be '
                '{} satoshi, but it is {} satoshi'.format(
                    i, balance, offer.amount))

    say('Asset atomic swap completed successfully')
Example #21
0
def create_elt_spend_tx(dst_addr,
                        txid,
                        vout_n,
                        elt_contract,
                        die,
                        spend_key=None,
                        contract_key=None,
                        blinding_key=None,
                        blinding_factor=None,
                        asset_blinding_factor=None,
                        branch_condition=True):

    fee_satoshi = coins_to_satoshi(fixed_fee_amount)
    out_amount = coins_to_satoshi(pre_agreed_amount) - fee_satoshi

    # Single blinded output is not allowed, so we add
    # dummy OP_RETURN output, and we need dummy pubkey for it
    dummy_key = CKey.from_secret_bytes(os.urandom(32))

    tx = CMutableTransaction(
        vin=[CTxIn(prevout=COutPoint(hash=lx(txid), n=vout_n))],
        vout=[
            CTxOut(nValue=CConfidentialValue(out_amount),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   scriptPubKey=dst_addr.to_scriptPubKey(),
                   nNonce=CConfidentialNonce(dst_addr.blinding_pubkey)),
            CTxOut(nValue=CConfidentialValue(0),
                   nAsset=CConfidentialAsset(bitcoin_asset),
                   nNonce=CConfidentialNonce(dummy_key.pub),
                   scriptPubKey=CElementsScript([OP_RETURN])),
            CTxOut(nValue=CConfidentialValue(fee_satoshi),
                   nAsset=CConfidentialAsset(bitcoin_asset))
        ])

    output_pubkeys = [dst_addr.blinding_pubkey, dummy_key.pub]

    in_amount = coins_to_satoshi(pre_agreed_amount)

    input_descriptors = [
        BlindingInputDescriptor(asset=bitcoin_asset,
                                amount=in_amount,
                                blinding_factor=blinding_factor,
                                asset_blinding_factor=asset_blinding_factor)
    ]

    blind_result = tx.blind(input_descriptors=input_descriptors,
                            output_pubkeys=output_pubkeys)

    # The blinding must succeed!
    if blind_result.error:
        die('blind failed: {}'.format(blind_result.error))

    if branch_condition is False:
        # Must set nSequence before we calculate signature hash,
        # because it is included in it
        tx.vin[0].nSequence = elements_contract_timeout

    # We used P2SHCoinAddress to create the address that
    # we sent Elements-BTC to, so we know that we need
    # to use SIGVERSION_BASE

    sighash = elt_contract.sighash(tx,
                                   0,
                                   SIGHASH_ALL,
                                   amount=CConfidentialValue(in_amount),
                                   sigversion=SIGVERSION_BASE)

    spend_sig = spend_key.sign(sighash) + bytes([SIGHASH_ALL])

    if branch_condition is True:
        prepare_elt_spend_reveal_branch(tx, elt_contract, spend_sig,
                                        contract_key, blinding_key)
    else:
        tx.vin[0].scriptSig = CElementsScript(
            [spend_sig, OP_FALSE, elt_contract])

    return tx
                digests.append(x(f))
            else:
                raise exp
        except IOError as exp:
            print(exp, file=sys.stderr)
            continue

    for digest in digests:
        txouts = []

        unspent = sorted(rpc.listunspent(0), key=lambda x: hash(x['amount']))

        txins = [
            CTxIn(COutPoint(lx(unspent[-1]['txid']), int(unspent[-1]['vout'])))
        ]
        value_in = coins_to_satoshi(unspent[-1]['amount'])

        change_addr = rpc.getnewaddress()
        change_pubkey_hex = rpc.getaddressinfo(change_addr)['pubkey']
        change_out = CMutableTxOut(
            CoreCoinParams.MAX_MONEY,
            CScript([x(change_pubkey_hex), OP_CHECKSIG]))

        digest_outs = [CMutableTxOut(0, CScript([OP_RETURN, digest]))]

        txouts = [change_out] + digest_outs

        tx = CTransaction(txins, txouts).to_mutable()

        FEE_PER_VBYTE = 0.00025 * CoreCoinParams.COIN / 1000
        while True:
Example #23
0
 def select_coins(self, address: ExternalAddress):
     target_value = coins_to_satoshi(self.spend_amount, False)
     return self.controller.select_coins(target_value, address,
                                         self.priority_fee_selected)
Example #24
0
    def check_serialize_deserialize(self, tx, tx_bytes, tx_decoded):
        self.assertEqual(tx_bytes, tx.serialize())
        self.assertEqual(tx_bytes,
                         CTransaction.deserialize(tx.serialize()).serialize())
        self.assertEqual(tx_bytes, tx.to_mutable().to_immutable().serialize())
        self.assertEqual(tx_decoded['version'], tx.nVersion)
        self.assertEqual(tx_decoded['locktime'], tx.nLockTime)
        # we ignore withash field - we do not have ComputeWitnessHash() function
        # as it is only relevant for blocks, not transactions
        self.assertEqual(tx_decoded['hash'], b2lx(tx.GetHash()))
        self.assertEqual(tx_decoded['txid'], b2lx(tx.GetTxid()))
        for n, vout in enumerate(tx_decoded['vout']):
            if 'amountcommitment' in vout:
                self.assertEqual(x(vout['amountcommitment']),
                                 tx.vout[n].nValue.commitment)
            if 'assetcommitment' in vout:
                self.assertEqual(x(vout['assetcommitment']),
                                 tx.vout[n].nAsset.commitment)
            if 'asset' in vout:
                self.assertEqual(vout['asset'],
                                 tx.vout[n].nAsset.to_asset().to_hex())
            if 'scriptPubKey' in vout:
                spk = vout['scriptPubKey']
                self.assertEqual(x(spk['hex']), tx.vout[n].scriptPubKey)

                if 'pegout_type' in spk:
                    self.assertEqual(spk['type'], 'nulldata')
                    self.assertTrue(tx.vout[n].scriptPubKey.is_pegout())
                    genesis_hash, pegout_scriptpubkey = tx.vout[
                        n].scriptPubKey.get_pegout_data()
                    if spk['pegout_type'] != 'nonstandard':
                        assert spk['pegout_type'] in ('pubkeyhash',
                                                      'scripthash')
                        addr = CCoinAddress.from_scriptPubKey(
                            pegout_scriptpubkey)
                        self.assertEqual(len(spk['pegout_addresses']), 1)
                        self.assertEqual(spk['pegout_addresses'][0], str(addr))
                    self.assertEqual(spk['pegout_hex'],
                                     b2x(pegout_scriptpubkey))
                    self.assertEqual(spk['pegout_chain'], b2lx(genesis_hash))

                if spk['type'] in ('pubkeyhash', 'scripthash'):
                    self.assertEqual(len(spk['addresses']), 1)
                    addr = CCoinAddress.from_scriptPubKey(
                        tx.vout[n].scriptPubKey)
                    self.assertEqual(spk['addresses'][0], str(addr))
                elif spk['type'] == 'nulldata':
                    self.assertEqual(tx.vout[n].scriptPubKey, x(spk['hex']))
                else:
                    self.assertEqual(spk['type'], 'fee')
                    self.assertEqual(len(tx.vout[n].scriptPubKey), 0)

            if secp256k1_has_zkp:
                if tx.wit.is_null():
                    rpinfo = None
                else:
                    rpinfo = tx.wit.vtxoutwit[n].get_rangeproof_info()
                if 'value-minimum' in vout:
                    self.assertIsNotNone(rpinfo)
                    self.assertEqual(vout['ct-exponent'], rpinfo.exp)
                    self.assertEqual(vout['ct-bits'], rpinfo.mantissa)
                    self.assertEqual(
                        coins_to_satoshi(vout['value-minimum'],
                                         check_range=False), rpinfo.value_min)
                    self.assertEqual(
                        coins_to_satoshi(vout['value-maximum'],
                                         check_range=False), rpinfo.value_max)
                else:
                    self.assertTrue(rpinfo is None or rpinfo.exp == -1)
                    if rpinfo is None:
                        value = tx.vout[n].nValue.to_amount()
                    else:
                        value = rpinfo.value_min
                    self.assertEqual(coins_to_satoshi(vout['value']), value)
            else:
                warn_zkp_unavailable()
                if 'value' in vout and tx.vout[n].nValue.is_explicit():
                    self.assertEqual(coins_to_satoshi(vout['value']),
                                     tx.vout[n].nValue.to_amount())

        for n, vin in enumerate(tx_decoded['vin']):
            if 'scripSig' in vin:
                self.assertEqual(
                    x(vin['scriptSig']['hex'], tx.vin[n].scriptSig))
            if 'txid' in vin:
                self.assertEqual(vin['txid'], b2lx(tx.vin[n].prevout.hash))
            if 'vout' in vin:
                self.assertEqual(vin['vout'], tx.vin[n].prevout.n)
            if 'is_pegin' in vin:
                self.assertEqual(vin['is_pegin'], tx.vin[n].is_pegin)
                if vin['is_pegin'] is False:
                    if 'scriptWitness' in vin:
                        self.assertTrue(
                            tx.wit.vtxinwit[n].scriptWitness.is_null())
                    if 'pegin_witness' in vin:
                        self.assertTrue(
                            tx.wit.vtxinwit[n].pegin_witness.is_null())
                else:
                    for stack_index, stack_item in enumerate(
                            vin['scriptWitness']):
                        self.assertTrue(
                            stack_item,
                            b2x(tx.wit.vtxinwit[n].scriptWitness.
                                stack[stack_index]))
                    for stack_index, stack_item in enumerate(
                            vin['pegin_witness']):
                        self.assertTrue(
                            stack_item,
                            b2x(tx.wit.vtxinwit[n].pegin_witness.
                                stack[stack_index]))
            if 'sequence' in vin:
                self.assertEqual(vin['sequence'], tx.vin[n].nSequence)
            if 'coinbase' in vin:
                self.assertTrue(tx.is_coinbase())
            if 'issuance' in vin:
                iss = vin['issuance']
                self.assertEqual(
                    iss['assetBlindingNonce'],
                    tx.vin[n].assetIssuance.assetBlindingNonce.to_hex())
                if 'asset' in iss:
                    if iss['isreissuance']:
                        self.assertTrue(not tx.vin[n].assetIssuance.
                                        assetBlindingNonce.is_null())
                        self.assertEqual(
                            iss['assetEntropy'],
                            tx.vin[n].assetIssuance.assetEntropy.to_hex())
                        asset = calculate_asset(
                            tx.vin[n].assetIssuance.assetEntropy)
                    else:
                        entropy = generate_asset_entropy(
                            tx.vin[n].prevout,
                            tx.vin[n].assetIssuance.assetEntropy)
                        self.assertEqual(iss['assetEntropy'], entropy.to_hex())
                        asset = calculate_asset(entropy)
                        reiss_token = calculate_reissuance_token(
                            entropy,
                            tx.vin[n].assetIssuance.nAmount.is_commitment())
                        self.assertEqual(iss['token'], reiss_token.to_hex())
                    self.assertEqual(iss['asset'], asset.to_hex())
                if 'assetamount' in iss:
                    self.assertEqual(
                        coins_to_satoshi(iss['assetamount']),
                        tx.vin[n].assetIssuance.nAmount.to_amount())
                elif 'assetamountcommitment' in iss:
                    self.assertEqual(
                        iss['assetamountcommitment'],
                        b2x(tx.vin[n].assetIssuance.nAmount.commitment))
                if 'tokenamount' in iss:
                    self.assertEqual(
                        coins_to_satoshi(iss['tokenamount']),
                        tx.vin[n].assetIssuance.nInflationKeys.to_amount())
                elif 'tokenamountcommitment' in iss:
                    self.assertEqual(
                        iss['tokenamountcommitment'],
                        b2x(tx.vin[n].assetIssuance.nInflationKeys.commitment))
#
# lx() takes *little-endian* hex and converts it to bytes; in Bitcoin
# transaction hashes are shown little-endian rather than the usual big-endian.
# There's also a corresponding x() convenience function that takes big-endian
# hex and converts it to bytes.
txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae')
vout = 0

# Create the txin structure, which includes the outpoint. The scriptSig
# defaults to being empty.
txin = CMutableTxIn(COutPoint(txid, vout))

# Create the txout. This time we create the scriptPubKey from a Bitcoin
# address.
txout = CMutableTxOut(
    coins_to_satoshi(0.0005),
    CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey())

# Create the unsigned transaction.
tx = CMutableTransaction([txin], [txout])

# Calculate the signature hash for that transaction. Note how the script we use
# is the redeemScript, not the scriptPubKey. That's because when the CHECKSIG
# operation happens EvalScript() will be evaluating the redeemScript, so the
# corresponding SignatureHash() function will use that same script when it
# replaces the scriptSig in the transaction being hashed with the script being
# executed.
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.
Example #26
0
    def check_blind(self,
                    unblinded_tx,
                    unblinded_tx_raw,
                    blinded_tx,
                    blinded_tx_raw,
                    bundle,
                    blinding_derivation_key,
                    asset_commitments=()):
        input_descriptors = []
        for utxo in bundle['vin_utxo']:
            amount = -1 if utxo['amount'] == -1 else coins_to_satoshi(
                utxo['amount'])
            input_descriptors.append(
                BlindingInputDescriptor(
                    amount=amount,
                    asset=CAsset(lx(utxo['asset'])),
                    blinding_factor=Uint256(lx(utxo['blinder'])),
                    asset_blinding_factor=Uint256(lx(utxo['assetblinder']))))

        num_to_blind = 0
        output_pubkeys = []
        for vout in unblinded_tx.vout:
            if not vout.nNonce.is_null() and vout.nValue.is_explicit():
                output_pubkeys.append(CPubKey(vout.nNonce.commitment))
                num_to_blind += 1
            else:
                output_pubkeys.append(CPubKey())

        tx_to_blind = unblinded_tx.to_mutable()

        blind_issuance_asset_keys = []
        blind_issuance_token_keys = []
        for vin in blinded_tx.vin:
            issuance = vin.assetIssuance
            if not issuance.is_null():
                issuance_blinding_script = CScript(
                    [OP_RETURN, vin.prevout.hash, vin.prevout.n])
                blind_issuance_key = issuance_blinding_script.derive_blinding_key(
                    blinding_derivation_key)
                if issuance.nAmount.is_commitment():
                    blind_issuance_asset_keys.append(blind_issuance_key)
                    num_to_blind += 1
                else:
                    blind_issuance_asset_keys.append(None)
                if issuance.nInflationKeys.is_commitment():
                    blind_issuance_token_keys.append(blind_issuance_key)
                    num_to_blind += 1
                else:
                    blind_issuance_token_keys.append(None)
            else:
                blind_issuance_asset_keys.append(None)
                blind_issuance_token_keys.append(None)

        # Deterministic random was used when generating test transactions,
        # to have reproducible results. We need to set the random seed
        # to the same value that was used when test data was generated.
        # (see note below on that supplying _rand_func parameter to blind()
        #  is intended only for testing code, not for production)
        random.seed(bundle['rand_seed'])

        def rand_func(n):
            return bytes([random.randint(0, 255) for _ in range(n)])

        # Auxiliary generators will be be non-empty only for the case
        # when we are blinding different transaction templates that is
        # then combined into one common transaction, that is done in
        # test_split_blinding_multi_sign().
        # In this case, you need to supply the asset commitments for
        # all of the inputs of the final transaction, even if currently
        # blinded transaction template does not contain these inputs.
        blind_result = tx_to_blind.blind(
            input_descriptors=input_descriptors,
            output_pubkeys=output_pubkeys,
            blind_issuance_asset_keys=blind_issuance_asset_keys,
            blind_issuance_token_keys=blind_issuance_token_keys,
            auxiliary_generators=asset_commitments,

            # IMPORTANT NOTE:
            # Specifying custom _rand_func is only required for testing.
            # Here we use it to supply deterministically generated
            # pseudo-random bytes, so that blinding results will match the test
            # data that was generated using deterministically generated random
            # bytes, with seed values that are saved in 'rand_seed' fields of
            # test data bunldes.
            #
            # In normal code you do should NOT specify _rand_func:
            # os.urandom will be used by default (os.urandom is suitable for cryptographic use)
            _rand_func=rand_func)

        self.assertFalse(blind_result.error)

        if all(_k is None for _k in blind_issuance_asset_keys):
            random.seed(bundle['rand_seed'])
            tx_to_blind2 = unblinded_tx.to_mutable()
            blind_result2 = tx_to_blind2.blind(
                input_descriptors=input_descriptors,
                output_pubkeys=output_pubkeys,
                blind_issuance_asset_keys=blind_issuance_asset_keys,
                blind_issuance_token_keys=blind_issuance_token_keys,
                auxiliary_generators=asset_commitments,
                _rand_func=rand_func)
            self.assertFalse(blind_result2.error)
            self.assertEqual(blind_result, blind_result2)
            self.assertEqual(tx_to_blind.serialize(), tx_to_blind2.serialize())

        self.assertEqual(blind_result.num_successfully_blinded, num_to_blind)
        self.assertNotEqual(unblinded_tx_raw, tx_to_blind.serialize())
        self.assertEqual(blinded_tx_raw, tx_to_blind.serialize())