2020-04-16 13:14:08 -04:00
// Copyright (c) 2009-2020 The Bitcoin Core developers
2019-01-09 02:06:29 -08:00
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
2020-11-30 14:31:38 +00:00
# include <blind.h>
2019-10-16 12:28:42 -04:00
# include <pegins.h>
2019-01-09 02:06:29 -08:00
# include <psbt.h>
# include <util/strencodings.h>
2019-05-01 18:28:10 -07:00
# include <confidential_validation.h>
2019-01-09 02:06:29 -08:00
2019-03-01 00:25:10 -08:00
2019-01-09 02:06:29 -08:00
PartiallySignedTransaction : : PartiallySignedTransaction ( const CMutableTransaction & tx ) : tx ( tx )
{
inputs . resize ( tx . vin . size ( ) ) ;
outputs . resize ( tx . vout . size ( ) ) ;
}
bool PartiallySignedTransaction : : IsNull ( ) const
{
return ! tx & & inputs . empty ( ) & & outputs . empty ( ) & & unknown . empty ( ) ;
}
2019-01-29 22:51:56 -08:00
bool PartiallySignedTransaction : : Merge ( const PartiallySignedTransaction & psbt )
2019-01-09 02:06:29 -08:00
{
2019-01-29 22:51:56 -08:00
// Prohibited to merge two PSBTs over different transactions
if ( tx - > GetHash ( ) ! = psbt . tx - > GetHash ( ) ) {
return false ;
}
2019-01-09 02:06:29 -08:00
for ( unsigned int i = 0 ; i < inputs . size ( ) ; + + i ) {
inputs [ i ] . Merge ( psbt . inputs [ i ] ) ;
}
for ( unsigned int i = 0 ; i < outputs . size ( ) ; + + i ) {
outputs [ i ] . Merge ( psbt . outputs [ i ] ) ;
}
unknown . insert ( psbt . unknown . begin ( ) , psbt . unknown . end ( ) ) ;
2019-01-29 22:51:56 -08:00
return true ;
2019-01-09 02:06:29 -08:00
}
2018-07-20 18:24:16 -07:00
bool PartiallySignedTransaction : : AddInput ( const CTxIn & txin , PSBTInput & psbtin )
{
if ( std : : find ( tx - > vin . begin ( ) , tx - > vin . end ( ) , txin ) ! = tx - > vin . end ( ) ) {
return false ;
}
tx - > vin . push_back ( txin ) ;
psbtin . partial_sigs . clear ( ) ;
psbtin . final_script_sig . clear ( ) ;
psbtin . final_script_witness . SetNull ( ) ;
inputs . push_back ( psbtin ) ;
return true ;
}
bool PartiallySignedTransaction : : AddOutput ( const CTxOut & txout , const PSBTOutput & psbtout )
{
tx - > vout . push_back ( txout ) ;
outputs . push_back ( psbtout ) ;
return true ;
}
2018-07-31 17:56:47 -07:00
bool PartiallySignedTransaction : : GetInputUTXO ( CTxOut & utxo , int input_index ) const
{
PSBTInput input = inputs [ input_index ] ;
2019-10-15 17:26:46 -04:00
uint32_t prevout_index = tx - > vin [ input_index ] . prevout . n ;
2018-07-31 17:56:47 -07:00
if ( input . non_witness_utxo ) {
2019-10-15 17:26:46 -04:00
if ( prevout_index > = input . non_witness_utxo - > vout . size ( ) ) {
return false ;
}
2018-07-31 17:56:47 -07:00
utxo = input . non_witness_utxo - > vout [ prevout_index ] ;
} else if ( ! input . witness_utxo . IsNull ( ) ) {
utxo = input . witness_utxo ;
} else {
return false ;
}
return true ;
}
2019-01-09 02:06:29 -08:00
bool PSBTInput : : IsNull ( ) const
{
return ! non_witness_utxo & & witness_utxo . IsNull ( ) & & partial_sigs . empty ( ) & & unknown . empty ( ) & & hd_keypaths . empty ( ) & & redeem_script . empty ( ) & & witness_script . empty ( ) ;
}
void PSBTInput : : FillSignatureData ( SignatureData & sigdata ) const
{
if ( ! final_script_sig . empty ( ) ) {
sigdata . scriptSig = final_script_sig ;
sigdata . complete = true ;
}
if ( ! final_script_witness . IsNull ( ) ) {
sigdata . scriptWitness = final_script_witness ;
sigdata . complete = true ;
}
if ( sigdata . complete ) {
return ;
}
sigdata . signatures . insert ( partial_sigs . begin ( ) , partial_sigs . end ( ) ) ;
if ( ! redeem_script . empty ( ) ) {
sigdata . redeem_script = redeem_script ;
}
if ( ! witness_script . empty ( ) ) {
sigdata . witness_script = witness_script ;
}
for ( const auto & key_pair : hd_keypaths ) {
sigdata . misc_pubkeys . emplace ( key_pair . first . GetID ( ) , key_pair ) ;
}
}
void PSBTInput : : FromSignatureData ( const SignatureData & sigdata )
{
if ( sigdata . complete ) {
partial_sigs . clear ( ) ;
hd_keypaths . clear ( ) ;
redeem_script . clear ( ) ;
witness_script . clear ( ) ;
if ( ! sigdata . scriptSig . empty ( ) ) {
final_script_sig = sigdata . scriptSig ;
}
if ( ! sigdata . scriptWitness . IsNull ( ) ) {
final_script_witness = sigdata . scriptWitness ;
}
return ;
}
partial_sigs . insert ( sigdata . signatures . begin ( ) , sigdata . signatures . end ( ) ) ;
if ( redeem_script . empty ( ) & & ! sigdata . redeem_script . empty ( ) ) {
redeem_script = sigdata . redeem_script ;
}
if ( witness_script . empty ( ) & & ! sigdata . witness_script . empty ( ) ) {
witness_script = sigdata . witness_script ;
}
for ( const auto & entry : sigdata . misc_pubkeys ) {
hd_keypaths . emplace ( entry . second ) ;
}
}
void PSBTInput : : Merge ( const PSBTInput & input )
{
if ( ! non_witness_utxo & & input . non_witness_utxo ) non_witness_utxo = input . non_witness_utxo ;
if ( witness_utxo . IsNull ( ) & & ! input . witness_utxo . IsNull ( ) ) {
2020-06-04 23:43:43 -04:00
// TODO: For segwit v1, we will want to clear out the non-witness utxo when setting a witness one. For v0 and non-segwit, this is not safe
2019-01-09 02:06:29 -08:00
witness_utxo = input . witness_utxo ;
}
partial_sigs . insert ( input . partial_sigs . begin ( ) , input . partial_sigs . end ( ) ) ;
hd_keypaths . insert ( input . hd_keypaths . begin ( ) , input . hd_keypaths . end ( ) ) ;
unknown . insert ( input . unknown . begin ( ) , input . unknown . end ( ) ) ;
if ( redeem_script . empty ( ) & & ! input . redeem_script . empty ( ) ) redeem_script = input . redeem_script ;
if ( witness_script . empty ( ) & & ! input . witness_script . empty ( ) ) witness_script = input . witness_script ;
if ( final_script_sig . empty ( ) & & ! input . final_script_sig . empty ( ) ) final_script_sig = input . final_script_sig ;
if ( final_script_witness . IsNull ( ) & & ! input . final_script_witness . IsNull ( ) ) final_script_witness = input . final_script_witness ;
2019-05-01 18:28:10 -07:00
if ( ! value & & input . value ) value = input . value ;
if ( value_blinding_factor . IsNull ( ) & & ! input . value_blinding_factor . IsNull ( ) ) value_blinding_factor = input . value_blinding_factor ;
if ( asset . IsNull ( ) & & ! input . asset . IsNull ( ) ) asset = input . asset ;
if ( asset_blinding_factor . IsNull ( ) & & ! input . asset_blinding_factor . IsNull ( ) ) asset_blinding_factor = input . asset_blinding_factor ;
2019-10-16 13:56:30 -04:00
if ( peg_in_tx . which ( ) = = 0 & & peg_in_tx . which ( ) > 0 ) peg_in_tx = input . peg_in_tx ;
if ( txout_proof . which ( ) = = 0 & & peg_in_tx . which ( ) > 0 ) txout_proof = input . txout_proof ;
if ( claim_script . empty ( ) & & ! input . claim_script . empty ( ) ) claim_script = input . claim_script ;
if ( genesis_hash . IsNull ( ) & & ! input . genesis_hash . IsNull ( ) ) genesis_hash = input . genesis_hash ;
2019-01-09 02:06:29 -08:00
}
void PSBTOutput : : FillSignatureData ( SignatureData & sigdata ) const
{
if ( ! redeem_script . empty ( ) ) {
sigdata . redeem_script = redeem_script ;
}
if ( ! witness_script . empty ( ) ) {
sigdata . witness_script = witness_script ;
}
for ( const auto & key_pair : hd_keypaths ) {
sigdata . misc_pubkeys . emplace ( key_pair . first . GetID ( ) , key_pair ) ;
}
}
void PSBTOutput : : FromSignatureData ( const SignatureData & sigdata )
{
if ( redeem_script . empty ( ) & & ! sigdata . redeem_script . empty ( ) ) {
redeem_script = sigdata . redeem_script ;
}
if ( witness_script . empty ( ) & & ! sigdata . witness_script . empty ( ) ) {
witness_script = sigdata . witness_script ;
}
for ( const auto & entry : sigdata . misc_pubkeys ) {
hd_keypaths . emplace ( entry . second ) ;
}
}
bool PSBTOutput : : IsNull ( ) const
{
return redeem_script . empty ( ) & & witness_script . empty ( ) & & hd_keypaths . empty ( ) & & unknown . empty ( ) ;
}
void PSBTOutput : : Merge ( const PSBTOutput & output )
{
hd_keypaths . insert ( output . hd_keypaths . begin ( ) , output . hd_keypaths . end ( ) ) ;
unknown . insert ( output . unknown . begin ( ) , output . unknown . end ( ) ) ;
if ( redeem_script . empty ( ) & & ! output . redeem_script . empty ( ) ) redeem_script = output . redeem_script ;
if ( witness_script . empty ( ) & & ! output . witness_script . empty ( ) ) witness_script = output . witness_script ;
2019-05-01 18:28:10 -07:00
if ( ! blinding_pubkey . IsValid ( ) & & output . blinding_pubkey . IsValid ( ) ) blinding_pubkey = output . blinding_pubkey ;
if ( value_commitment . IsNull ( ) & & ! output . value_commitment . IsNull ( ) ) value_commitment = output . value_commitment ;
if ( value_blinding_factor . IsNull ( ) & & ! output . value_blinding_factor . IsNull ( ) ) value_blinding_factor = output . value_blinding_factor ;
if ( asset_commitment . IsNull ( ) & & ! output . asset_commitment . IsNull ( ) ) asset_commitment = output . asset_commitment ;
if ( asset_blinding_factor . IsNull ( ) & & ! output . asset_blinding_factor . IsNull ( ) ) asset_blinding_factor = output . asset_blinding_factor ;
if ( nonce_commitment . IsNull ( ) & & ! output . nonce_commitment . IsNull ( ) ) nonce_commitment = output . nonce_commitment ;
if ( range_proof . empty ( ) & & ! output . range_proof . empty ( ) ) range_proof = output . range_proof ;
if ( surjection_proof . empty ( ) & & ! output . surjection_proof . empty ( ) ) surjection_proof = output . surjection_proof ;
2019-01-09 02:06:29 -08:00
}
2019-03-01 00:25:10 -08:00
bool PSBTInputSigned ( const PSBTInput & input )
2019-01-09 02:06:29 -08:00
{
return ! input . final_script_sig . empty ( ) | | ! input . final_script_witness . IsNull ( ) ;
}
2020-05-29 01:25:04 -07:00
size_t CountPSBTUnsignedInputs ( const PartiallySignedTransaction & psbt ) {
size_t count = 0 ;
for ( const auto & input : psbt . inputs ) {
if ( ! PSBTInputSigned ( input ) ) {
count + + ;
}
}
return count ;
}
2019-02-16 15:49:36 -08:00
void UpdatePSBTOutput ( const SigningProvider & provider , PartiallySignedTransaction & psbt , int index )
{
const CTxOut & out = psbt . tx - > vout . at ( index ) ;
PSBTOutput & psbt_out = psbt . outputs . at ( index ) ;
// Fill a SignatureData with output info
SignatureData sigdata ;
psbt_out . FillSignatureData ( sigdata ) ;
// Construct a would-be spend of this output, to update sigdata with.
// Note that ProduceSignature is used to fill in metadata (not actual signatures),
// so provider does not need to provide any private keys (it can be a HidingSigningProvider).
MutableTransactionSignatureCreator creator ( psbt . tx . get_ptr ( ) , /* index */ 0 , out . nValue , SIGHASH_ALL ) ;
ProduceSignature ( provider , creator , out . scriptPubKey , sigdata ) ;
// Put redeem_script, witness_script, key paths, into PSBTOutput.
psbt_out . FromSignatureData ( sigdata ) ;
}
2018-07-31 17:57:15 -07:00
bool SignPSBTInput ( const SigningProvider & provider , PartiallySignedTransaction & psbt , int index , int sighash , SignatureData * out_sigdata , bool use_dummy )
2019-01-09 02:06:29 -08:00
{
PSBTInput & input = psbt . inputs . at ( index ) ;
const CMutableTransaction & tx = * psbt . tx ;
if ( PSBTInputSigned ( input ) ) {
return true ;
}
// Fill SignatureData with input info
SignatureData sigdata ;
input . FillSignatureData ( sigdata ) ;
2019-05-01 18:28:10 -07:00
// Get UTXO for this input
2019-01-09 02:06:29 -08:00
bool require_witness_sig = false ;
CTxOut utxo ;
if ( input . non_witness_utxo ) {
// If we're taking our information from a non-witness UTXO, verify that it matches the prevout.
COutPoint prevout = tx . vin [ index ] . prevout ;
2019-10-15 17:26:46 -04:00
if ( prevout . n > = input . non_witness_utxo - > vout . size ( ) ) {
return false ;
}
2019-01-09 02:06:29 -08:00
if ( input . non_witness_utxo - > GetHash ( ) ! = prevout . hash ) {
return false ;
}
utxo = input . non_witness_utxo - > vout [ prevout . n ] ;
} else if ( ! input . witness_utxo . IsNull ( ) ) {
utxo = input . witness_utxo ;
// When we're taking our information from a witness UTXO, we can't verify it is actually data from
// the output being spent. This is safe in case a witness signature is produced (which includes this
// information directly in the hash), but not for non-witness signatures. Remember that we require
// a witness signature in this situation.
require_witness_sig = true ;
} else {
return false ;
}
sigdata . witness = false ;
2018-07-31 17:57:15 -07:00
bool sig_complete ;
if ( use_dummy ) {
sig_complete = ProduceSignature ( provider , DUMMY_SIGNATURE_CREATOR , utxo . scriptPubKey , sigdata ) ;
} else {
MutableTransactionSignatureCreator creator ( & tx , index , utxo . nValue , sighash ) ;
sig_complete = ProduceSignature ( provider , creator , utxo . scriptPubKey , sigdata ) ;
}
2019-01-09 02:06:29 -08:00
// Verify that a witness signature was produced in case one was required.
if ( require_witness_sig & & ! sigdata . witness ) return false ;
input . FromSignatureData ( sigdata ) ;
2020-06-04 23:43:43 -04:00
// If we have a witness signature, put a witness UTXO.
// TODO: For segwit v1, we should remove the non_witness_utxo
2019-01-09 02:06:29 -08:00
if ( sigdata . witness ) {
input . witness_utxo = utxo ;
2020-06-04 23:43:43 -04:00
// input.non_witness_utxo = nullptr;
2019-01-09 02:06:29 -08:00
}
2018-07-31 17:57:15 -07:00
// Fill in the missing info
if ( out_sigdata ) {
out_sigdata - > missing_pubkeys = sigdata . missing_pubkeys ;
out_sigdata - > missing_sigs = sigdata . missing_sigs ;
out_sigdata - > missing_redeem_script = sigdata . missing_redeem_script ;
out_sigdata - > missing_witness_script = sigdata . missing_witness_script ;
}
2019-01-09 02:06:29 -08:00
return sig_complete ;
}
2019-01-09 03:08:32 -08:00
bool FinalizePSBT ( PartiallySignedTransaction & psbtx )
{
// Finalize input signatures -- in case we have partial signatures that add up to a complete
// signature, but have not combined them yet (e.g. because the combiner that created this
// PartiallySignedTransaction did not understand them), this will combine them into a final
// script.
bool complete = true ;
for ( unsigned int i = 0 ; i < psbtx . tx - > vin . size ( ) ; + + i ) {
complete & = SignPSBTInput ( DUMMY_SIGNING_PROVIDER , psbtx , i , SIGHASH_ALL ) ;
}
return complete ;
}
bool FinalizeAndExtractPSBT ( PartiallySignedTransaction & psbtx , CMutableTransaction & result )
{
// It's not safe to extract a PSBT that isn't finalized, and there's no easy way to check
// whether a PSBT is finalized without finalizing it, so we just do this.
if ( ! FinalizePSBT ( psbtx ) ) {
return false ;
}
result = * psbtx . tx ;
2019-05-01 18:28:10 -07:00
result . witness . vtxinwit . resize ( result . vin . size ( ) ) ;
2019-01-09 03:08:32 -08:00
for ( unsigned int i = 0 ; i < result . vin . size ( ) ; + + i ) {
result . vin [ i ] . scriptSig = psbtx . inputs [ i ] . final_script_sig ;
2019-05-17 16:43:38 +01:00
result . witness . vtxinwit [ i ] . scriptWitness = psbtx . inputs [ i ] . final_script_witness ;
2019-10-16 12:28:42 -04:00
PSBTInput & input = psbtx . inputs [ i ] ;
if ( input . value & & input . peg_in_tx . which ( ) ! = 0 & & input . txout_proof . which ( ) ! = 0 & & ! input . claim_script . empty ( ) & & ! input . genesis_hash . IsNull ( ) ) {
CScriptWitness pegin_witness ;
if ( Params ( ) . GetConsensus ( ) . ParentChainHasPow ( ) ) {
const Sidechain : : Bitcoin : : CTransactionRef & btc_peg_in_tx = boost : : get < Sidechain : : Bitcoin : : CTransactionRef > ( input . peg_in_tx ) ;
const Sidechain : : Bitcoin : : CMerkleBlock & btc_txout_proof = boost : : get < Sidechain : : Bitcoin : : CMerkleBlock > ( input . txout_proof ) ;
pegin_witness = CreatePeginWitness ( * input . value , input . asset , input . genesis_hash , input . claim_script , btc_peg_in_tx , btc_txout_proof ) ;
} else {
const CTransactionRef & elem_peg_in_tx = boost : : get < CTransactionRef > ( input . peg_in_tx ) ;
const CMerkleBlock & elem_txout_proof = boost : : get < CMerkleBlock > ( input . txout_proof ) ;
pegin_witness = CreatePeginWitness ( * input . value , input . asset , input . genesis_hash , input . claim_script , elem_peg_in_tx , elem_txout_proof ) ;
}
result . vin [ i ] . m_is_pegin = true ;
result . witness . vtxinwit [ i ] . m_pegin_witness = pegin_witness ;
}
2019-01-09 03:08:32 -08:00
}
2019-05-01 18:28:10 -07:00
result . witness . vtxoutwit . resize ( result . vout . size ( ) ) ;
for ( unsigned int i = 0 ; i < result . vout . size ( ) ; + + i ) {
PSBTOutput & output = psbtx . outputs . at ( i ) ;
CTxOut & out = result . vout [ i ] ;
CTxOutWitness & outwit = result . witness . vtxoutwit [ i ] ;
if ( ! output . value_commitment . IsNull ( ) ) {
out . nValue = output . value_commitment ;
}
if ( ! output . asset_commitment . IsNull ( ) ) {
out . nAsset = output . asset_commitment ;
}
if ( ! output . nonce_commitment . IsNull ( ) ) {
out . nNonce = output . nonce_commitment ;
}
if ( ! output . range_proof . empty ( ) ) {
outwit . vchRangeproof = output . range_proof ;
}
if ( ! output . surjection_proof . empty ( ) ) {
outwit . vchSurjectionproof = output . surjection_proof ;
}
}
2019-01-09 03:08:32 -08:00
return true ;
}
2019-02-14 10:01:06 -05:00
TransactionError CombinePSBTs ( PartiallySignedTransaction & out , const std : : vector < PartiallySignedTransaction > & psbtxs )
2019-01-09 03:08:32 -08:00
{
out = psbtxs [ 0 ] ; // Copy the first one
// Merge
for ( auto it = std : : next ( psbtxs . begin ( ) ) ; it ! = psbtxs . end ( ) ; + + it ) {
if ( ! out . Merge ( * it ) ) {
2019-02-14 10:01:06 -05:00
return TransactionError : : PSBT_MISMATCH ;
2019-01-09 03:08:32 -08:00
}
}
2019-02-14 10:01:06 -05:00
return TransactionError : : OK ;
2019-01-09 03:08:32 -08:00
}
2019-03-05 18:55:40 -08:00
2019-03-01 00:25:10 -08:00
std : : string PSBTRoleName ( PSBTRole role ) {
switch ( role ) {
2019-11-19 14:35:14 -05:00
case PSBTRole : : CREATOR : return " creator " ;
2019-03-01 00:25:10 -08:00
case PSBTRole : : UPDATER : return " updater " ;
case PSBTRole : : SIGNER : return " signer " ;
case PSBTRole : : FINALIZER : return " finalizer " ;
case PSBTRole : : EXTRACTOR : return " extractor " ;
2020-10-28 00:54:12 +00:00
// no default case, so the compiler can warn about missing cases
2019-03-01 00:25:10 -08:00
}
2020-10-28 00:54:12 +00:00
assert ( false ) ;
2019-03-01 00:25:10 -08:00
}
2019-05-01 18:28:10 -07:00
std : : string EncodePSBT ( const PartiallySignedTransaction & psbt )
{
CDataStream ssTx ( SER_NETWORK , PROTOCOL_VERSION ) ;
ssTx < < psbt ;
2020-11-28 13:35:24 +00:00
return EncodeBase64 ( MakeUCharSpan ( ssTx ) ) ;
2019-05-01 18:28:10 -07:00
}
2019-03-05 18:55:40 -08:00
bool DecodeBase64PSBT ( PartiallySignedTransaction & psbt , const std : : string & base64_tx , std : : string & error )
{
bool invalid ;
std : : string tx_data = DecodeBase64 ( base64_tx , & invalid ) ;
if ( invalid ) {
error = " invalid base64 " ;
return false ;
}
return DecodeRawPSBT ( psbt , tx_data , error ) ;
}
bool DecodeRawPSBT ( PartiallySignedTransaction & psbt , const std : : string & tx_data , std : : string & error )
{
CDataStream ss_data ( tx_data . data ( ) , tx_data . data ( ) + tx_data . size ( ) , SER_NETWORK , PROTOCOL_VERSION ) ;
try {
ss_data > > psbt ;
if ( ! ss_data . empty ( ) ) {
error = " extra data after PSBT " ;
return false ;
}
} catch ( const std : : exception & e ) {
error = e . what ( ) ;
return false ;
}
return true ;
}
2020-11-30 14:31:38 +00:00
bool CheckPSBTBlinding ( const PartiallySignedTransaction & psbtx , std : : string & error ) {
// Plausibly, we may want a way to let the user continue anyway. However, we
// want to fail by default, to make it as hard as possible to do something
// really dangerous. And since this way of handling blinded PSBTs is going
// away "real soon now" in favor of a better one, no sense in trying too
// hard about it.
for ( size_t i = 0 ; i < psbtx . outputs . size ( ) ; + + i ) {
const PSBTOutput & output = psbtx . outputs [ i ] ;
const CTxOut & txo = psbtx . tx - > vout [ i ] ;
if ( txo . nValue . IsCommitment ( ) | | txo . nAsset . IsCommitment ( ) ) {
error = " PSBT's 'tx' field may not have pre-blinded outputs. " ;
return false ;
}
if ( ! output . value_commitment . IsCommitment ( ) & &
! output . asset_commitment . IsCommitment ( ) & &
output . value_blinding_factor . IsNull ( ) & &
output . asset_blinding_factor . IsNull ( ) ) {
// Nothing blinded, nothing to check.
continue ;
} else if ( ! output . value_commitment . IsCommitment ( ) | |
! output . asset_commitment . IsCommitment ( ) | |
output . value_blinding_factor . IsNull ( ) | |
output . asset_blinding_factor . IsNull ( ) ) {
// Something blinded, but not everything? That's not expected.
error = " PSBT has a partially-blinded output. Blinded outputs must be fully blinded. " ;
return false ;
}
if ( ! VerifyConfidentialPair ( output . value_commitment , output . asset_commitment , txo . nValue . GetAmount ( ) , txo . nAsset . GetAsset ( ) , output . value_blinding_factor , output . asset_blinding_factor ) ) {
error = " PSBT's 'tx' field output values do not match blinded output values (or are invalid in some way)! Either there is a bug, or the blinder is attacking you. " ;
return false ;
}
}
return true ;
}