Skip to content

Commit b25a4c2

Browse files
committed
Merge #13072: Update createmultisig RPC to support segwit
f40b3b8 [tests] functional test for createmultisig RPC (Anthony Towns) b9024fd segwit support for createmultisig RPC (Anthony Towns) d58055d Move AddAndGetDestinationForScript from wallet to outputype module (Anthony Towns) 9a44db2 Add outputtype module (Anthony Towns) Pull request description: Adds an "address_type" parameter that accepts "legacy", "p2sh-segwit", and "bech32" to choose the type of address created. Defaults to "legacy" rather than the value of the `-address-type` option for backwards compatibility. As part of implementing this, OutputType is moved from wallet into its own module, and `AddAndGetDestinationForScript` is changed to apply to a `CKeyStore` rather than a wallet, and to invoke `keystore.AddCScript(script)` itself rather than expecting the caller to have done that. Fixes #12502 Tree-SHA512: a08c1cfa89976e4fd7d29caa90919ebd34a446354d17abb862e99f2ee60ed9bc19d8a21a18547c51dc3812cb9fbed86af0bef2f1e971f62bf95cade4a7d86237
2 parents 1329ef1 + f40b3b8 commit b25a4c2

File tree

10 files changed

+271
-122
lines changed

10 files changed

+271
-122
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ BITCOIN_CORE_H = \
139139
netbase.h \
140140
netmessagemaker.h \
141141
noui.h \
142+
outputtype.h \
142143
policy/feerate.h \
143144
policy/fees.h \
144145
policy/policy.h \
@@ -230,6 +231,7 @@ libbitcoin_server_a_SOURCES = \
230231
net.cpp \
231232
net_processing.cpp \
232233
noui.cpp \
234+
outputtype.cpp \
233235
policy/fees.cpp \
234236
policy/policy.cpp \
235237
policy/rbf.cpp \

src/outputtype.cpp

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2017 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#include <outputtype.h>
7+
8+
#include <keystore.h>
9+
#include <pubkey.h>
10+
#include <script/script.h>
11+
#include <script/standard.h>
12+
13+
#include <assert.h>
14+
#include <string>
15+
16+
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
17+
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
18+
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
19+
20+
bool ParseOutputType(const std::string& type, OutputType& output_type)
21+
{
22+
if (type == OUTPUT_TYPE_STRING_LEGACY) {
23+
output_type = OutputType::LEGACY;
24+
return true;
25+
} else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
26+
output_type = OutputType::P2SH_SEGWIT;
27+
return true;
28+
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
29+
output_type = OutputType::BECH32;
30+
return true;
31+
}
32+
return false;
33+
}
34+
35+
const std::string& FormatOutputType(OutputType type)
36+
{
37+
switch (type) {
38+
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
39+
case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
40+
case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
41+
default: assert(false);
42+
}
43+
}
44+
45+
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
46+
{
47+
switch (type) {
48+
case OutputType::LEGACY: return key.GetID();
49+
case OutputType::P2SH_SEGWIT:
50+
case OutputType::BECH32: {
51+
if (!key.IsCompressed()) return key.GetID();
52+
CTxDestination witdest = WitnessV0KeyHash(key.GetID());
53+
CScript witprog = GetScriptForDestination(witdest);
54+
if (type == OutputType::P2SH_SEGWIT) {
55+
return CScriptID(witprog);
56+
} else {
57+
return witdest;
58+
}
59+
}
60+
default: assert(false);
61+
}
62+
}
63+
64+
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
65+
{
66+
CKeyID keyid = key.GetID();
67+
if (key.IsCompressed()) {
68+
CTxDestination segwit = WitnessV0KeyHash(keyid);
69+
CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
70+
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
71+
} else {
72+
return std::vector<CTxDestination>{std::move(keyid)};
73+
}
74+
}
75+
76+
CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType type)
77+
{
78+
// Add script to keystore
79+
keystore.AddCScript(script);
80+
// Note that scripts over 520 bytes are not yet supported.
81+
switch (type) {
82+
case OutputType::LEGACY:
83+
return CScriptID(script);
84+
case OutputType::P2SH_SEGWIT:
85+
case OutputType::BECH32: {
86+
CTxDestination witdest = WitnessV0ScriptHash(script);
87+
CScript witprog = GetScriptForDestination(witdest);
88+
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
89+
if (!IsSolvable(keystore, witprog)) return CScriptID(script);
90+
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
91+
keystore.AddCScript(witprog);
92+
if (type == OutputType::BECH32) {
93+
return witdest;
94+
} else {
95+
return CScriptID(witprog);
96+
}
97+
}
98+
default: assert(false);
99+
}
100+
}
101+

src/outputtype.h

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copyright (c) 2009-2010 Satoshi Nakamoto
2+
// Copyright (c) 2009-2017 The Bitcoin Core developers
3+
// Distributed under the MIT software license, see the accompanying
4+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5+
6+
#ifndef BITCOIN_OUTPUTTYPE_H
7+
#define BITCOIN_OUTPUTTYPE_H
8+
9+
#include <keystore.h>
10+
#include <script/standard.h>
11+
12+
#include <string>
13+
#include <vector>
14+
15+
enum class OutputType {
16+
LEGACY,
17+
P2SH_SEGWIT,
18+
BECH32,
19+
20+
/**
21+
* Special output type for change outputs only. Automatically choose type
22+
* based on address type setting and the types other of non-change outputs
23+
* (see -changetype option documentation and implementation in
24+
* CWallet::TransactionChangeType for details).
25+
*/
26+
CHANGE_AUTO,
27+
};
28+
29+
bool ParseOutputType(const std::string& str, OutputType& output_type);
30+
const std::string& FormatOutputType(OutputType type);
31+
32+
/**
33+
* Get a destination of the requested type (if possible) to the specified key.
34+
* The caller must make sure LearnRelatedScripts has been called beforehand.
35+
*/
36+
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType);
37+
38+
/** Get all destinations (potentially) supported by the wallet for the given key. */
39+
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key);
40+
41+
/**
42+
* Get a destination of the requested type (if possible) to the specified script.
43+
* This function will automatically add the script (and any other
44+
* necessary scripts) to the keystore.
45+
*/
46+
CTxDestination AddAndGetDestinationForScript(CKeyStore& keystore, const CScript& script, OutputType);
47+
48+
#endif // BITCOIN_OUTPUTTYPE_H
49+

src/rpc/misc.cpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <httpserver.h>
1313
#include <net.h>
1414
#include <netbase.h>
15+
#include <outputtype.h>
1516
#include <rpc/blockchain.h>
1617
#include <rpc/server.h>
1718
#include <rpc/util.h>
@@ -91,9 +92,9 @@ class CWallet;
9192

9293
static UniValue createmultisig(const JSONRPCRequest& request)
9394
{
94-
if (request.fHelp || request.params.size() < 2 || request.params.size() > 2)
95+
if (request.fHelp || request.params.size() < 2 || request.params.size() > 3)
9596
{
96-
std::string msg = "createmultisig nrequired [\"key\",...]\n"
97+
std::string msg = "createmultisig nrequired [\"key\",...] ( \"address_type\" )\n"
9798
"\nCreates a multi-signature address with n signature of m keys required.\n"
9899
"It returns a json object with the address and redeemScript.\n"
99100
"\nArguments:\n"
@@ -103,6 +104,7 @@ static UniValue createmultisig(const JSONRPCRequest& request)
103104
" \"key\" (string) The hex-encoded public key\n"
104105
" ,...\n"
105106
" ]\n"
107+
"3. \"address_type\" (string, optional) The address type to use. Options are \"legacy\", \"p2sh-segwit\", and \"bech32\". Default is legacy.\n"
106108

107109
"\nResult:\n"
108110
"{\n"
@@ -133,12 +135,21 @@ static UniValue createmultisig(const JSONRPCRequest& request)
133135
}
134136
}
135137

138+
// Get the output type
139+
OutputType output_type = OutputType::LEGACY;
140+
if (!request.params[2].isNull()) {
141+
if (!ParseOutputType(request.params[2].get_str(), output_type)) {
142+
throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, strprintf("Unknown address type '%s'", request.params[2].get_str()));
143+
}
144+
}
145+
136146
// Construct using pay-to-script-hash:
137-
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
138-
CScriptID innerID(inner);
147+
const CScript inner = CreateMultisigRedeemscript(required, pubkeys);
148+
CBasicKeyStore keystore;
149+
const CTxDestination dest = AddAndGetDestinationForScript(keystore, inner, output_type);
139150

140151
UniValue result(UniValue::VOBJ);
141-
result.pushKV("address", EncodeDestination(innerID));
152+
result.pushKV("address", EncodeDestination(dest));
142153
result.pushKV("redeemScript", HexStr(inner.begin(), inner.end()));
143154

144155
return result;

src/wallet/init.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
#include <init.h>
88
#include <net.h>
99
#include <scheduler.h>
10+
#include <outputtype.h>
1011
#include <util.h>
1112
#include <utilmoneystr.h>
1213
#include <validation.h>

src/wallet/rpcwallet.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <validation.h>
1212
#include <key_io.h>
1313
#include <net.h>
14+
#include <outputtype.h>
1415
#include <policy/feerate.h>
1516
#include <policy/fees.h>
1617
#include <policy/policy.h>
@@ -1369,8 +1370,7 @@ static UniValue addmultisigaddress(const JSONRPCRequest& request)
13691370

13701371
// Construct using pay-to-script-hash:
13711372
CScript inner = CreateMultisigRedeemscript(required, pubkeys);
1372-
pwallet->AddCScript(inner);
1373-
CTxDestination dest = pwallet->AddAndGetDestinationForScript(inner, output_type);
1373+
CTxDestination dest = AddAndGetDestinationForScript(*pwallet, inner, output_type);
13741374
pwallet->SetAddressBook(dest, label, "send");
13751375

13761376
UniValue result(UniValue::VOBJ);

src/wallet/wallet.cpp

Lines changed: 0 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -4362,35 +4362,6 @@ bool CWalletTx::AcceptToMemoryPool(const CAmount& nAbsurdFee, CValidationState&
43624362
return ret;
43634363
}
43644364

4365-
static const std::string OUTPUT_TYPE_STRING_LEGACY = "legacy";
4366-
static const std::string OUTPUT_TYPE_STRING_P2SH_SEGWIT = "p2sh-segwit";
4367-
static const std::string OUTPUT_TYPE_STRING_BECH32 = "bech32";
4368-
4369-
bool ParseOutputType(const std::string& type, OutputType& output_type)
4370-
{
4371-
if (type == OUTPUT_TYPE_STRING_LEGACY) {
4372-
output_type = OutputType::LEGACY;
4373-
return true;
4374-
} else if (type == OUTPUT_TYPE_STRING_P2SH_SEGWIT) {
4375-
output_type = OutputType::P2SH_SEGWIT;
4376-
return true;
4377-
} else if (type == OUTPUT_TYPE_STRING_BECH32) {
4378-
output_type = OutputType::BECH32;
4379-
return true;
4380-
}
4381-
return false;
4382-
}
4383-
4384-
const std::string& FormatOutputType(OutputType type)
4385-
{
4386-
switch (type) {
4387-
case OutputType::LEGACY: return OUTPUT_TYPE_STRING_LEGACY;
4388-
case OutputType::P2SH_SEGWIT: return OUTPUT_TYPE_STRING_P2SH_SEGWIT;
4389-
case OutputType::BECH32: return OUTPUT_TYPE_STRING_BECH32;
4390-
default: assert(false);
4391-
}
4392-
}
4393-
43944365
void CWallet::LearnRelatedScripts(const CPubKey& key, OutputType type)
43954366
{
43964367
if (key.IsCompressed() && (type == OutputType::P2SH_SEGWIT || type == OutputType::BECH32)) {
@@ -4408,57 +4379,3 @@ void CWallet::LearnAllRelatedScripts(const CPubKey& key)
44084379
LearnRelatedScripts(key, OutputType::P2SH_SEGWIT);
44094380
}
44104381

4411-
CTxDestination GetDestinationForKey(const CPubKey& key, OutputType type)
4412-
{
4413-
switch (type) {
4414-
case OutputType::LEGACY: return key.GetID();
4415-
case OutputType::P2SH_SEGWIT:
4416-
case OutputType::BECH32: {
4417-
if (!key.IsCompressed()) return key.GetID();
4418-
CTxDestination witdest = WitnessV0KeyHash(key.GetID());
4419-
CScript witprog = GetScriptForDestination(witdest);
4420-
if (type == OutputType::P2SH_SEGWIT) {
4421-
return CScriptID(witprog);
4422-
} else {
4423-
return witdest;
4424-
}
4425-
}
4426-
default: assert(false);
4427-
}
4428-
}
4429-
4430-
std::vector<CTxDestination> GetAllDestinationsForKey(const CPubKey& key)
4431-
{
4432-
CKeyID keyid = key.GetID();
4433-
if (key.IsCompressed()) {
4434-
CTxDestination segwit = WitnessV0KeyHash(keyid);
4435-
CTxDestination p2sh = CScriptID(GetScriptForDestination(segwit));
4436-
return std::vector<CTxDestination>{std::move(keyid), std::move(p2sh), std::move(segwit)};
4437-
} else {
4438-
return std::vector<CTxDestination>{std::move(keyid)};
4439-
}
4440-
}
4441-
4442-
CTxDestination CWallet::AddAndGetDestinationForScript(const CScript& script, OutputType type)
4443-
{
4444-
// Note that scripts over 520 bytes are not yet supported.
4445-
switch (type) {
4446-
case OutputType::LEGACY:
4447-
return CScriptID(script);
4448-
case OutputType::P2SH_SEGWIT:
4449-
case OutputType::BECH32: {
4450-
CTxDestination witdest = WitnessV0ScriptHash(script);
4451-
CScript witprog = GetScriptForDestination(witdest);
4452-
// Check if the resulting program is solvable (i.e. doesn't use an uncompressed key)
4453-
if (!IsSolvable(*this, witprog)) return CScriptID(script);
4454-
// Add the redeemscript, so that P2WSH and P2SH-P2WSH outputs are recognized as ours.
4455-
AddCScript(witprog);
4456-
if (type == OutputType::BECH32) {
4457-
return witdest;
4458-
} else {
4459-
return CScriptID(witprog);
4460-
}
4461-
}
4462-
default: assert(false);
4463-
}
4464-
}

0 commit comments

Comments
 (0)