Skip to content

Commit

Permalink
CreateTransaction: Assume minimum p2sh-p2wpkh spend size for unknown …
Browse files Browse the repository at this point in the history
…change
  • Loading branch information
instagibbs committed Nov 12, 2018
1 parent b06483c commit 0fb2e69
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 1 deletion.
44 changes: 44 additions & 0 deletions src/wallet/test/wallet_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <validation.h>
#include <wallet/coincontrol.h>
#include <wallet/test/wallet_test_fixture.h>
#include <policy/policy.h>

#include <boost/test/unit_test.hpp>
#include <univalue.h>
Expand Down Expand Up @@ -394,4 +395,47 @@ BOOST_FIXTURE_TEST_CASE(wallet_disableprivkeys, TestChain100Setup)
BOOST_CHECK(!wallet->GetKeyFromPool(pubkey, false));
}

// Explicit calculation which is used to test the wallet constant
// We get the same virtual size due to rounding(weight/4) for both use_max_sig values
static size_t CalculateNestedKeyhashInputSize(bool use_max_sig)
{
// Generate ephemeral valid pubkey
CKey key;
key.MakeNewKey(true);
CPubKey pubkey = key.GetPubKey();

// Generate pubkey hash
uint160 key_hash(Hash160(pubkey.begin(), pubkey.end()));

// Create inner-script to enter into keystore. Key hash can't be 0...
CScript inner_script = CScript() << OP_0 << std::vector<unsigned char>(key_hash.begin(), key_hash.end());

// Create outer P2SH script for the output
uint160 script_id(Hash160(inner_script.begin(), inner_script.end()));
CScript script_pubkey = CScript() << OP_HASH160 << std::vector<unsigned char>(script_id.begin(), script_id.end()) << OP_EQUAL;

// Add inner-script to key store and key to watchonly
CBasicKeyStore keystore;
keystore.AddCScript(inner_script);
keystore.AddKeyPubKey(key, pubkey);

// Fill in dummy signatures for fee calculation.
SignatureData sig_data;

if (!ProduceSignature(keystore, use_max_sig ? DUMMY_MAXIMUM_SIGNATURE_CREATOR : DUMMY_SIGNATURE_CREATOR, script_pubkey, sig_data)) {
// We're hand-feeding it correct arguments; shouldn't happen
assert(false);
}

CTxIn tx_in;
UpdateInput(tx_in, sig_data);
return (size_t)GetVirtualTransactionInputSize(tx_in);
}

BOOST_FIXTURE_TEST_CASE(dummy_input_size_test, TestChain100Setup)
{
BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(false), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
BOOST_CHECK_EQUAL(CalculateNestedKeyhashInputSize(true), DUMMY_NESTED_P2WPKH_INPUT_SIZE);
}

BOOST_AUTO_TEST_SUITE_END()
9 changes: 8 additions & 1 deletion src/wallet/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2753,7 +2753,14 @@ bool CWallet::CreateTransaction(interfaces::Chain::Lock& locked_chain, const std
if (pick_new_inputs) {
nValueIn = 0;
setCoins.clear();
coin_selection_params.change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
int change_spend_size = CalculateMaximumSignedInputSize(change_prototype_txout, this);
// If the wallet doesn't know how to sign change output, assume p2sh-p2wpkh
// as lower-bound to allow BnB to do it's thing
if (change_spend_size == -1) {
coin_selection_params.change_spend_size = DUMMY_NESTED_P2WPKH_INPUT_SIZE;
} else {
coin_selection_params.change_spend_size = (size_t)change_spend_size;
}
coin_selection_params.effective_fee = nFeeRateNeeded;
if (!SelectCoins(vAvailableCoins, nValueToSelect, setCoins, nValueIn, coin_control, coin_selection_params, bnb_used))
{
Expand Down
3 changes: 3 additions & 0 deletions src/wallet/wallet.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ static const bool DEFAULT_WALLET_RBF = false;
static const bool DEFAULT_WALLETBROADCAST = true;
static const bool DEFAULT_DISABLE_WALLET = false;

//! Pre-calculated constants for input size estimation in *virtual size*
static constexpr size_t DUMMY_NESTED_P2WPKH_INPUT_SIZE = 91;

class CBlockIndex;
class CCoinControl;
class COutput;
Expand Down
4 changes: 4 additions & 0 deletions test/functional/rpc_psbt.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,10 @@ def run_test(self):
assert tx_in["sequence"] > MAX_BIP125_RBF_SEQUENCE
assert_equal(decoded_psbt["tx"]["locktime"], 0)

# Make sure change address wallet does not have P2SH innerscript access to results in success
# when attempting BnB coin selection
self.nodes[0].walletcreatefundedpsbt([], [{self.nodes[2].getnewaddress():unspent["amount"]+1}], block_height+2, {"changeAddress":self.nodes[1].getnewaddress()}, False)

# Regression test for 14473 (mishandling of already-signed witness transaction):
psbtx_info = self.nodes[0].walletcreatefundedpsbt([{"txid":unspent["txid"], "vout":unspent["vout"]}], [{self.nodes[2].getnewaddress():unspent["amount"]+1}])
complete_psbt = self.nodes[0].walletprocesspsbt(psbtx_info["psbt"])
Expand Down

0 comments on commit 0fb2e69

Please sign in to comment.