diff --git a/src/rpc/misc.cpp b/src/rpc/misc.cpp --- a/src/rpc/misc.cpp +++ b/src/rpc/misc.cpp @@ -155,10 +155,10 @@ OutputType output_type = OutputType::LEGACY; // Construct using pay-to-script-hash: - const CScript inner = CreateMultisigRedeemscript(required, pubkeys); CBasicKeyStore keystore; - const CTxDestination dest = - AddAndGetDestinationForScript(keystore, inner, output_type); + CScript inner; + const CTxDestination dest = AddAndGetMultisigDestination( + required, pubkeys, output_type, keystore, inner); UniValue result(UniValue::VOBJ); result.pushKV("address", EncodeDestination(dest, config)); diff --git a/src/rpc/util.h b/src/rpc/util.h --- a/src/rpc/util.h +++ b/src/rpc/util.h @@ -6,6 +6,7 @@ #define BITCOIN_RPC_UTIL_H #include <node/transaction.h> +#include <outputtype.h> #include <rpc/protocol.h> #include <rpc/request.h> #include <script/standard.h> // For CTxDestination @@ -73,8 +74,11 @@ CPubKey HexToPubKey(const std::string &hex_in); CPubKey AddrToPubKey(const CChainParams &chainparams, CKeyStore *const keystore, const std::string &addr_in); -CScript CreateMultisigRedeemscript(const int required, - const std::vector<CPubKey> &pubkeys); +CTxDestination AddAndGetMultisigDestination(const int required, + const std::vector<CPubKey> &pubkeys, + OutputType type, + CKeyStore &keystore, + CScript &script_out); UniValue DescribeAddress(const CTxDestination &dest); diff --git a/src/rpc/util.cpp b/src/rpc/util.cpp --- a/src/rpc/util.cpp +++ b/src/rpc/util.cpp @@ -2,10 +2,11 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include <rpc/util.h> + #include <key_io.h> #include <keystore.h> #include <pubkey.h> -#include <rpc/util.h> #include <tinyformat.h> #include <util/strencodings.h> #include <util/string.h> @@ -181,10 +182,13 @@ return vchPubKey; } -// Creates a multisig redeemscript from a given list of public keys and number -// required. -CScript CreateMultisigRedeemscript(const int required, - const std::vector<CPubKey> &pubkeys) { +// Creates a multisig address from a given list of public keys, number of +// signatures required, and the address type +CTxDestination AddAndGetMultisigDestination(const int required, + const std::vector<CPubKey> &pubkeys, + OutputType type, + CKeyStore &keystore, + CScript &script_out) { // Gather public keys if (required < 1) { throw JSONRPCError( @@ -203,16 +207,28 @@ "address creation > 16\nReduce the number"); } - CScript result = GetScriptForMultisig(required, pubkeys); + script_out = GetScriptForMultisig(required, pubkeys); - if (result.size() > MAX_SCRIPT_ELEMENT_SIZE) { + if (script_out.size() > MAX_SCRIPT_ELEMENT_SIZE) { throw JSONRPCError( RPC_INVALID_PARAMETER, (strprintf("redeemScript exceeds size limit: %d > %d", - result.size(), MAX_SCRIPT_ELEMENT_SIZE))); + script_out.size(), MAX_SCRIPT_ELEMENT_SIZE))); } - return result; + // Check if any keys are uncompressed. If so, the type is legacy + for (const CPubKey &pk : pubkeys) { + if (!pk.IsCompressed()) { + type = OutputType::LEGACY; + break; + } + } + + // Make the address + CTxDestination dest = + AddAndGetDestinationForScript(keystore, script_out, type); + + return dest; } class DescribeAddressVisitor : public boost::static_visitor<UniValue> { diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -1224,9 +1224,9 @@ OutputType output_type = pwallet->m_default_address_type; // Construct using pay-to-script-hash: - CScript inner = CreateMultisigRedeemscript(required, pubkeys); - CTxDestination dest = - AddAndGetDestinationForScript(*pwallet, inner, output_type); + CScript inner; + CTxDestination dest = AddAndGetMultisigDestination( + required, pubkeys, output_type, *pwallet, inner); pwallet->SetAddressBook(dest, label, "send"); UniValue result(UniValue::VOBJ); diff --git a/test/functional/rpc_createmultisig.py b/test/functional/rpc_createmultisig.py --- a/test/functional/rpc_createmultisig.py +++ b/test/functional/rpc_createmultisig.py @@ -7,8 +7,13 @@ from test_framework.test_framework import BitcoinTestFramework from test_framework.util import ( assert_raises_rpc_error, + assert_equal, ) +from test_framework.key import ECPubKey + +import binascii import decimal +import itertools class RpcCreateMultiSigTest(BitcoinTestFramework): @@ -43,6 +48,31 @@ self.checkbalances() + # Test mixed compressed and uncompressed pubkeys + self.log.info( + 'Mixed compressed and uncompressed multisigs are not allowed') + pk0 = node0.getaddressinfo(node0.getnewaddress())['pubkey'] + pk1 = node1.getaddressinfo(node1.getnewaddress())['pubkey'] + pk2 = node2.getaddressinfo(node2.getnewaddress())['pubkey'] + + # decompress pk2 + pk_obj = ECPubKey() + pk_obj.set(binascii.unhexlify(pk2)) + pk_obj.compressed = False + pk2 = binascii.hexlify(pk_obj.get_bytes()).decode() + + # Check all permutations of keys because order matters apparently + for keys in itertools.permutations([pk0, pk1, pk2]): + # Results should be the same as this legacy one + legacy_addr = node0.createmultisig(2, keys)['address'] + assert_equal( + legacy_addr, node0.addmultisigaddress( + 2, keys, '')['address']) + + # Generate addresses with the segwit types. These should all make + # legacy addresses + assert_equal(legacy_addr, node0.createmultisig(2, keys)['address']) + def check_addmultisigaddress_errors(self): self.log.info( 'Check that addmultisigaddress fails when the private keys are missing')