Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 1 addition & 4 deletions doc/REST-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,8 @@ $ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff76
"scriptPubKey" : {
"asm" : "OP_DUP OP_HASH160 1c7cebb529b86a04c683dfa87be49de35bcf589e OP_EQUALVERIFY OP_CHECKSIG",
"hex" : "76a9141c7cebb529b86a04c683dfa87be49de35bcf589e88ac",
"reqSigs" : 1,
"type" : "pubkeyhash",
"addresses" : [
"mi7as51dvLJsizWnTMurtRmrP8hG2m1XvD"
]
"address" : "mi7as51dvLJsizWnTMurtRmrP8hG2m1XvD"
}
}
]
Expand Down
28 changes: 8 additions & 20 deletions src/core_write.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -141,40 +141,28 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_address)
out.pushKV("asm", ScriptToAsmStr(script));
out.pushKV("hex", HexStr(script));

std::vector<std::vector<unsigned char>> solns;
TxoutType type = Solver(script, solns);
out.pushKV("type", GetTxnOutputType(type));

TxoutType type;
CTxDestination address;
if (include_address && ExtractDestination(script, address) && type != TxoutType::PUBKEY) {

if (ExtractDestination(script, type, address) && include_address && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}
out.pushKV("type", GetTxnOutputType(type));
}

void ScriptPubKeyToUniv(const CScript& scriptPubKey,
UniValue& out, bool fIncludeHex)
void ScriptPubKeyToUniv(const CScript& scriptPubKey, UniValue& out, bool fIncludeHex)
{
TxoutType type;
std::vector<CTxDestination> addresses;
int nRequired;
CTxDestination address;

out.pushKV("asm", ScriptToAsmStr(scriptPubKey));
if (fIncludeHex)
out.pushKV("hex", HexStr(scriptPubKey));

if (!ExtractDestinations(scriptPubKey, type, addresses, nRequired) || type == TxoutType::PUBKEY) {
out.pushKV("type", GetTxnOutputType(type));
return;
if (ExtractDestination(scriptPubKey, type, address) && type != TxoutType::PUBKEY) {
out.pushKV("address", EncodeDestination(address));
}

out.pushKV("reqSigs", nRequired);
out.pushKV("type", GetTxnOutputType(type));

UniValue a(UniValue::VARR);
for (const CTxDestination& addr : addresses) {
a.push_back(EncodeDestination(addr));
}
out.pushKV("addresses", a);
}

void TxToUniv(const CTransaction& tx, const uint256& hashBlock, UniValue& entry, bool include_hex, int serialize_flags)
Expand Down
6 changes: 2 additions & 4 deletions src/rpc/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1114,10 +1114,8 @@ static RPCHelpMan gettxout()
{
{RPCResult::Type::STR_HEX, "asm", ""},
{RPCResult::Type::STR_HEX, "hex", ""},
{RPCResult::Type::NUM, "reqSigs", "Number of required signatures"},
{RPCResult::Type::STR_HEX, "type", "The type, eg pubkeyhash"},
{RPCResult::Type::ARR, "addresses", "array of bitcoin addresses",
{{RPCResult::Type::STR, "address", "bitcoin address"}}},
{RPCResult::Type::STR, "type", "The type, eg pubkeyhash"},
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
{RPCResult::Type::BOOL, "coinbase", "Coinbase or not"},
}},
Expand Down
19 changes: 3 additions & 16 deletions src/rpc/rawtransaction.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -132,12 +132,8 @@ static RPCHelpMan getrawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR, "hex", "the hex"},
{RPCResult::Type::NUM, "reqSigs", "The required sigs"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::ARR, "addresses", "",
{
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
}},
}},
Expand Down Expand Up @@ -490,12 +486,8 @@ static RPCHelpMan decoderawtransaction()
{
{RPCResult::Type::STR, "asm", "the asm"},
{RPCResult::Type::STR_HEX, "hex", "the hex"},
{RPCResult::Type::NUM, "reqSigs", "The required sigs"},
{RPCResult::Type::STR, "type", "The type, eg 'pubkeyhash'"},
{RPCResult::Type::ARR, "addresses", "",
{
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
}},
}},
Expand Down Expand Up @@ -548,18 +540,13 @@ static RPCHelpMan decodescript()
{
{RPCResult::Type::STR, "asm", "Script public key"},
{RPCResult::Type::STR, "type", "The output type (e.g. "+GetAllOutputTypes()+")"},
{RPCResult::Type::NUM, "reqSigs", "The required signatures"},
{RPCResult::Type::ARR, "addresses", "",
{
{RPCResult::Type::STR, "address", "bitcoin address"},
}},
{RPCResult::Type::STR, "address", "bitcoin address"},
{RPCResult::Type::STR, "p2sh", "address of P2SH script wrapping this redeem script (not returned if the script is already a P2SH)"},
{RPCResult::Type::OBJ, "segwit", "Result of a witness script public key wrapping this redeem script (not returned if the script is a P2SH or witness)",
{
{RPCResult::Type::STR, "asm", "String representation of the script public key"},
{RPCResult::Type::STR_HEX, "hex", "Hex string of the script public key"},
{RPCResult::Type::STR, "type", "The type of the script public key (e.g. witness_v0_keyhash or witness_v0_scripthash)"},
{RPCResult::Type::NUM, "reqSigs", "The required signatures (always 1)"},
{RPCResult::Type::ARR, "addresses", "(always length 1)",
{
{RPCResult::Type::STR, "address", "segwit address"},
Expand Down
44 changes: 5 additions & 39 deletions src/script/standard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,10 +177,10 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
return TxoutType::NONSTANDARD;
}

bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
bool ExtractDestination(const CScript& scriptPubKey, TxoutType& whichType, CTxDestination& addressRet)
{
std::vector<valtype> vSolutions;
TxoutType whichType = Solver(scriptPubKey, vSolutions);
whichType = Solver(scriptPubKey, vSolutions);

if (whichType == TxoutType::PUBKEY) {
CPubKey pubKey(vSolutions[0]);
Expand Down Expand Up @@ -221,44 +221,10 @@ bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
return false;
}

bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet)
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
{
addressRet.clear();
std::vector<valtype> vSolutions;
typeRet = Solver(scriptPubKey, vSolutions);
if (typeRet == TxoutType::NONSTANDARD) {
return false;
} else if (typeRet == TxoutType::NULL_DATA) {
// This is data, not addresses
return false;
}

if (typeRet == TxoutType::MULTISIG)
{
nRequiredRet = vSolutions.front()[0];
for (unsigned int i = 1; i < vSolutions.size()-1; i++)
{
CPubKey pubKey(vSolutions[i]);
if (!pubKey.IsValid())
continue;

CTxDestination address = PKHash(pubKey);
addressRet.push_back(address);
}

if (addressRet.empty())
return false;
}
else
{
nRequiredRet = 1;
CTxDestination address;
if (!ExtractDestination(scriptPubKey, address))
return false;
addressRet.push_back(address);
}

return true;
TxoutType type;
return ExtractDestination(scriptPubKey, type, addressRet);
}

namespace
Expand Down
17 changes: 2 additions & 15 deletions src/script/standard.h
Original file line number Diff line number Diff line change
Expand Up @@ -233,25 +233,12 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c

/**
* Parse a standard scriptPubKey for the destination address. Assigns result to
* the addressRet parameter and returns true if successful. For multisig
* scripts, instead use ExtractDestinations. Currently only works for P2PK,
* the addressRet parameter and returns true if successful. Currently only works for P2PK,
* P2PKH, P2SH, P2WPKH, and P2WSH scripts.
*/
bool ExtractDestination(const CScript& scriptPubKey, TxoutType& typeRet, CTxDestination& addressRet);
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet);

/**
* Parse a standard scriptPubKey with one or more destination addresses. For
* multisig scripts, this populates the addressRet vector with the pubkey IDs
* and nRequiredRet with the n required to spend. For other destinations,
* addressRet is populated with a single value and nRequiredRet is set to 1.
* Returns true if successful.
*
* Note: this function confuses destinations (a subset of CScripts that are
* encodable as an address) with key identifiers (of keys involved in a
* CScript), and its use should be phased out.
*/
bool ExtractDestinations(const CScript& scriptPubKey, TxoutType& typeRet, std::vector<CTxDestination>& addressRet, int& nRequiredRet);

/**
* Generate a Bitcoin scriptPubKey for the given CTxDestination. Returns a P2PKH
* script for a CKeyID destination, a P2SH script for a CScriptID, and an empty
Expand Down
5 changes: 0 additions & 5 deletions src/test/fuzz/script.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,6 @@ FUZZ_TARGET_INIT(script, initialize_script)
CTxDestination address;
(void)ExtractDestination(script, address);

TxoutType type_ret;
std::vector<CTxDestination> addresses;
int required_ret;
(void)ExtractDestinations(script, type_ret, addresses, required_ret);

const FlatSigningProvider signing_provider;
(void)InferDescriptor(script, signing_provider);

Expand Down
66 changes: 0 additions & 66 deletions src/test/script_standard_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -238,72 +238,6 @@ BOOST_AUTO_TEST_CASE(script_standard_ExtractDestination)
BOOST_CHECK(boost::get<WitnessUnknown>(&address) && *boost::get<WitnessUnknown>(&address) == unk);
}

BOOST_AUTO_TEST_CASE(script_standard_ExtractDestinations)
{
CKey keys[3];
CPubKey pubkeys[3];
for (int i = 0; i < 3; i++) {
keys[i].MakeNewKey(true);
pubkeys[i] = keys[i].GetPubKey();
}

CScript s;
TxoutType whichType;
std::vector<CTxDestination> addresses;
int nRequired;

// TxoutType::PUBKEY
s.clear();
s << ToByteVector(pubkeys[0]) << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEY);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));

// TxoutType::PUBKEYHASH
s.clear();
s << OP_DUP << OP_HASH160 << ToByteVector(pubkeys[0].GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
BOOST_CHECK_EQUAL(whichType, TxoutType::PUBKEYHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));

// TxoutType::SCRIPTHASH
CScript redeemScript(s); // initialize with leftover P2PKH script
s.clear();
s << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
BOOST_CHECK_EQUAL(whichType, TxoutType::SCRIPTHASH);
BOOST_CHECK_EQUAL(addresses.size(), 1U);
BOOST_CHECK_EQUAL(nRequired, 1);
BOOST_CHECK(boost::get<ScriptHash>(&addresses[0]) &&
*boost::get<ScriptHash>(&addresses[0]) == ScriptHash(redeemScript));

// TxoutType::MULTISIG
s.clear();
s << OP_2 <<
ToByteVector(pubkeys[0]) <<
ToByteVector(pubkeys[1]) <<
OP_2 << OP_CHECKMULTISIG;
BOOST_CHECK(ExtractDestinations(s, whichType, addresses, nRequired));
BOOST_CHECK_EQUAL(whichType, TxoutType::MULTISIG);
BOOST_CHECK_EQUAL(addresses.size(), 2U);
BOOST_CHECK_EQUAL(nRequired, 2);
BOOST_CHECK(boost::get<PKHash>(&addresses[0]) &&
*boost::get<PKHash>(&addresses[0]) == PKHash(pubkeys[0]));
BOOST_CHECK(boost::get<PKHash>(&addresses[1]) &&
*boost::get<PKHash>(&addresses[1]) == PKHash(pubkeys[1]));

// TxoutType::NULL_DATA
s.clear();
s << OP_RETURN << std::vector<unsigned char>({75});
BOOST_CHECK(!ExtractDestinations(s, whichType, addresses, nRequired));
}

BOOST_AUTO_TEST_CASE(script_standard_GetScriptFor_)
{
CKey keys[3];
Expand Down
16 changes: 1 addition & 15 deletions src/wallet/rpcwallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3672,16 +3672,6 @@ class DescribeWalletAddressVisitor : public boost::static_visitor<UniValue>
// Always report the pubkey at the top level, so that `getnewaddress()['pubkey']` always works.
if (subobj.exists("pubkey")) obj.pushKV("pubkey", subobj["pubkey"]);
obj.pushKV("embedded", std::move(subobj));
} else if (which_type == TxoutType::MULTISIG) {
// Also report some information on multisig scripts (which do not have a corresponding address).
// TODO: abstract out the common functionality between this logic and ExtractDestinations.
obj.pushKV("sigsrequired", solutions_data[0][0]);
UniValue pubkeys(UniValue::VARR);
for (size_t i = 1; i < solutions_data.size() - 1; ++i) {
CPubKey key(solutions_data[i].begin(), solutions_data[i].end());
pubkeys.push_back(HexStr(key));
}
obj.pushKV("pubkeys", std::move(pubkeys));
}
}

Expand Down Expand Up @@ -3789,11 +3779,7 @@ RPCHelpMan getaddressinfo()
"types: nonstandard, pubkey, pubkeyhash, scripthash, multisig, nulldata, witness_v0_keyhash,\n"
"witness_v0_scripthash, witness_unknown."},
{RPCResult::Type::STR_HEX, "hex", /* optional */ true, "The redeemscript for the p2sh address."},
{RPCResult::Type::ARR, "pubkeys", /* optional */ true, "Array of pubkeys associated with the known redeemscript (only if script is multisig).",
{
{RPCResult::Type::STR, "pubkey", ""},
}},
{RPCResult::Type::NUM, "sigsrequired", /* optional */ true, "The number of signatures required to spend multisig output (only if script is multisig)."},
{RPCResult::Type::STR, "pubkey", ""},
{RPCResult::Type::STR_HEX, "pubkey", /* optional */ true, "The hex value of the raw public key for single-key addresses (possibly embedded in P2SH or P2WSH)."},
{RPCResult::Type::OBJ, "embedded", /* optional */ true, "Information about the address embedded in P2SH or P2WSH, if relevant and known.",
{
Expand Down
7 changes: 1 addition & 6 deletions test/functional/feature_rbf.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=DUMMY_P2WPKH_SCRIPT):
txid = node.sendtoaddress(new_addr, satoshi_round((amount+fee)/COIN))
tx1 = node.getrawtransaction(txid, 1)
txid = int(txid, 16)
i = None

for i, txout in enumerate(tx1['vout']):
if txout['scriptPubKey']['addresses'] == [new_addr]:
break
assert i is not None
i, _ = next(filter(lambda vout: new_addr == vout[1]['scriptPubKey']['address'], enumerate(tx1['vout'])))

tx2 = CTransaction()
tx2.vin = [CTxIn(COutPoint(txid, i))]
Expand Down
2 changes: 1 addition & 1 deletion test/functional/feature_segwit.py
Original file line number Diff line number Diff line change
Expand Up @@ -523,7 +523,7 @@ def run_test(self):
v1_addr = program_to_witness(1, [3, 5])
v1_tx = self.nodes[0].createrawtransaction([getutxo(spendable_txid[0])], {v1_addr: 1})
v1_decoded = self.nodes[1].decoderawtransaction(v1_tx)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['addresses'][0], v1_addr)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['address'], v1_addr)
assert_equal(v1_decoded['vout'][0]['scriptPubKey']['hex'], "51020305")

# Check that spendable outputs are really spendable
Expand Down
2 changes: 1 addition & 1 deletion test/functional/mempool_package_onemore.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ def run_test(self):
self.chain_transaction(self.nodes[0], [second_chain], [0], second_chain_value, fee, 1)

# Make sure we can RBF the chain which used our carve-out rule
second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['addresses'][0]: replacable_orig_value - (Decimal(1) / Decimal(100))}
second_tx_outputs = {self.nodes[0].getrawtransaction(replacable_txid, True)["vout"][0]['scriptPubKey']['address']: replacable_orig_value - (Decimal(1) / Decimal(100))}
second_tx = self.nodes[0].createrawtransaction([{'txid': chain[0][0], 'vout': 1}], second_tx_outputs)
signed_second_tx = self.nodes[0].signrawtransactionwithwallet(second_tx)
self.nodes[0].sendrawtransaction(signed_second_tx['hex'])
Expand Down
6 changes: 3 additions & 3 deletions test/functional/p2p_filter.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ def test_msg_mempool(self):
filter_peer = P2PBloomFilter()

self.log.debug("Create a tx relevant to the peer before connecting")
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']
txid = self.nodes[0].sendtoaddress(filter_address, 90)

self.log.debug("Send a mempool msg after connecting and check that the tx is received")
Expand All @@ -137,7 +137,7 @@ def test_msg_mempool(self):
def test_frelay_false(self, filter_peer):
self.log.info("Check that a node with fRelay set to false does not receive invs until the filter is set")
filter_peer.tx_received = False
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']
self.nodes[0].sendtoaddress(filter_address, 90)
# Sync to make sure the reason filter_peer doesn't receive the tx is not p2p delays
filter_peer.sync_with_ping()
Expand All @@ -151,7 +151,7 @@ def test_filter(self, filter_peer):
filter_peer.send_and_ping(filter_peer.watch_filter_init)
# If fRelay is not already True, sending filterload sets it to True
assert self.nodes[0].getpeerinfo()[0]['relaytxes']
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['addresses'][0]
filter_address = self.nodes[0].decodescript(filter_peer.watch_script_pubkey)['address']

self.log.info('Check that we receive merkleblock and tx if the filter matches a tx in a block')
block_hash = self.nodes[0].generatetoaddress(1, filter_address)[0]
Expand Down
2 changes: 1 addition & 1 deletion test/functional/rpc_createmultisig.py
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ def do_multisig(self):
txid = node0.sendtoaddress(madd, 40)

tx = node0.getrawtransaction(txid, True)
vout = [v["n"] for v in tx["vout"] if madd in v["scriptPubKey"].get("addresses", [])]
vout = [v["n"] for v in tx["vout"] if madd == v["scriptPubKey"]["address"]]
assert len(vout) == 1
vout = vout[0]
scriptPubKey = tx["vout"][vout]["scriptPubKey"]["hex"]
Expand Down
Loading