diff --git a/src/bitcoin-tx.cpp b/src/bitcoin-tx.cpp index 51d1f61955..1cf77af604 100644 --- a/src/bitcoin-tx.cpp +++ b/src/bitcoin-tx.cpp @@ -282,7 +282,7 @@ static void MutateTxAddOutAddr(CMutableTransaction& tx, const std::string& strIn CTxOut txout(asset, value, scriptPubKey); if (addr.IsBlinded()) { CPubKey pubkey = addr.GetBlindingKey(); - txout.vchNonceCommitment = std::vector(pubkey.begin(), pubkey.end()); + txout.nNonce.vchCommitment = std::vector(pubkey.begin(), pubkey.end()); } tx.vout.push_back(txout); } @@ -470,10 +470,10 @@ static void MutateTxBlind(CMutableTransaction& tx, const std::string& strInput) for (size_t nOut = 0; nOut < tx.vout.size(); nOut++) { if (!tx.vout[nOut].nValue.IsExplicit()) throw std::runtime_error("Invalid parameter: transaction outputs must be unblinded"); - if (tx.vout[nOut].vchNonceCommitment.size() == 0) { + if (tx.vout[nOut].nNonce.vchCommitment.size() == 0) { output_pubkeys.push_back(CPubKey()); } else { - CPubKey pubkey(tx.vout[nOut].vchNonceCommitment); + CPubKey pubkey(tx.vout[nOut].nNonce.vchCommitment); if (!pubkey.IsValid()) { throw std::runtime_error("Invalid parameter: invalid confidentiality public key given"); } diff --git a/src/blind.cpp b/src/blind.cpp index fb5f712bec..367e6566ea 100644 --- a/src/blind.cpp +++ b/src/blind.cpp @@ -39,7 +39,7 @@ bool UnblindOutput(const CKey &key, const CTxOut& txout, CAmount& amount_out, ui if (!key.IsValid()) { return false; } - CPubKey ephemeral_key(txout.vchNonceCommitment); + CPubKey ephemeral_key(txout.nNonce.vchCommitment); if (!ephemeral_key.IsValid()) { return false; } @@ -217,8 +217,8 @@ int BlindOutputs(std::vector& input_blinding_factors, const std::vecto CKey ephemeral_key; ephemeral_key.MakeNewKey(true); CPubKey ephemeral_pubkey = ephemeral_key.GetPubKey(); - out.vchNonceCommitment.resize(33); - memcpy(&out.vchNonceCommitment[0], &ephemeral_pubkey[0], 33); + out.nNonce.vchCommitment.resize(33); + memcpy(&out.nNonce.vchCommitment[0], &ephemeral_pubkey[0], 33); // Generate nonce uint256 nonce = ephemeral_key.ECDH(output_pubkeys[nOut]); CSHA256().Write(nonce.begin(), 32).Finalize(nonce.begin()); diff --git a/src/primitives/transaction.h b/src/primitives/transaction.h index af395032e4..00d59ac6f2 100644 --- a/src/primitives/transaction.h +++ b/src/primitives/transaction.h @@ -133,6 +133,16 @@ public: void SetToAmount(CAmount nAmount); }; +/** + * An 33-byte data field that typically is used to convey to the + * recipient the ECDH ephemeral key (an EC point) for deriving the + * transaction output blinding factor. */ +class CConfidentialNonce : public CConfidentialCommitment<33, 2, 3> +{ +public: + CConfidentialNonce() { SetNull(); } +}; + /** An output of a transaction. It contains the public key that the next input * must be able to sign with to claim it. */ @@ -143,7 +153,7 @@ public: std::vector vchSurjectionproof; CConfidentialValue nValue; std::vector vchRangeproof; - std::vector vchNonceCommitment; + CConfidentialNonce nNonce; CScript scriptPubKey; // FIXME: Inventory the places this constructor is called, and make sure @@ -175,13 +185,13 @@ public: vchSurjectionproof.clear(); nValue.SetNull(); vchRangeproof.clear(); - vchNonceCommitment.clear(); + nNonce.SetNull(); scriptPubKey.clear(); } bool IsNull() const { - return nAsset.IsNull() && vchSurjectionproof.empty() && nValue.IsNull() && vchRangeproof.empty() && vchNonceCommitment.empty() && scriptPubKey.empty(); + return nAsset.IsNull() && vchSurjectionproof.empty() && nValue.IsNull() && vchRangeproof.empty() && nNonce.IsNull() && scriptPubKey.empty(); } CAmount GetDustThreshold(const CFeeRate &minRelayTxFee) const @@ -243,7 +253,7 @@ public: a.vchSurjectionproof == b.vchSurjectionproof && a.nValue == b.nValue && a.vchRangeproof == b.vchRangeproof && - a.vchNonceCommitment == b.vchNonceCommitment && + a.nNonce == b.nNonce && a.scriptPubKey == b.scriptPubKey); } @@ -507,20 +517,20 @@ public: ADD_SERIALIZE_METHODS; bool IsNull() const { - return ref.vchSurjectionproof.empty() && ref.vchRangeproof.empty() && ref.vchNonceCommitment.empty(); + return ref.vchSurjectionproof.empty() && ref.vchRangeproof.empty() && ref.nNonce.IsNull(); } template inline void SerializationOp(Stream& s, Operation ser_action) { READWRITE(ref.vchSurjectionproof); READWRITE(ref.vchRangeproof); - READWRITE(ref.vchNonceCommitment); + READWRITE(ref.nNonce.vchCommitment); } void SetNull() { std::vector().swap(ref.vchSurjectionproof); std::vector().swap(ref.vchRangeproof); - std::vector().swap(ref.vchNonceCommitment); + std::vector().swap(ref.nNonce.vchCommitment); } }; diff --git a/src/rpc/rawtransaction.cpp b/src/rpc/rawtransaction.cpp index e09cbbd2d0..bc9c420c30 100644 --- a/src/rpc/rawtransaction.cpp +++ b/src/rpc/rawtransaction.cpp @@ -559,7 +559,7 @@ UniValue createrawtransaction(const JSONRPCRequest& request) CPubKey confidentiality_pubkey = address.GetBlindingKey(); if (!confidentiality_pubkey.IsValid()) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given")); - out.vchNonceCommitment = std::vector(confidentiality_pubkey.begin(), confidentiality_pubkey.end()); + out.nNonce.vchCommitment = std::vector(confidentiality_pubkey.begin(), confidentiality_pubkey.end()); } rawTx.vout.push_back(out); } @@ -600,13 +600,13 @@ void FillOutputBlinds(const CMutableTransaction& tx, bool fUseWallet, std::vecto #endif if (!fUseWallet) throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: transaction outputs must be unblinded")); - } else if (tx.vout[nOut].vchNonceCommitment.size() == 0) { + } else if (tx.vout[nOut].nNonce.vchCommitment.size() == 0) { output_pubkeys.push_back(CPubKey()); output_value_blinds.push_back(uint256()); output_asset_blinds.push_back(uint256()); output_assets.push_back(CAsset()); } else { - CPubKey pubkey(tx.vout[nOut].vchNonceCommitment); + CPubKey pubkey(tx.vout[nOut].nNonce.vchCommitment); if (!pubkey.IsValid()) { throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter: invalid confidentiality public key given")); } @@ -1245,7 +1245,7 @@ UniValue sendrawtransaction(const JSONRPCRequest& request) if (!fOverrideBlindable) { for (unsigned i = 0; i < tx->vout.size(); i++) { const CTxOut& txout = tx->vout[i]; - if (txout.nValue.IsExplicit() && txout.vchNonceCommitment.size() != 0) + if (txout.nValue.IsExplicit() && txout.nNonce.vchCommitment.size() != 0) throw JSONRPCError(RPC_TRANSACTION_ERROR, strprintf("Output %u is unblinded, but has blinding pubkey attached, please use [raw]blindrawtransaction", i)); } } diff --git a/src/validation.cpp b/src/validation.cpp index b3edf033e4..2c000e9b55 100644 --- a/src/validation.cpp +++ b/src/validation.cpp @@ -724,7 +724,9 @@ bool VerifyAmounts(const CCoinsViewCache& cache, const CTransaction& tx, std::ve const CConfidentialAsset& asset = tx.vout[i].nAsset; assert(val.vchCommitment.size() == CConfidentialValue::nCommittedSize || val.vchCommitment.size() == CConfidentialValue::nExplicitSize); - if (tx.vout[i].vchNonceCommitment.size() > 33 || tx.vout[i].vchRangeproof.size() > 5000) + if (!tx.vout[i].nNonce.IsValid()) + return false; + if (tx.vout[i].vchRangeproof.size() > 5000) return false; if (asset.IsExplicit()) { diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp index e8603af8f1..370bb0c7fd 100644 --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -2558,7 +2558,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, bool ov strFailReason = _("Pre-funded amounts must be non-blinded"); return false; } - CRecipient recipient = {txOut.scriptPubKey, txOut.nValue.GetAmount(), txOut.nAsset.GetAsset(), CPubKey(txOut.vchNonceCommitment), false}; + CRecipient recipient = {txOut.scriptPubKey, txOut.nValue.GetAmount(), txOut.nAsset.GetAsset(), CPubKey(txOut.nNonce.vchCommitment), false}; vecSend.push_back(recipient); if (setAssets.count(txOut.nAsset.GetAsset()) == 0) {