diff --git a/src/script/sign.h b/src/script/sign.h --- a/src/script/sign.h +++ b/src/script/sign.h @@ -92,8 +92,10 @@ const CScript &scriptCode) const override; }; -/** A signature creator that just produces 72-byte empty signatures. */ +/** A signature creator that just produces 71-byte empty signatures. */ extern const BaseSignatureCreator &DUMMY_SIGNATURE_CREATOR; +/** A signature creator that just produces 72-byte empty signatures. */ +extern const BaseSignatureCreator &DUMMY_MAXIMUM_SIGNATURE_CREATOR; typedef std::pair> SigPair; diff --git a/src/script/sign.cpp b/src/script/sign.cpp --- a/src/script/sign.cpp +++ b/src/script/sign.cpp @@ -406,8 +406,13 @@ const DummySignatureChecker DUMMY_CHECKER; class DummySignatureCreator final : public BaseSignatureCreator { +private: + char m_r_len = 32; + char m_s_len = 32; + public: - DummySignatureCreator() {} + DummySignatureCreator(char r_len, char s_len) + : m_r_len(r_len), m_s_len(s_len) {} const BaseSignatureChecker &Checker() const override { return DUMMY_CHECKER; } @@ -415,16 +420,16 @@ std::vector &vchSig, const CKeyID &keyid, const CScript &scriptCode) const override { // Create a dummy signature that is a valid DER-encoding - vchSig.assign(71, '\000'); + vchSig.assign(m_r_len + m_s_len + 7, '\000'); vchSig[0] = 0x30; - vchSig[1] = 68; + vchSig[1] = m_r_len + m_s_len + 4; vchSig[2] = 0x02; - vchSig[3] = 32; + vchSig[3] = m_r_len; vchSig[4] = 0x01; - vchSig[4 + 32] = 0x02; - vchSig[5 + 32] = 32; - vchSig[6 + 32] = 0x01; - vchSig[6 + 32 + 32] = SIGHASH_ALL | SIGHASH_FORKID; + vchSig[4 + m_r_len] = 0x02; + vchSig[5 + m_r_len] = m_s_len; + vchSig[6 + m_r_len] = 0x01; + vchSig[6 + m_r_len + m_s_len] = SIGHASH_ALL | SIGHASH_FORKID; return true; } }; @@ -441,7 +446,10 @@ } // namespace -const BaseSignatureCreator &DUMMY_SIGNATURE_CREATOR = DummySignatureCreator(); +const BaseSignatureCreator &DUMMY_SIGNATURE_CREATOR = + DummySignatureCreator(32, 32); +const BaseSignatureCreator &DUMMY_MAXIMUM_SIGNATURE_CREATOR = + DummySignatureCreator(33, 32); const SigningProvider &DUMMY_SIGNING_PROVIDER = SigningProvider(); bool PartiallySignedTransaction::IsNull() const { diff --git a/src/wallet/wallet.h b/src/wallet/wallet.h --- a/src/wallet/wallet.h +++ b/src/wallet/wallet.h @@ -272,8 +272,8 @@ }; // Get the marginal bytes of spending the specified output -int CalculateMaximumSignedInputSize(const CTxOut &txout, - const CWallet *pwallet); +int CalculateMaximumSignedInputSize(const CTxOut &txout, const CWallet *pwallet, + bool use_max_sig = false); /** * A transaction with a bunch of additional info that only the owner cares @@ -465,8 +465,9 @@ // Get the marginal bytes if spending the specified output from this // transaction - int GetSpendSize(unsigned int out) const { - return CalculateMaximumSignedInputSize(tx->vout[out], pwallet); + int GetSpendSize(unsigned int out, bool use_max_sig = false) const { + return CalculateMaximumSignedInputSize(tx->vout[out], pwallet, + use_max_sig); } void GetAmounts(std::list &listReceived, @@ -518,6 +519,13 @@ /** Whether we know how to spend this output, ignoring the lack of keys */ bool fSolvable; + /** + * Whether to use the maximum sized, 72 byte signature when calculating the + * size of the input spend. This should only be set when watch-only outputs + * are allowed. + */ + bool use_max_sig; + /** * Whether this output is considered safe to spend. Unconfirmed transactions * from outside keys are considered unsafe and will not be used to fund new @@ -526,7 +534,7 @@ bool fSafe; COutput(const CWalletTx *txIn, int iIn, int nDepthIn, bool fSpendableIn, - bool fSolvableIn, bool fSafeIn) { + bool fSolvableIn, bool fSafeIn, bool use_max_sig_in = false) { tx = txIn; i = iIn; nDepth = nDepthIn; @@ -534,10 +542,11 @@ fSolvable = fSolvableIn; fSafe = fSafeIn; nInputBytes = -1; + use_max_sig = use_max_sig_in; // If known and signable by the given wallet, compute nInputBytes // Failure will keep this value -1 if (fSpendable && tx) { - nInputBytes = tx->GetSpendSize(i); + nInputBytes = tx->GetSpendSize(i, use_max_sig); } } @@ -565,7 +574,9 @@ template inline void SerializationOp(Stream &s, Operation ser_action) { int nVersion = s.GetVersion(); - if (!(s.GetType() & SER_GETHASH)) READWRITE(nVersion); + if (!(s.GetType() & SER_GETHASH)) { + READWRITE(nVersion); + } READWRITE(vchPrivKey); READWRITE(nTimeCreated); READWRITE(nTimeExpires); @@ -1088,15 +1099,17 @@ std::list &entries); bool AddAccountingEntry(const CAccountingEntry &); bool AddAccountingEntry(const CAccountingEntry &, WalletBatch *batch); - bool DummySignTx(CMutableTransaction &txNew, - const std::set &txouts) const { + bool DummySignTx(CMutableTransaction &txNew, const std::set &txouts, + bool use_max_sig = false) const { std::vector v_txouts(txouts.size()); std::copy(txouts.begin(), txouts.end(), v_txouts.begin()); - return DummySignTx(txNew, v_txouts); + return DummySignTx(txNew, v_txouts, use_max_sig); } bool DummySignTx(CMutableTransaction &txNew, - const std::vector &txouts) const; - bool DummySignInput(CTxIn &tx_in, const CTxOut &txout) const; + const std::vector &txouts, + bool use_max_sig = false) const; + bool DummySignInput(CTxIn &tx_in, const CTxOut &txout, + bool use_max_sig = false) const; CFeeRate m_pay_tx_fee{DEFAULT_PAY_TX_FEE}; bool m_spend_zero_conf_change{DEFAULT_SPEND_ZEROCONF_CHANGE}; @@ -1419,9 +1432,11 @@ // NOTE: this requires that all inputs must be in mapWallet (eg the tx should // be IsAllFromMe). int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, - const CWallet *wallet); + const CWallet *wallet, + bool use_max_sig = false); int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, - const std::vector &txouts); + const std::vector &txouts, + bool use_max_sig = false); #endif // BITCOIN_WALLET_WALLET_H diff --git a/src/wallet/wallet.cpp b/src/wallet/wallet.cpp --- a/src/wallet/wallet.cpp +++ b/src/wallet/wallet.cpp @@ -1612,13 +1612,17 @@ } // Helper for producing a max-sized low-S low-R signature (eg 71 bytes) -bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout) const { +// or a max-sized low-S signature (e.g. 72 bytes) if use_max_sig is true +bool CWallet::DummySignInput(CTxIn &tx_in, const CTxOut &txout, + bool use_max_sig) const { // Fill in dummy signatures for fee calculation. const CScript &scriptPubKey = txout.scriptPubKey; SignatureData sigdata; - if (!ProduceSignature(*this, DUMMY_SIGNATURE_CREATOR, scriptPubKey, - sigdata)) { + if (!ProduceSignature(*this, + use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR + : DUMMY_SIGNATURE_CREATOR, + scriptPubKey, sigdata)) { return false; } @@ -1629,11 +1633,12 @@ // Helper for producing a bunch of max-sized low-S low-R signatures (eg 71 // bytes) bool CWallet::DummySignTx(CMutableTransaction &txNew, - const std::vector &txouts) const { + const std::vector &txouts, + bool use_max_sig) const { // Fill in dummy signatures for fee calculation. int nIn = 0; for (const auto &txout : txouts) { - if (!DummySignInput(txNew.vin[nIn], txout)) { + if (!DummySignInput(txNew.vin[nIn], txout, use_max_sig)) { return false; } @@ -1643,7 +1648,7 @@ } int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, - const CWallet *wallet) { + const CWallet *wallet, bool use_max_sig) { std::vector txouts; // Look up the inputs. We should have already checked that this transaction // IsAllFromMe(ISMINE_SPENDABLE), so every input should already be in our @@ -1656,15 +1661,16 @@ assert(input.prevout.GetN() < mi->second.tx->vout.size()); txouts.emplace_back(mi->second.tx->vout[input.prevout.GetN()]); } - return CalculateMaximumSignedTxSize(tx, wallet, txouts); + return CalculateMaximumSignedTxSize(tx, wallet, txouts, use_max_sig); } // txouts needs to be in the order of tx.vin int64_t CalculateMaximumSignedTxSize(const CTransaction &tx, const CWallet *wallet, - const std::vector &txouts) { + const std::vector &txouts, + bool use_max_sig) { CMutableTransaction txNew(tx); - if (!wallet->DummySignTx(txNew, txouts)) { + if (!wallet->DummySignTx(txNew, txouts, use_max_sig)) { // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) // implies that we can sign for every input. return -1; @@ -1672,11 +1678,11 @@ return GetVirtualTransactionSize(CTransaction(txNew)); } -int CalculateMaximumSignedInputSize(const CTxOut &txout, - const CWallet *wallet) { +int CalculateMaximumSignedInputSize(const CTxOut &txout, const CWallet *wallet, + bool use_max_sig) { CMutableTransaction txn; txn.vin.push_back(CTxIn(COutPoint())); - if (!wallet->DummySignInput(txn.vin[0], txout)) { + if (!wallet->DummySignInput(txn.vin[0], txout, use_max_sig)) { // This should never happen, because IsAllFromMe(ISMINE_SPENDABLE) // implies that we can sign for every input. return -1; @@ -2501,7 +2507,8 @@ ISMINE_NO; vCoins.push_back( - COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx)); + COutput(pcoin, i, nDepth, fSpendableIn, fSolvableIn, safeTx, + (coinControl && coinControl->fAllowWatchOnly))); // Checks the sum amount of all UTXO's. if (nMinimumSumAmount != MAX_MONEY) { @@ -3145,7 +3152,8 @@ } CTransaction txNewConst(txNew); - int nBytes = CalculateMaximumSignedTxSize(txNewConst, this); + int nBytes = CalculateMaximumSignedTxSize( + txNewConst, this, coinControl.fAllowWatchOnly); if (nBytes < 0) { strFailReason = _("Signing transaction failed"); return false;