Skip to content

Commit 2726abd

Browse files
l0rincRaimo33
andcommitted
refactor: make script Solver's solutions parameter optional
Make the `vSolutionsRet` parameter of `Solver()` nullable pointer allowing callers that only need the return value to avoid allocating and populating the solutions vector. Benchmark results show a 25.0% performance improvement (4,240,283 to 5,300,923 operations per second) for callers that don't need solutions. This approach addresses the performance concern raised in #33645 more fundamentally than the vector reuse optimization, while simplifying the call sites at the same time. Co-authored-by: Raimo33 <[email protected]>
1 parent 832a576 commit 2726abd

File tree

17 files changed

+90
-96
lines changed

17 files changed

+90
-96
lines changed

src/addresstype.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ WitnessV0ScriptHash::WitnessV0ScriptHash(const CScript& in)
4949
bool ExtractDestination(const CScript& scriptPubKey, CTxDestination& addressRet)
5050
{
5151
std::vector<valtype> vSolutions;
52-
TxoutType whichType = Solver(scriptPubKey, vSolutions);
52+
TxoutType whichType = Solver(scriptPubKey, &vSolutions);
5353

5454
switch (whichType) {
5555
case TxoutType::PUBKEY: {

src/common/bloom.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -123,8 +123,7 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx)
123123
insert(COutPoint(hash, i));
124124
else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY)
125125
{
126-
std::vector<std::vector<unsigned char> > vSolutions;
127-
TxoutType type = Solver(txout.scriptPubKey, vSolutions);
126+
TxoutType type = Solver(txout.scriptPubKey);
128127
if (type == TxoutType::PUBKEY || type == TxoutType::MULTISIG) {
129128
insert(COutPoint(hash, i));
130129
}

src/core_write.cpp

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,8 +159,7 @@ void ScriptToUniv(const CScript& script, UniValue& out, bool include_hex, bool i
159159
out.pushKV("hex", HexStr(script));
160160
}
161161

162-
std::vector<std::vector<unsigned char>> solns;
163-
const TxoutType type{Solver(script, solns)};
162+
const TxoutType type{Solver(script)};
164163

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

src/policy/policy.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ std::vector<uint32_t> GetDust(const CTransaction& tx, CFeeRate dust_relay_rate)
7979
bool IsStandard(const CScript& scriptPubKey, TxoutType& whichType)
8080
{
8181
std::vector<std::vector<unsigned char> > vSolutions;
82-
whichType = Solver(scriptPubKey, vSolutions);
82+
whichType = Solver(scriptPubKey, &vSolutions);
8383

8484
if (whichType == TxoutType::NONSTANDARD) {
8585
return false;
@@ -223,8 +223,8 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
223223
for (unsigned int i = 0; i < tx.vin.size(); i++) {
224224
const CTxOut& prev = mapInputs.AccessCoin(tx.vin[i].prevout).out;
225225

226-
std::vector<std::vector<unsigned char> > vSolutions;
227-
TxoutType whichType = Solver(prev.scriptPubKey, vSolutions);
226+
std::vector<std::vector<unsigned char>> vSolutions;
227+
TxoutType whichType = Solver(prev.scriptPubKey, &vSolutions);
228228
if (whichType == TxoutType::NONSTANDARD || whichType == TxoutType::WITNESS_UNKNOWN) {
229229
// WITNESS_UNKNOWN failures are typically also caught with a policy
230230
// flag in the script interpreter, but it can be helpful to catch

src/rpc/rawtransaction.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ static RPCHelpMan decodescript()
534534
ScriptToUniv(script, /*out=*/r, /*include_hex=*/false, /*include_address=*/true);
535535

536536
std::vector<std::vector<unsigned char>> solutions_data;
537-
const TxoutType which_type{Solver(script, solutions_data)};
537+
const TxoutType which_type{Solver(script, &solutions_data)};
538538

539539
const bool can_wrap{[&] {
540540
switch (which_type) {

src/script/descriptor.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2564,7 +2564,7 @@ std::unique_ptr<DescriptorImpl> InferScript(const CScript& script, ParseScriptCo
25642564
}
25652565

25662566
std::vector<std::vector<unsigned char>> data;
2567-
TxoutType txntype = Solver(script, data);
2567+
TxoutType txntype = Solver(script, &data);
25682568

25692569
if (txntype == TxoutType::PUBKEY && (ctx == ParseScriptContext::TOP || ctx == ParseScriptContext::P2SH || ctx == ParseScriptContext::P2WSH)) {
25702570
CPubKey pubkey(data[0]);

src/script/sign.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ static bool SignStep(const SigningProvider& provider, const BaseSignatureCreator
635635
std::vector<unsigned char> sig;
636636

637637
std::vector<valtype> vSolutions;
638-
whichTypeRet = Solver(scriptPubKey, vSolutions);
638+
whichTypeRet = Solver(scriptPubKey, &vSolutions);
639639

640640
switch (whichTypeRet) {
641641
case TxoutType::NONSTANDARD:
@@ -854,7 +854,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
854854

855855
// Get scripts
856856
std::vector<std::vector<unsigned char>> solutions;
857-
TxoutType script_type = Solver(txout.scriptPubKey, solutions);
857+
TxoutType script_type = Solver(txout.scriptPubKey, &solutions);
858858
SigVersion sigversion = SigVersion::BASE;
859859
CScript next_script = txout.scriptPubKey;
860860

@@ -865,7 +865,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
865865
next_script = std::move(redeem_script);
866866

867867
// Get redeemScript type
868-
script_type = Solver(next_script, solutions);
868+
script_type = Solver(next_script, &solutions);
869869
stack.script.pop_back();
870870
}
871871
if (script_type == TxoutType::WITNESS_V0_SCRIPTHASH && !stack.witness.empty() && !stack.witness.back().empty()) {
@@ -875,7 +875,7 @@ SignatureData DataFromTransaction(const CMutableTransaction& tx, unsigned int nI
875875
next_script = std::move(witness_script);
876876

877877
// Get witnessScript type
878-
script_type = Solver(next_script, solutions);
878+
script_type = Solver(next_script, &solutions);
879879
stack.witness.pop_back();
880880
stack.script = std::move(stack.witness);
881881
stack.witness.clear();
@@ -996,7 +996,7 @@ bool IsSegWitOutput(const SigningProvider& provider, const CScript& script)
996996
if (script.IsWitnessProgram(version, program)) return true;
997997
if (script.IsPayToScriptHash()) {
998998
std::vector<valtype> solutions;
999-
auto whichtype = Solver(script, solutions);
999+
auto whichtype = Solver(script, &solutions);
10001000
if (whichtype == TxoutType::SCRIPTHASH) {
10011001
auto h160 = uint160(solutions[0]);
10021002
CScript subscript;

src/script/solver.cpp

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -138,40 +138,41 @@ std::optional<std::pair<int, std::vector<std::span<const unsigned char>>>> Match
138138
return std::pair{*threshold, std::move(keyspans)};
139139
}
140140

141-
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet)
141+
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>* vSolutionsRet)
142142
{
143-
vSolutionsRet.clear();
143+
if (vSolutionsRet) vSolutionsRet->clear();
144144

145145
// Shortcut for pay-to-script-hash, which are more constrained than the other types:
146146
// it is always OP_HASH160 20 [20 byte hash] OP_EQUAL
147-
if (scriptPubKey.IsPayToScriptHash())
148-
{
147+
if (scriptPubKey.IsPayToScriptHash()) {
149148
std::vector<unsigned char> hashBytes(scriptPubKey.begin()+2, scriptPubKey.begin()+22);
150-
vSolutionsRet.push_back(hashBytes);
149+
if (vSolutionsRet) vSolutionsRet->push_back(hashBytes);
151150
return TxoutType::SCRIPTHASH;
152151
}
153152

154153
int witnessversion;
155154
std::vector<unsigned char> witnessprogram;
156155
if (scriptPubKey.IsWitnessProgram(witnessversion, witnessprogram)) {
157156
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_KEYHASH_SIZE) {
158-
vSolutionsRet.push_back(std::move(witnessprogram));
157+
if (vSolutionsRet) vSolutionsRet->push_back(std::move(witnessprogram));
159158
return TxoutType::WITNESS_V0_KEYHASH;
160159
}
161160
if (witnessversion == 0 && witnessprogram.size() == WITNESS_V0_SCRIPTHASH_SIZE) {
162-
vSolutionsRet.push_back(std::move(witnessprogram));
161+
if (vSolutionsRet) vSolutionsRet->push_back(std::move(witnessprogram));
163162
return TxoutType::WITNESS_V0_SCRIPTHASH;
164163
}
165164
if (witnessversion == 1 && witnessprogram.size() == WITNESS_V1_TAPROOT_SIZE) {
166-
vSolutionsRet.push_back(std::move(witnessprogram));
165+
if (vSolutionsRet) vSolutionsRet->push_back(std::move(witnessprogram));
167166
return TxoutType::WITNESS_V1_TAPROOT;
168167
}
169168
if (scriptPubKey.IsPayToAnchor()) {
170169
return TxoutType::ANCHOR;
171170
}
172171
if (witnessversion != 0) {
173-
vSolutionsRet.push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174-
vSolutionsRet.push_back(std::move(witnessprogram));
172+
if (vSolutionsRet) {
173+
vSolutionsRet->push_back(std::vector<unsigned char>{(unsigned char)witnessversion});
174+
vSolutionsRet->push_back(std::move(witnessprogram));
175+
}
175176
return TxoutType::WITNESS_UNKNOWN;
176177
}
177178
return TxoutType::NONSTANDARD;
@@ -188,25 +189,27 @@ TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned c
188189

189190
std::vector<unsigned char> data;
190191
if (MatchPayToPubkey(scriptPubKey, data)) {
191-
vSolutionsRet.push_back(std::move(data));
192+
if (vSolutionsRet) vSolutionsRet->push_back(std::move(data));
192193
return TxoutType::PUBKEY;
193194
}
194195

195196
if (MatchPayToPubkeyHash(scriptPubKey, data)) {
196-
vSolutionsRet.push_back(std::move(data));
197+
if (vSolutionsRet) vSolutionsRet->push_back(std::move(data));
197198
return TxoutType::PUBKEYHASH;
198199
}
199200

200201
int required;
201202
std::vector<std::vector<unsigned char>> keys;
202203
if (MatchMultisig(scriptPubKey, required, keys)) {
203-
vSolutionsRet.push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
204-
vSolutionsRet.insert(vSolutionsRet.end(), keys.begin(), keys.end());
205-
vSolutionsRet.push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
204+
if (vSolutionsRet) {
205+
vSolutionsRet->push_back({static_cast<unsigned char>(required)}); // safe as required is in range 1..20
206+
vSolutionsRet->insert(vSolutionsRet->end(), keys.begin(), keys.end());
207+
vSolutionsRet->push_back({static_cast<unsigned char>(keys.size())}); // safe as size is in range 1..20
208+
}
206209
return TxoutType::MULTISIG;
207210
}
208211

209-
vSolutionsRet.clear();
212+
if (vSolutionsRet) vSolutionsRet->clear();
210213
return TxoutType::NONSTANDARD;
211214
}
212215

src/script/solver.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ constexpr bool IsPushdataOp(opcodetype opcode)
5252
* @param[out] vSolutionsRet Vector of parsed pubkeys and hashes
5353
* @return The script type. TxoutType::NONSTANDARD represents a failed solve.
5454
*/
55-
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>& vSolutionsRet);
55+
TxoutType Solver(const CScript& scriptPubKey, std::vector<std::vector<unsigned char>>* vSolutionsRet = nullptr);
5656

5757
/** Generate a P2PK script for the given pubkey. */
5858
CScript GetScriptForRawPubKey(const CPubKey& pubkey);

src/test/fuzz/key.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -160,13 +160,13 @@ FUZZ_TARGET(key, .init = initialize_key)
160160
assert(which_type_tx_multisig == TxoutType::MULTISIG);
161161

162162
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_pubkey;
163-
const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, v_solutions_ret_tx_pubkey);
163+
const TxoutType outtype_tx_pubkey = Solver(tx_pubkey_script, &v_solutions_ret_tx_pubkey);
164164
assert(outtype_tx_pubkey == TxoutType::PUBKEY);
165165
assert(v_solutions_ret_tx_pubkey.size() == 1);
166166
assert(v_solutions_ret_tx_pubkey[0].size() == 33);
167167

168168
std::vector<std::vector<unsigned char>> v_solutions_ret_tx_multisig;
169-
const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, v_solutions_ret_tx_multisig);
169+
const TxoutType outtype_tx_multisig = Solver(tx_multisig_script, &v_solutions_ret_tx_multisig);
170170
assert(outtype_tx_multisig == TxoutType::MULTISIG);
171171
assert(v_solutions_ret_tx_multisig.size() == 3);
172172
assert(v_solutions_ret_tx_multisig[0].size() == 1);

0 commit comments

Comments
 (0)