def test_mutable_tx_creation_with_immutable_parts_specified(self): tx = CMutableTransaction( vin=[CTxIn(prevout=COutPoint(hash=b'a' * 32, n=0))], vout=[CTxOut(nValue=1)], witness=CTxWitness([CTxInWitness()])) def check_mutable_parts(tx): self.assertTrue(tx.vin[0].is_mutable()) self.assertTrue(tx.vin[0].prevout.is_mutable()) self.assertTrue(tx.vout[0].is_mutable()) self.assertTrue(tx.wit.is_mutable()) self.assertTrue(tx.wit.vtxinwit[0].is_mutable()) check_mutable_parts(tx) # Test that if we deserialize with CMutableTransaction, # all the parts are mutable tx = CMutableTransaction.deserialize(tx.serialize()) check_mutable_parts(tx) # Test some parts separately, because when created via # CMutableTransaction instantiation, they are created with from_* # methods, and not directly txin = CMutableTxIn(prevout=COutPoint(hash=b'a' * 32, n=0)) self.assertTrue(txin.prevout.is_mutable()) wit = CMutableTxWitness((CTxInWitness(), )) self.assertTrue(wit.vtxinwit[0].is_mutable())
txin_scriptPubKey = \ P2PKHBitcoinAddress.from_pubkey(seckey.pub).to_scriptPubKey() # Create the txout. This time we create the scriptPubKey from a Bitcoin # address. txout = CMutableTxOut( 0.001 * COIN, 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]) # Verify the signature worked. This calls EvalScript() and actually executes # the opcodes in the scripts to see if everything worked out. If it doesn't an # exception will be raised. VerifyScript(txin.scriptSig, txin_scriptPubKey, tx, 0) # Done! Print the transaction to standard output with the bytes-to-hex # function. print(b2x(tx.serialize()))
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)
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')