33# Distributed under the MIT/X11 software license, see the accompanying
44# file COPYING or http://www.opensource.org/licenses/mit-license.php.
55
6+ import io
7+
8+ from decimal import Decimal
9+
610from test_framework .test_framework import BitcoinTestFramework
7- from test_framework .util import *
11+ from test_framework .mininode import (
12+ COIN , CTransaction , CTxOut , CTxOutAsset , CTxOutValue ,
13+ CTxInWitness , CTxOutWitness ,
14+ )
15+ from test_framework .util import (
16+ start_nodes , connect_nodes_bi , assert_equal ,
17+ hex_str_to_bytes , bytes_to_hex_str ,
18+ BITCOIN_ASSET_OUT
19+ )
20+ from test_framework .authproxy import JSONRPCException
21+
822
923class CTTest (BitcoinTestFramework ):
1024
@@ -307,7 +321,8 @@ def run_test(self):
307321 assert_equal (multi_asset_amount ['bitcoin' ], value1 + value3 )
308322 assert_equal (multi_asset_amount [test_asset ], Decimal ('0.00000003' ))
309323
310- # Check blinded multisig functionality
324+ # Check blinded multisig functionality and partial blinding functionality
325+
311326 # Get two pubkeys
312327 blinded_addr = self .nodes [0 ].getnewaddress ()
313328 pubkey = self .nodes [0 ].validateaddress (blinded_addr )["pubkey" ]
@@ -325,16 +340,149 @@ def run_test(self):
325340 # Create blinded address from p2sh address and import corresponding privkey
326341 blinded_multisig_addr = self .nodes [0 ].createblindedaddress (unconfidential_addr , blinding_pubkey )
327342 self .nodes [0 ].importblindingkey (blinded_multisig_addr , blinding_key )
328- self .nodes [1 ].importblindingkey (blinded_multisig_addr , blinding_key )
329- # Send coins to blinded multisig address and check that they were received
330- self .nodes [2 ].sendtoaddress (blinded_multisig_addr , 1 )
343+
344+ # Issue new asset, to use different assets in one transaction when doing
345+ # partial blinding. Just to make these tests a bit more elaborate :-)
346+ issued3 = self .nodes [2 ].issueasset (1 , 0 )
347+ self .nodes [2 ].generate (1 )
331348 self .sync_all ()
332- assert_equal (len (self .nodes [0 ].listunspent (0 , 0 , [unconfidential_addr ])), 1 )
333- assert_equal (len (self .nodes [1 ].listunspent (0 , 0 , [unconfidential_addr ])), 1 )
349+ node2_balance = self .nodes [2 ].getbalance ()
350+ assert (issued3 ['asset' ] in node2_balance )
351+ assert_equal (node2_balance [issued3 ['asset' ]], Decimal (1 ))
334352
335- self .nodes [0 ].generate (1 )
353+ # Send asset to blinded multisig address and check that it was received
354+ self .nodes [2 ].sendtoaddress (blinded_multisig_addr , 1 , "" , "" , False , issued3 ['asset' ])
355+ self .sync_all ()
356+ # We will use this multisig UTXO in our partially-blinded transaction,
357+ # and will also check that multisig UTXO can be successfully spent
358+ # after the transaction is signed by node1 and node0 in succession.
359+ unspent_asset = self .nodes [0 ].listunspent (0 , 0 , [unconfidential_addr ], True , issued3 ['asset' ])
360+ assert_equal (len (unspent_asset ), 1 )
361+ assert (issued3 ['asset' ] not in self .nodes [2 ].getbalance ())
362+
363+ # Create new UTXO on node0 to be used in our partially-blinded transaction
364+ blinded_addr = self .nodes [0 ].getnewaddress ()
365+ addr = self .nodes [0 ].validateaddress (blinded_addr )["unconfidential" ]
366+ self .nodes [0 ].sendtoaddress (blinded_addr , 0.1 )
367+ unspent = self .nodes [0 ].listunspent (0 , 0 , [addr ])
368+ assert_equal (len (unspent ), 1 )
369+
370+ # Create new UTXO on node1 to be used in our partially-blinded transaction
371+ blinded_addr2 = self .nodes [1 ].getnewaddress ()
372+ addr2 = self .nodes [1 ].validateaddress (blinded_addr2 )["unconfidential" ]
373+ self .nodes [1 ].sendtoaddress (blinded_addr2 , 0.11 )
374+ unspent2 = self .nodes [1 ].listunspent (0 , 0 , [addr2 ])
375+ assert_equal (len (unspent2 ), 1 )
376+
377+ # The transaction will have three non-fee outputs
378+ dst_addr = self .nodes [0 ].getnewaddress ()
379+ dst_addr2 = self .nodes [1 ].getnewaddress ()
380+ dst_addr3 = self .nodes [2 ].getnewaddress ()
381+
382+ # Create one part of the transaction to partially blind
383+ rawtx = self .nodes [0 ].createrawtransaction (
384+ [{"txid" : unspent2 [0 ]["txid" ], "vout" : unspent2 [0 ]["vout" ]}], {dst_addr2 : Decimal ("0.01" )})
385+
386+ # Create another part of the transaction to partially blind
387+ rawtx2 = self .nodes [0 ].createrawtransaction (
388+ [{"txid" : unspent [0 ]["txid" ], "vout" : unspent [0 ]["vout" ]},
389+ {"txid" : unspent_asset [0 ]["txid" ], "vout" : unspent_asset [0 ]["vout" ]}],
390+ {dst_addr : Decimal ("0.1" ), dst_addr3 : Decimal ("1.0" )},
391+ 0 ,
392+ {dst_addr : unspent [0 ]['asset' ], dst_addr3 : unspent_asset [0 ]['asset' ]})
393+
394+ sum_i = unspent2 [0 ]["amount" ] + unspent [0 ]["amount" ]
395+ sum_o = 0.01 + 0.10 + 0.1
396+ assert_equal (int (round (sum_i * COIN )), int (round (sum_o * COIN )))
397+
398+ # Blind the first part of the transaction - we need to supply the
399+ # assetcommmitments for all of the inputs, for the surjectionproof
400+ # to be valid after we combine the transactions
401+ blindtx = self .nodes [1 ].blindrawtransaction (
402+ rawtx , True , [
403+ unspent2 [0 ]['assetcommitment' ],
404+ unspent [0 ]['assetcommitment' ],
405+ unspent_asset [0 ]['assetcommitment' ]
406+ ])
407+
408+ # Combine the transactions
409+
410+ # Blinded, but incomplete transaction.
411+ # 1 input and 1 output, but no fee output, and
412+ # it was blinded with 3 asset commitments, that means
413+ # the final transaction should have 3 inputs.
414+ btx = CTransaction ()
415+ btx .deserialize (io .BytesIO (hex_str_to_bytes (blindtx )))
416+
417+ # Unblinded transaction, with 2 inputs and 2 outputs.
418+ # We will add them to the other transaction to make it complete.
419+ ubtx = CTransaction ()
420+ ubtx .deserialize (io .BytesIO (hex_str_to_bytes (rawtx2 )))
421+
422+ # We will add inputs and outputs of unblinded transaction
423+ # on top of inputs and outputs of the blinded, but incomplete transaction.
424+ # We also append empty witness instances to make witness arrays match
425+ # vin/vout arrays
426+ btx .vin .append (ubtx .vin [0 ])
427+ btx .wit .vtxinwit .append (CTxInWitness ())
428+ btx .vout .append (ubtx .vout [0 ])
429+ btx .wit .vtxoutwit .append (CTxOutWitness ())
430+ btx .vin .append (ubtx .vin [1 ])
431+ btx .wit .vtxinwit .append (CTxInWitness ())
432+ btx .vout .append (ubtx .vout [1 ])
433+ btx .wit .vtxoutwit .append (CTxOutWitness ())
434+ # Add explicit fee output
435+ btx .vout .append (CTxOut (nValue = CTxOutValue (10000000 ),
436+ nAsset = CTxOutAsset (BITCOIN_ASSET_OUT )))
437+ btx .wit .vtxoutwit .append (CTxOutWitness ())
438+
439+ # Input 0 is bitcoin asset (already blinded)
440+ # Input 1 is also bitcoin asset
441+ # Input 2 is our new asset
442+ # Input 3 is fee that we added above (also bitcoin asset)
443+
444+ # Blind with wrong order of assetcommitments - such transaction should be rejected
445+ blindtx = self .nodes [0 ].blindrawtransaction (
446+ bytes_to_hex_str (btx .serialize ()), True , [
447+ unspent_asset [0 ]['assetcommitment' ],
448+ unspent [0 ]['assetcommitment' ],
449+ unspent2 [0 ]['assetcommitment' ]
450+ ])
451+
452+ stx2 = self .nodes [1 ].signrawtransaction (blindtx )
453+ stx = self .nodes [0 ].signrawtransaction (stx2 ['hex' ])
454+ self .sync_all ()
455+ try :
456+ self .nodes [2 ].sendrawtransaction (stx ['hex' ])
457+ raise AssertionError (
458+ "Shouldn't be able to send a transaction that was blinded "
459+ "with incorrectly ordered assetcommitments" )
460+ except JSONRPCException :
461+ pass
462+
463+ # Blind with correct order of assetcommitments
464+ blindtx = self .nodes [0 ].blindrawtransaction (
465+ bytes_to_hex_str (btx .serialize ()), True , [
466+ unspent2 [0 ]['assetcommitment' ],
467+ unspent [0 ]['assetcommitment' ],
468+ unspent_asset [0 ]['assetcommitment' ]
469+ ])
470+
471+ stx2 = self .nodes [1 ].signrawtransaction (blindtx )
472+ stx = self .nodes [0 ].signrawtransaction (stx2 ['hex' ])
473+ txid = self .nodes [2 ].sendrawtransaction (stx ['hex' ])
474+ self .nodes [2 ].generate (1 )
475+ assert self .nodes [2 ].getrawtransaction (txid , 1 )['confirmations' ] == 1
336476 self .sync_all ()
337477
478+ # Check that the sent asset has reached its destination
479+ unconfidential_dst_addr3 = self .nodes [2 ].validateaddress (dst_addr3 )["unconfidential" ]
480+ unspent_asset2 = self .nodes [2 ].listunspent (1 , 1 , [unconfidential_dst_addr3 ], True , issued3 ['asset' ])
481+ assert_equal (len (unspent_asset2 ), 1 )
482+ assert_equal (unspent_asset2 [0 ]['amount' ], Decimal (1 ))
483+ # And that the balance was correctly updated
484+ assert_equal (self .nodes [2 ].getbalance ()[issued3 ['asset' ]], Decimal (1 ))
485+
338486 # Basic checks of rawblindrawtransaction functionality
339487 blinded_addr = self .nodes [0 ].getnewaddress ()
340488 addr = self .nodes [0 ].validateaddress (blinded_addr )["unconfidential" ]
0 commit comments