Skip to content

Commit 979150b

Browse files
committed
Merge #12729: Get rid of ambiguous OutputType::NONE value
1e46d8a Get rid of ambiguous OutputType::NONE value (Russell Yanofsky) Pull request description: Based on suggestion by @sipa bitcoin/bitcoin#12119 (comment) After #12119, the NONE output type was overloaded to refer to either an output type that couldn't be parsed, or to an automatic change output mode. This change drops the NONE enum and uses a simple bool to indicate parse failure, and a new CHANGE_AUTO enum to refer the change output type. This change is almost a pure refactoring except it makes RPCs reject empty string ("") address types instead of treating them like they were unset. This simplifies the parsing code a little bit and could prevent RPC usage mistakes. It's noted in the release notes. Follows up #12408 by @MarcoFalke Followups for future PRs: - [ ] Add explicit support for specifying "auto" in `ParseOutputType` as suggested by promag and sipa: bitcoin/bitcoin#12729 (comment) and bitcoin/bitcoin#12729 (comment) - [ ] Add wallet `AddressChangeType` method to complement `TransactionChangeType`: bitcoin/bitcoin#12729 (comment). Tree-SHA512: 8b08b272bcb177a0a9e556dcd965840a7fe601ef83ca97938b879c9b1a33b5b3f96939e1bceef11ba7c644ac21bfd6c1dbc6ca715cd1da4ace50475240e4ee48
2 parents 2afdc29 + 1e46d8a commit 979150b

File tree

7 files changed

+41
-31
lines changed

7 files changed

+41
-31
lines changed

doc/release-notes.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ Low-level RPC changes
104104
now the empty string `""` instead of `"wallet.dat"`. If bitcoin is started
105105
with any `-wallet=<path>` options, there is no change in behavior, and the
106106
name of any wallet is just its `<path>` string.
107+
- Passing an empty string (`""`) as the `address_type` parameter to
108+
`getnewaddress`, `getrawchangeaddress`, `addmultisigaddress`,
109+
`fundrawtransaction` RPCs is now an error. Previously, this would fall back
110+
to using the default address type. It is still possible to pass null or leave
111+
the parameter unset to use the default address type.
107112

108113
- Bare multisig outputs to our keys are no longer automatically treated as
109114
incoming payments. As this feature was only available for multisig outputs for

src/qt/paymentserver.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -648,7 +648,7 @@ void PaymentServer::fetchPaymentACK(WalletModel* walletModel, const SendCoinsRec
648648
// use for change. Despite an actual payment and not change, this is a close match:
649649
// it's the output type we use subject to privacy issues, but not restricted by what
650650
// other software supports.
651-
const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::NONE ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
651+
const OutputType change_type = walletModel->wallet().getDefaultChangeType() != OutputType::CHANGE_AUTO ? walletModel->wallet().getDefaultChangeType() : walletModel->wallet().getDefaultAddressType();
652652
walletModel->wallet().learnRelatedScripts(newKey, change_type);
653653
CTxDestination dest = GetDestinationForKey(newKey, change_type);
654654
std::string label = tr("Refund from %1").arg(recipient.authenticatedMerchant).toStdString();

src/wallet/rpcwallet.cpp

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -164,8 +164,7 @@ UniValue getnewaddress(const JSONRPCRequest& request)
164164

165165
OutputType output_type = pwallet->m_default_address_type;
166166
if (!request.params[1].isNull()) {
167-
output_type = ParseOutputType(request.params[1].get_str(), pwallet->m_default_address_type);
168-
if (output_type == OutputType::NONE) {
167+
if (!ParseOutputType(request.params[1].get_str(), output_type)) {
169168
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[1].get_str()));
170169
}
171170
}
@@ -282,10 +281,9 @@ UniValue getrawchangeaddress(const JSONRPCRequest& request)
282281
pwallet->TopUpKeyPool();
283282
}
284283

285-
OutputType output_type = pwallet->m_default_change_type != OutputType::NONE ? pwallet->m_default_change_type : pwallet->m_default_address_type;
284+
OutputType output_type = pwallet->m_default_change_type != OutputType::CHANGE_AUTO ? pwallet->m_default_change_type : pwallet->m_default_address_type;
286285
if (!request.params[0].isNull()) {
287-
output_type = ParseOutputType(request.params[0].get_str(), output_type);
288-
if (output_type == OutputType::NONE) {
286+
if (!ParseOutputType(request.params[0].get_str(), output_type)) {
289287
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[0].get_str()));
290288
}
291289
}
@@ -1317,8 +1315,7 @@ UniValue addmultisigaddress(const JSONRPCRequest& request)
13171315

13181316
OutputType output_type = pwallet->m_default_address_type;
13191317
if (!request.params[3].isNull()) {
1320-
output_type = ParseOutputType(request.params[3].get_str(), output_type);
1321-
if (output_type == OutputType::NONE) {
1318+
if (!ParseOutputType(request.params[3].get_str(), output_type)) {
13221319
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[3].get_str()));
13231320
}
13241321
}
@@ -3317,8 +3314,8 @@ UniValue fundrawtransaction(const JSONRPCRequest& request)
33173314
if (options.exists("changeAddress")) {
33183315
throw JSONRPCError(RPC_INVALID_PARAMETER, "Cannot specify both changeAddress and address_type options");
33193316
}
3320-
coinControl.m_change_type = ParseOutputType(options["change_type"].get_str(), pwallet->m_default_change_type);
3321-
if (coinControl.m_change_type == OutputType::NONE) {
3317+
coinControl.m_change_type = pwallet->m_default_change_type;
3318+
if (!ParseOutputType(options["change_type"].get_str(), *coinControl.m_change_type)) {
33223319
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown change type '%s'", options["change_type"].get_str()));
33233320
}
33243321
}

src/wallet/wallet.cpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2674,7 +2674,7 @@ bool CWallet::FundTransaction(CMutableTransaction& tx, CAmount& nFeeRet, int& nC
26742674
OutputType CWallet::TransactionChangeType(OutputType change_type, const std::vector<CRecipient>& vecSend)
26752675
{
26762676
// If -changetype is specified, always use that change type.
2677-
if (change_type != OutputType::NONE) {
2677+
if (change_type != OutputType::CHANGE_AUTO) {
26782678
return change_type;
26792679
}
26802680

@@ -4054,16 +4054,12 @@ CWallet* CWallet::CreateWalletFromFile(const std::string& name, const fs::path&
40544054
}
40554055
}
40564056

4057-
walletInstance->m_default_address_type = ParseOutputType(gArgs.GetArg("-addresstype", ""), DEFAULT_ADDRESS_TYPE);
4058-
if (walletInstance->m_default_address_type == OutputType::NONE) {
4057+
if (!gArgs.GetArg("-addresstype", "").empty() && !ParseOutputType(gArgs.GetArg("-addresstype", ""), walletInstance->m_default_address_type)) {
40594058
InitError(strprintf("Unknown address type '%s'", gArgs.GetArg("-addresstype", "")));
40604059
return nullptr;
40614060
}
40624061

4063-
// If changetype is set in config file or parameter, check that it's valid.
4064-
// Default to OutputType::NONE if not set.
4065-
walletInstance->m_default_change_type = ParseOutputType(gArgs.GetArg("-changetype", ""), OutputType::NONE);
4066-
if (walletInstance->m_default_change_type == OutputType::NONE && !gArgs.GetArg("-changetype", "").empty()) {
4062+
if (!gArgs.GetArg("-changetype", "").empty() && !ParseOutputType(gArgs.GetArg("-changetype", ""), walletInstance->m_default_change_type)) {
40674063
InitError(strprintf("Unknown change type '%s'", gArgs.GetArg("-changetype", "")));
40684064
return nullptr;
40694065
}
@@ -4311,19 +4307,19 @@ static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
43114307
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
43124308
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
43134309

4314-
OutputType ParseOutputType(const std::string& type, OutputType default_type)
4310+
bool ParseOutputType(const std::string& type, OutputType& output_type)
43154311
{
4316-
if (type.empty()) {
4317-
return default_type;
4318-
} else if (type == OUTPUT_TYPE_STRING_LEGACY) {
4319-
return OutputType::LEGACY;
4312+
if (type == OUTPUT_TYPE_STRING_LEGACY) {
4313+
output_type = OutputType::LEGACY;
4314+
return true;
43204315
} else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
4321-
return OutputType::P2SH_SEGWIT;
4316+
output_type = OutputType::P2SH_SEGWIT;
4317+
return true;
43224318
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
4323-
return OutputType::BECH32;
4324-
} else {
4325-
return OutputType::NONE;
4319+
output_type = OutputType::BECH32;
4320+
return true;
43264321
}
4322+
return false;
43274323
}
43284324

43294325
const std::string& FormatOutputType(OutputType type)

src/wallet/wallet.h

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -93,15 +93,24 @@ enum WalletFeature
9393
};
9494

9595
enum class OutputType {
96-
NONE,
9796
LEGACY,
9897
P2SH_SEGWIT,
9998
BECH32,
99+
100+
/**
101+
* Special output type for change outputs only. Automatically choose type
102+
* based on address type setting and the types other of non-change outputs
103+
* (see -changetype option documentation and implementation in
104+
* CWallet::TransactionChangeType for details).
105+
*/
106+
CHANGE_AUTO,
100107
};
101108

102109
//! Default for -addresstype
103110
constexpr OutputType DEFAULT_ADDRESS_TYPE{OutputType::P2SH_SEGWIT};
104111

112+
//! Default for -changetype
113+
constexpr OutputType DEFAULT_CHANGE_TYPE{OutputType::CHANGE_AUTO};
105114

106115
/** A key pool entry */
107116
class CKeyPool
@@ -973,7 +982,7 @@ class CWallet final : public CCryptoKeyStore, public CValidationInterface
973982
CFeeRate m_fallback_fee{DEFAULT_FALLBACK_FEE};
974983
CFeeRate m_discard_rate{DEFAULT_DISCARD_FEE};
975984
OutputType m_default_address_type{DEFAULT_ADDRESS_TYPE};
976-
OutputType m_default_change_type{OutputType::NONE}; // Default to OutputType::NONE if not set by -changetype
985+
OutputType m_default_change_type{DEFAULT_CHANGE_TYPE};
977986

978987
bool NewKeyPool();
979988
size_t KeypoolCountExternalKeys();
@@ -1218,7 +1227,7 @@ class CAccount
12181227
}
12191228
};
12201229

1221-
OutputType ParseOutputType(const std::string& str, OutputType default_type);
1230+
bool ParseOutputType(const std::string& str, OutputType& output_type);
12221231
const std::string& FormatOutputType(OutputType type);
12231232

12241233
/**

test/functional/rpc_fundrawtransaction.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -224,7 +224,7 @@ def run_test(self):
224224
outputs = { self.nodes[0].getnewaddress() : Decimal(4.0) }
225225
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
226226
assert_raises_rpc_error(-1, "JSON value is not a string as expected", self.nodes[2].fundrawtransaction, rawtx, {'change_type': None})
227-
assert_raises_rpc_error(-5, "Unknown change type", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
227+
assert_raises_rpc_error(-5, "Unknown change type ''", self.nodes[2].fundrawtransaction, rawtx, {'change_type': ''})
228228
rawtx = self.nodes[2].fundrawtransaction(rawtx, {'change_type': 'bech32'})
229229
dec_tx = self.nodes[2].decoderawtransaction(rawtx['hex'])
230230
assert_equal('witness_v0_keyhash', dec_tx['vout'][rawtx['changepos']]['scriptPubKey']['type'])

test/functional/wallet_address_types.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,7 +280,10 @@ def run_test(self):
280280
self.log.info('getrawchangeaddress defaults to addresstype if -changetype is not set and argument is absent')
281281
self.test_address(3, self.nodes[3].getrawchangeaddress(), multisig=False, typ='bech32')
282282

283-
self.log.info('getrawchangeaddress fails with invalid changetype argument')
283+
self.log.info('test invalid address type arguments')
284+
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].addmultisigaddress, 2, [compressed_1, compressed_2], None, '')
285+
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getnewaddress, None, '')
286+
assert_raises_rpc_error(-5, "Unknown address type ''", self.nodes[3].getrawchangeaddress, '')
284287
assert_raises_rpc_error(-5, "Unknown address type 'bech23'", self.nodes[3].getrawchangeaddress, 'bech23')
285288

286289
self.log.info("Nodes with changetype=p2sh-segwit never use a P2WPKH change output")

0 commit comments

Comments
 (0)