Example #1
0
 def from_json(cls, json: dict):
     return cls(
         Block.from_json(json["block"]),
         TxIn(
             OutPoint(
                 lx(json["prevout"]["hash"]),
                 json["prevout"]["n"]
             )
         ),
         TxOut(
             json["value"],
             lx(json["script_pubkey"])
         )
     )
Example #2
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())
Example #3
0
    def test_blind_unnblind_sign(self):
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) + '/data/elements_txs_blinding.json',
                'r') as fd:
            for bundle in json.load(fd):
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CTransaction.deserialize(unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx_raw = x(bundle['signed_hex'])
                signed_tx = CTransaction.deserialize(signed_tx_raw)
                self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                # ensure that str and repr works
                for f in (str, repr):
                    f(unblinded_tx)
                    f(blinded_tx)
                    f(signed_tx)

                if len(blinded_tx.vout) != len(unblinded_tx.vout):
                    assert len(blinded_tx.vout) == len(unblinded_tx.vout) + 1
                    assert blinded_tx.vout[-1].scriptPubKey == b'\x6a',\
                        "expected last output of blinded tx to be OP_RETURN"
                    scriptPubKey = CScript([OP_RETURN])
                    unblinded_tx = unblinded_tx.to_mutable()
                    unblinded_tx.vout.append(
                        CMutableTxOut(
                            nValue=CConfidentialValue(0),
                            nAsset=CConfidentialAsset(
                                unblinded_tx.vout[-1].nAsset.to_asset()),
                            nNonce=CConfidentialNonce(
                                scriptPubKey.derive_blinding_key(
                                    blinding_derivation_key).pub),
                            scriptPubKey=scriptPubKey))
                    unblinded_tx = unblinded_tx.to_immutable()
                    unblinded_tx_raw = unblinded_tx.serialize()

                self.check_blind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                 blinded_tx_raw, bundle,
                                 blinding_derivation_key)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                self.check_sign(blinded_tx, signed_tx, bundle)
Example #4
0
    def listunspent(self, minconf=0, maxconf=9999999, addrs=None):
        """Return unspent transaction outputs in wallet

        Outputs will have between minconf and maxconf (inclusive)
        confirmations, optionally filtered to only include txouts paid to
        addresses in addrs.
        """
        r = None
        if addrs is None:
            r = self._call('listunspent', minconf, maxconf)
        else:
            addrs = [str(addr) for addr in addrs]
            r = self._call('listunspent', minconf, maxconf, addrs)

        r2 = []
        for unspent in r:
            unspent['outpoint'] = COutPoint(lx(unspent['txid']),
                                            unspent['vout'])
            del unspent['txid']
            del unspent['vout']

            # address isn't always available as Bitcoin Core allows scripts w/o
            # an address type to be imported into the wallet, e.g. non-p2sh
            # segwit
            try:
                unspent['address'] = CBitcoinAddress(unspent['address'])
            except KeyError:
                pass
            unspent['scriptPubKey'] = CScript(
                unhexlify(unspent['scriptPubKey']))
            unspent['amount'] = int(unspent['amount'] * COIN)
            r2.append(unspent)
        return r2
Example #5
0
    def getrawtransaction(self, txid, verbose=False):
        """Return transaction with hash txid

        Raises IndexError if transaction not found.

        verbose - If true a dict is returned instead with additional
        information on the transaction.

        Note that if all txouts are spent and the transaction index is not
        enabled the transaction may not be available.
        """
        try:
            r = self._call('getrawtransaction', b2lx(txid),
                           1 if verbose else 0)
        except InvalidAddressOrKeyError as ex:
            raise IndexError('%s.getrawtransaction(): %s (%d)' %
                             (self.__class__.__name__, ex.error['message'],
                              ex.error['code']))
        if verbose:
            r['tx'] = CTransaction.deserialize(unhexlify(r['hex']))
            del r['hex']
            del r['txid']
            del r['version']
            del r['locktime']
            del r['vin']
            del r['vout']
            r['blockhash'] = lx(r['blockhash']) if 'blockhash' in r else None
        else:
            r = CTransaction.deserialize(unhexlify(r))

        return r
Example #6
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 #7
0
    def getblockheader(self, block_hash, verbose=False):
        """Get block header <block_hash>

        verbose - If true a dict is returned with the values returned by
                  getblockheader that are not in the block header itself
                  (height, nextblockhash, etc.)

        Raises IndexError if block_hash is not valid.
        """
        try:
            block_hash = b2lx(block_hash)
        except TypeError:
            raise TypeError(
                '%s.getblockheader(): block_hash must be bytes; got %r instance'
                % (self.__class__.__name__, block_hash.__class__))
        try:
            r = self._call('getblockheader', block_hash, verbose)
        except InvalidAddressOrKeyError as ex:
            raise IndexError('%s.getblockheader(): %s (%d)' %
                             (self.__class__.__name__, ex.error['message'],
                              ex.error['code']))

        if verbose:
            nextblockhash = None
            if 'nextblockhash' in r:
                nextblockhash = lx(r['nextblockhash'])
            return {
                'confirmations': r['confirmations'],
                'height': r['height'],
                'mediantime': r['mediantime'],
                'nextblockhash': nextblockhash,
                'chainwork': x(r['chainwork'])
            }
        else:
            return CBlockHeader.deserialize(unhexlify(r))
Example #8
0
 def test_clone(self):
     outpoint = COutPoint(
         lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
            ), 0)
     txin = CMutableTxIn(prevout=outpoint,
                         scriptSig=CScript(b'\x03abc'),
                         nSequence=0xffffffff)
     self.assertEqual(txin.serialize(), txin.clone().serialize())
Example #9
0
    def getrawmempool(self, verbose=False):
        """Return the mempool"""
        if verbose:
            return self._call('getrawmempool', verbose)

        else:
            r = self._call('getrawmempool')
            r = [lx(txid) for txid in r]
            return r
Example #10
0
    def generate(self, numblocks):
        """Mine blocks immediately (before the RPC call returns)

        numblocks - How many blocks are generated immediately.

        Returns iterable of block hashes generated.
        """
        r = self._call('generate', numblocks)
        return (lx(blk_hash) for blk_hash in r)
    def test_split_blinding_multi_sign(self) -> None:
        if not secp256k1_has_zkp:
            warn_zkp_unavailable()
            return

        with open(
                os.path.dirname(__file__) +
                '/data/elements_txs_split_blinding.json', 'r') as fd:
            split_blind_txdata = json.load(fd)
            # we need to supply asset commitments from all inputs of the final
            # tranaction to the blinding function, even if we are blinding a tx
            # template that does not contain these inputs
            asset_commitments = [
                x(utxo['assetcommitment'])
                for utxo in split_blind_txdata['tx2']['vin_utxo']
            ]

            for txlabel in ('tx1', 'tx2'):
                bundle = split_blind_txdata[txlabel]
                blinded_tx_raw = x(bundle['blinded']['hex'])
                blinded_tx = CElementsTransaction.deserialize(blinded_tx_raw)
                self.assertEqual(blinded_tx.serialize(), blinded_tx_raw)
                self.check_serialize_deserialize(blinded_tx, blinded_tx_raw,
                                                 bundle['blinded'])
                unblinded_tx_raw = x(bundle['unblinded']['hex'])
                unblinded_tx = CElementsTransaction.deserialize(
                    unblinded_tx_raw)

                self.assertEqual(unblinded_tx.serialize(), unblinded_tx_raw)
                self.check_serialize_deserialize(unblinded_tx,
                                                 unblinded_tx_raw,
                                                 bundle['unblinded'])
                signed_tx: Optional[CElementsTransaction]
                if 'signed_hex' in bundle:
                    signed_tx_raw = x(bundle['signed_hex'])
                    signed_tx = CElementsTransaction.deserialize(signed_tx_raw)
                    self.assertEqual(signed_tx.serialize(), signed_tx_raw)
                else:
                    signed_tx = None

                blinding_derivation_key = CKey(
                    lx(bundle['blinding_derivation_key']))

                self.check_blind(unblinded_tx,
                                 unblinded_tx_raw,
                                 blinded_tx,
                                 blinded_tx_raw,
                                 bundle,
                                 blinding_derivation_key,
                                 asset_commitments=asset_commitments)

                self.check_unblind(unblinded_tx, unblinded_tx_raw, blinded_tx,
                                   blinded_tx_raw, bundle,
                                   blinding_derivation_key)

                if signed_tx is not None:
                    self.check_sign(blinded_tx, signed_tx, bundle)
Example #12
0
    def getblockhash(self, height):
        """Return hash of block in best-block-chain at height.

        Raises IndexError if height is not valid.
        """
        try:
            return lx(self._call('getblockhash', height))
        except InvalidParameterError as ex:
            raise IndexError('%s.getblockhash(): %s (%d)' %
                             (self.__class__.__name__, ex.error['message'],
                              ex.error['code']))
    def test_str(self):
        def T(outpoint, expected):
            actual = str(outpoint)
            self.assertEqual(actual, expected)

        T(
            COutPoint(),
            '0000000000000000000000000000000000000000000000000000000000000000:4294967295'
        )
        T(
            COutPoint(
                lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
                   ), 0),
            '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:0'
        )
        T(
            COutPoint(
                lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
                   ), 10),
            '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b:10'
        )
Example #14
0
 def sendtoaddress(self,
                   addr,
                   amount,
                   comment='',
                   commentto='',
                   subtractfeefromamount=False):
     """Send amount to a given address"""
     addr = str(addr)
     amount = float(amount) / COIN
     r = self._call('sendtoaddress', addr, amount, comment, commentto,
                    subtractfeefromamount)
     return lx(r)
Example #15
0
    def sendrawtransaction(self, tx, allowhighfees=False):
        """Submit transaction to local node and network.

        allowhighfees - Allow even if fees are unreasonably high.
        """
        hextx = hexlify(tx.serialize())
        r = None
        if allowhighfees:
            r = self._call('sendrawtransaction', hextx, True)
        else:
            r = self._call('sendrawtransaction', hextx)
        return lx(r)
    def test_repr(self):
        def T(outpoint, expected):
            actual = repr(outpoint)
            self.assertEqual(actual, expected)

        T(COutPoint(), 'CBitcoinOutPoint()')
        T(
            COutPoint(
                lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
                   ), 0),
            "CBitcoinOutPoint(lx('4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'), 0)"
        )
Example #17
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 #18
0
    def get_utxos_by_address(self, address: Address) -> List[Utxo]:
        raw_utxos = self.query_api(self.Query.GET_UNSPENT_TX, str(address))["txs"]
        utxos: List[Utxo] = []

        for raw_utxo in raw_utxos:
            value = int(float(raw_utxo["value"]) * SATOSHIS_PER_COIN)
            tx_hash = raw_utxo["txid"]
            block = self.get_block_by_tx(tx_hash)
            vout = raw_utxo["output_no"]

            tx_in = TxIn(OutPoint(lx(tx_hash), vout))
            tx_out = TxOut(value, address.to_scriptPubKey())

            utxo = Utxo(block, tx_in, tx_out)
            utxos.append(utxo)

        return utxos
Example #19
0
    def sendmany(self,
                 fromaccount,
                 payments,
                 minconf=1,
                 comment='',
                 subtractfeefromamount=[]):
        """Send amount to given addresses.

        payments - dict with {address: amount}
        """
        json_payments = {
            str(addr): float(amount) / COIN
            for addr, amount in payments.items()
        }
        r = self._call('sendmany', fromaccount, json_payments, minconf,
                       comment, subtractfeefromamount)
        return lx(r)
Example #20
0
    def create_custom_block(self, reward):
        txid, _ = self.fund_address(self.next_address(), 10)
        tx2 = self.proxy.getrawtransaction(lx(txid))

        coinbase = CMutableTransaction()
        coinbase.vin.append(CMutableTxIn(COutPoint(), CScript([self.proxy.getblockcount() + 1])))
        coinbase.vout.append(CMutableTxOut(reward * COIN, self.next_address().address.to_scriptPubKey()))

        prev_block_hash = self.proxy.getblockhash(self.proxy.getblockcount())

        ts = self._next_timestamp()
        self.proxy.call("setmocktime", ts)

        for nonce in range(1000):
            block = CBlock(nBits=0x207fffff, vtx=[coinbase, tx2], hashPrevBlock=prev_block_hash, nTime=ts, nNonce=nonce)
            result = self.proxy.submitblock(block)
            if not result:
                self.log.debug("Chosen nonce: {}".format(nonce))
                break
Example #21
0
    def gettxout(self, outpoint, includemempool=True):
        """Return details about an unspent transaction output.

        Raises IndexError if outpoint is not found or was spent.

        includemempool - Include mempool txouts
        """
        r = self._call('gettxout', b2lx(outpoint.hash), outpoint.n,
                       includemempool)

        if r is None:
            raise IndexError('%s.gettxout(): unspent txout %r not found' %
                             (self.__class__.__name__, outpoint))

        r['txout'] = CTxOut(int(r['value'] * COIN),
                            CScript(unhexlify(r['scriptPubKey']['hex'])))
        del r['value']
        del r['scriptPubKey']
        r['bestblock'] = lx(r['bestblock'])
        return r
def load_test_vectors(name):
    with open(os.path.dirname(__file__) + '/data/' + name, 'r') as fd:
        for test_case in json.load(fd):
            # Comments designated by single length strings
            if len(test_case) == 1:
                continue
            assert len(test_case) == 3

            prevouts = {}
            for json_prevout in test_case[0]:
                assert len(json_prevout) == 3
                n = json_prevout[1]
                if n == -1:
                    n = 0xffffffff
                prevout = COutPoint(lx(json_prevout[0]), n)
                prevouts[prevout] = parse_script(json_prevout[2])

            tx_data = x(test_case[1])
            tx = CTransaction.deserialize(tx_data)
            enforceP2SH = test_case[2]

            yield (prevouts, tx, tx_data, enforceP2SH)
Example #23
0
    def raw_multisig(self):
        source = self.next_address()
        self.fund_address(source, 0.1)

        # construct transaction manually
        tx_ins = [CMutableTxIn(COutPoint(source.txid, source.vout))]

        keys = [self.next_address().key for _ in range(3)]
        redeem_script = CScript([OP_2, keys[0].pub, keys[1].pub, keys[2].pub, OP_3, OP_CHECKMULTISIG])
        tx_outs = [
            CMutableTxOut(Coin(0.1 - self.fee).satoshi(), redeem_script)]

        tx = CMutableTransaction(tx_ins, tx_outs)

        # sign and submit
        key = source.key
        script = source.address.to_scriptPubKey()

        sig = self._sign(script, tx, 0, Coin(source.value).satoshi(), key)
        tx_ins[0].scriptSig = CScript([sig, key.pub])

        txid = self._send_transaction(tx, [])
        self.log_value("raw-multisig-tx", txid)

        # Redeem Transaction
        tx_ins = [CMutableTxIn(COutPoint(lx(txid), 0))]
        destination = self.next_address()
        tx_outs = [CMutableTxOut(Coin(0.1 - 2 * self.fee).satoshi(), destination.address.to_scriptPubKey())]
        tx = CMutableTransaction(tx_ins, tx_outs)

        # Sign with 2 out of three keys
        sig1 = self._sign(redeem_script, tx, 0, Coin(0.1 - self.fee).satoshi(), keys[0])
        sig3 = self._sign(redeem_script, tx, 0, Coin(0.1 - self.fee).satoshi(), keys[2])

        tx_ins[0].scriptSig = CScript([OP_0, sig1, sig3])

        txid = self._send_transaction(tx, [])
        self.log_value("raw-multisig-redeem-tx", txid)
        self.generate_block()
Example #24
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 #25
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 #26
0
def main():
    """The main function prepares everyting for two participant processes
    to operate and communicate with each other, and starts them"""

    global console_lock
    global fee_asset

    if len(sys.argv) != 4:
        sys.stderr.write(
            "usage: {} <alice-daemon-dir> <bob-daemon-dir> <fee_asset_hex>\n".
            format(sys.argv[0]))
        sys.exit(-1)

    elements_config_path1 = os.path.join(sys.argv[1], 'elements.conf')
    if not os.path.isfile(elements_config_path1):
        sys.stderr.write(
            'config file {} not found or is not a regular file\n'.format(
                elements_config_path1))
        sys.exit(-1)

    elements_config_path2 = os.path.join(sys.argv[2], 'elements.conf')
    if not os.path.isfile(elements_config_path2):
        sys.stderr.write(
            'config file {} not found or is not a regular file\n'.format(
                elements_config_path2))
        sys.exit(-1)

    try:
        fee_asset = CAsset(lx(sys.argv[3]))
    except ValueError as e:
        sys.stderr.write('specified fee asset is not valid: {}\n'.format(e))
        sys.exit(-1)

    # Initialize console lock
    console_lock = Lock()

    # Switch the chain parameters to Elements.
    # The setting should remain in place for child processes.
    select_chain_params('elements')

    # Create a pipe for processes to communicate
    pipe1, pipe2 = Pipe(duplex=True)

    # Create process to run 'alice' participant function
    # and pass it one end of a pipe, and path to config file for node1
    p1 = Process(target=participant,
                 name='alice',
                 args=(alice, 'Alice', pipe1, elements_config_path1))

    # Create process to run 'bob' participant function
    # and pass it one end of a pipe, and path to config file for node2
    p2 = Process(target=participant,
                 name='bob',
                 args=(bob, '  Bob', pipe2, elements_config_path2))

    # Start both processes
    p1.start()
    p2.start()

    # The childs are on their own now. We just wait for them to finish.
    try:
        p1.join()
        p2.join()
    except KeyboardInterrupt:
        print()
        print("=============================================================")
        print("Interrupted from keyboard, terminating participant processes.")
        print("-------------------------------------------------------------")
        for p in (p1, p2):
            if p.is_alive():
                print('terminating', p.name)
                p.terminate()
            else:
                print(p.name, 'is not alive')
            p.join()
        print('Exiting.')
        print("=============================================================")
Example #27
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 #28
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())
Example #29
0
select_chain_params('bitcoin')

COIN = CoreCoinParams.COIN

# Create the (in)famous correct brainwallet secret key.
h = hashlib.sha256(b'correct horse battery staple').digest()
seckey = CBitcoinKey.from_secret_bytes(h)

# Same as the txid:vout the createrawtransaction RPC call requires
#
# 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('7e195aa3de827814f172c362fcf838d92ba10e3f9fdd9c3ecaf79522b311b22d')
vout = 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
# to understand what's happening, as well as read BIP16:
# https://github.com/bitcoin/bips/blob/master/bip-0016.mediawiki
txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()

# Convert the P2SH scriptPubKey to a base58 Bitcoin address and print it.
# You'll need to send some funds to it to create a txout to spend.
txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)
print('Pay to:', str(txin_p2sh_address))

# Same as the txid:vout the createrawtransaction RPC call requires
#
# 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(0.0005*COIN,
                      CBitcoinAddress(
                          '323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp'
                      ).to_scriptPubKey())

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