Skip to content

Commit 6c5e075

Browse files
committed
add code to test partial blinding functionality to qa/rpc-tests
1 parent d009244 commit 6c5e075

File tree

1 file changed

+156
-8
lines changed

1 file changed

+156
-8
lines changed

qa/rpc-tests/confidential_transactions.py

Lines changed: 156 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,22 @@
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+
610
from 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

923
class 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

Comments
 (0)