Skip to content

Commit 4258f9f

Browse files
dgpvinstagibbs
authored andcommitted
add code to test partial blinding functionality to qa/rpc-tests
1 parent a904f02 commit 4258f9f

File tree

1 file changed

+156
-9
lines changed

1 file changed

+156
-9
lines changed

test/functional/feature_confidential_transactions.py

+156-9
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,28 @@
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
69
from test_framework.test_framework import BitcoinTestFramework
7-
from test_framework.util import connect_nodes_bi, assert_equal
810
from test_framework.authproxy import JSONRPCException
9-
from decimal import Decimal
11+
from test_framework.messages import (
12+
COIN,
13+
CTransaction,
14+
CTxOut,
15+
CTxOutAsset,
16+
CTxOutValue,
17+
CTxInWitness,
18+
CTxOutWitness,
19+
)
20+
from test_framework.util import (
21+
connect_nodes_bi,
22+
assert_equal,
23+
hex_str_to_bytes,
24+
bytes_to_hex_str,
25+
BITCOIN_ASSET_OUT,
26+
assert_raises_rpc_error,
27+
)
1028
import os
1129
import re
1230

@@ -378,7 +396,8 @@ def run_test(self):
378396
assert_equal(multi_asset_amount['bitcoin'], value1 + value3)
379397
assert_equal(multi_asset_amount[test_asset], Decimal('0.00000003'))
380398

381-
# Check blinded multisig functionality
399+
# Check blinded multisig functionality and partial blinding functionality
400+
382401
# Get two pubkeys
383402
blinded_addr = self.nodes[0].getnewaddress()
384403
pubkey = self.nodes[0].getaddressinfo(blinded_addr)["pubkey"]
@@ -396,15 +415,143 @@ def run_test(self):
396415
# Create blinded address from p2sh address and import corresponding privkey
397416
blinded_multisig_addr = self.nodes[0].createblindedaddress(unconfidential_addr, blinding_pubkey)
398417
self.nodes[0].importblindingkey(blinded_multisig_addr, blinding_key)
399-
self.nodes[1].importblindingkey(blinded_multisig_addr, blinding_key)
400-
# Send coins to blinded multisig address and check that they were received
401-
self.nodes[2].sendtoaddress(blinded_multisig_addr, 1)
418+
419+
# Issue new asset, to use different assets in one transaction when doing
420+
# partial blinding. Just to make these tests a bit more elaborate :-)
421+
issued3 = self.nodes[2].issueasset(1, 0)
422+
self.nodes[2].generate(1)
402423
self.sync_all()
403-
assert_equal(len(self.nodes[0].listunspent(0, 0, [unconfidential_addr])), 1)
404-
assert_equal(len(self.nodes[1].listunspent(0, 0, [unconfidential_addr])), 1)
424+
node2_balance = self.nodes[2].getbalance()
425+
assert(issued3['asset'] in node2_balance)
426+
assert_equal(node2_balance[issued3['asset']], Decimal(1))
405427

406-
self.nodes[0].generate(1)
428+
# Send asset to blinded multisig address and check that it was received
429+
self.nodes[2].sendtoaddress(address=blinded_multisig_addr, amount=1, assetlabel=issued3['asset'])
407430
self.sync_all()
431+
# We will use this multisig UTXO in our partially-blinded transaction,
432+
# and will also check that multisig UTXO can be successfully spent
433+
# after the transaction is signed by node1 and node0 in succession.
434+
unspent_asset = self.nodes[0].listunspent(0, 0, [unconfidential_addr], True, {"asset":issued3['asset']})
435+
assert_equal(len(unspent_asset), 1)
436+
assert(issued3['asset'] not in self.nodes[2].getbalance())
437+
438+
# Create new UTXO on node0 to be used in our partially-blinded transaction
439+
blinded_addr = self.nodes[0].getnewaddress()
440+
addr = self.nodes[0].validateaddress(blinded_addr)["unconfidential"]
441+
self.nodes[0].sendtoaddress(blinded_addr, 0.1)
442+
unspent = self.nodes[0].listunspent(0, 0, [addr])
443+
assert_equal(len(unspent), 1)
444+
445+
# Create new UTXO on node1 to be used in our partially-blinded transaction
446+
blinded_addr2 = self.nodes[1].getnewaddress()
447+
addr2 = self.nodes[1].validateaddress(blinded_addr2)["unconfidential"]
448+
self.nodes[1].sendtoaddress(blinded_addr2, 0.11)
449+
unspent2 = self.nodes[1].listunspent(0, 0, [addr2])
450+
assert_equal(len(unspent2), 1)
451+
452+
# The transaction will have three non-fee outputs
453+
dst_addr = self.nodes[0].getnewaddress()
454+
dst_addr2 = self.nodes[1].getnewaddress()
455+
dst_addr3 = self.nodes[2].getnewaddress()
456+
457+
# Inputs are selected up front
458+
inputs = [{"txid": unspent2[0]["txid"], "vout": unspent2[0]["vout"]}, {"txid": unspent[0]["txid"], "vout": unspent[0]["vout"]}, {"txid": unspent_asset[0]["txid"], "vout": unspent_asset[0]["vout"]}]
459+
460+
# Create one part of the transaction to partially blind
461+
rawtx = self.nodes[0].createrawtransaction(
462+
inputs, {dst_addr2: Decimal("0.01")})
463+
464+
# Create another part of the transaction to partially blind
465+
rawtx2 = self.nodes[0].createrawtransaction(
466+
inputs,
467+
{dst_addr: Decimal("0.1"), dst_addr3: Decimal("1.0")},
468+
0,
469+
False,
470+
{dst_addr: unspent[0]['asset'], dst_addr3: unspent_asset[0]['asset']})
471+
472+
sum_i = unspent2[0]["amount"] + unspent[0]["amount"]
473+
sum_o = 0.01 + 0.10 + 0.1
474+
assert_equal(int(round(sum_i*COIN)), int(round(sum_o*COIN)))
475+
476+
# Blind the first part of the transaction - we need to supply the
477+
# assetcommmitments for all of the inputs, for the surjectionproof
478+
# to be valid after we combine the transactions
479+
blindtx = self.nodes[1].blindrawtransaction(
480+
rawtx, True, [
481+
unspent2[0]['assetcommitment'],
482+
unspent[0]['assetcommitment'],
483+
unspent_asset[0]['assetcommitment']
484+
])
485+
486+
# Combine the transactions
487+
488+
# Blinded, but incomplete transaction.
489+
# 3 inputs and 1 output, but no fee output, and
490+
# it was blinded with 3 asset commitments, that means
491+
# the final transaction should have 3 inputs.
492+
btx = CTransaction()
493+
btx.deserialize(io.BytesIO(hex_str_to_bytes(blindtx)))
494+
495+
# Unblinded transaction, with 3 inputs and 2 outputs.
496+
# We will add them to the other transaction to make it complete.
497+
ubtx = CTransaction()
498+
ubtx.deserialize(io.BytesIO(hex_str_to_bytes(rawtx2)))
499+
500+
# We will add outputs of unblinded transaction
501+
# on top of inputs and outputs of the blinded, but incomplete transaction.
502+
# We also append empty witness instances to make witness arrays match
503+
# vin/vout arrays
504+
btx.wit.vtxinwit.append(CTxInWitness())
505+
btx.vout.append(ubtx.vout[0])
506+
btx.wit.vtxoutwit.append(CTxOutWitness())
507+
btx.wit.vtxinwit.append(CTxInWitness())
508+
btx.vout.append(ubtx.vout[1])
509+
btx.wit.vtxoutwit.append(CTxOutWitness())
510+
# Add explicit fee output
511+
btx.vout.append(CTxOut(nValue=CTxOutValue(10000000),
512+
nAsset=CTxOutAsset(BITCOIN_ASSET_OUT)))
513+
btx.wit.vtxoutwit.append(CTxOutWitness())
514+
515+
# Input 0 is bitcoin asset (already blinded)
516+
# Input 1 is also bitcoin asset
517+
# Input 2 is our new asset
518+
519+
# Blind with wrong order of assetcommitments - such transaction should be rejected
520+
blindtx = self.nodes[0].blindrawtransaction(
521+
bytes_to_hex_str(btx.serialize()), True, [
522+
unspent_asset[0]['assetcommitment'],
523+
unspent[0]['assetcommitment'],
524+
unspent2[0]['assetcommitment']
525+
])
526+
527+
stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx)
528+
stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex'])
529+
self.sync_all()
530+
531+
assert_raises_rpc_error(-26, "bad-txns-in-ne-out", self.nodes[2].sendrawtransaction, stx['hex'])
532+
533+
# Blind with correct order of assetcommitments
534+
blindtx = self.nodes[0].blindrawtransaction(
535+
bytes_to_hex_str(btx.serialize()), True, [
536+
unspent2[0]['assetcommitment'],
537+
unspent[0]['assetcommitment'],
538+
unspent_asset[0]['assetcommitment']
539+
])
540+
541+
stx2 = self.nodes[1].signrawtransactionwithwallet(blindtx)
542+
stx = self.nodes[0].signrawtransactionwithwallet(stx2['hex'])
543+
txid = self.nodes[2].sendrawtransaction(stx['hex'])
544+
self.nodes[2].generate(1)
545+
assert self.nodes[2].getrawtransaction(txid, 1)['confirmations'] == 1
546+
self.sync_all()
547+
548+
# Check that the sent asset has reached its destination
549+
unconfidential_dst_addr3 = self.nodes[2].validateaddress(dst_addr3)["unconfidential"]
550+
unspent_asset2 = self.nodes[2].listunspent(1, 1, [unconfidential_dst_addr3], True, {"asset":issued3['asset']})
551+
assert_equal(len(unspent_asset2), 1)
552+
assert_equal(unspent_asset2[0]['amount'], Decimal(1))
553+
# And that the balance was correctly updated
554+
assert_equal(self.nodes[2].getbalance()[issued3['asset']], Decimal(1))
408555

409556
# Basic checks of rawblindrawtransaction functionality
410557
blinded_addr = self.nodes[0].getnewaddress()

0 commit comments

Comments
 (0)