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)
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())