Skip to content

Commit 83a425d

Browse files
committed
compressor: use a prevector in compressed script serialization
Use a prevector for stack allocation instead of heap allocation during script compression and decompression. These functions were doing millions of unnecessary heap allocations during IBD. We introduce a CompressedScript type alias for this prevector. It is size 33 as that is the maximum size of a compressed script. Fix the DecompressScript header to match the variable name from compressor.cpp Signed-off-by: William Casarin <[email protected]>
1 parent 844d207 commit 83a425d

File tree

4 files changed

+27
-13
lines changed

4 files changed

+27
-13
lines changed

src/compressor.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ static bool IsToPubKey(const CScript& script, CPubKey &pubkey)
5252
return false;
5353
}
5454

55-
bool CompressScript(const CScript& script, std::vector<unsigned char> &out)
55+
bool CompressScript(const CScript& script, CompressedScript& out)
5656
{
5757
CKeyID keyID;
5858
if (IsToKeyID(script, keyID)) {
@@ -92,7 +92,7 @@ unsigned int GetSpecialScriptSize(unsigned int nSize)
9292
return 0;
9393
}
9494

95-
bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &in)
95+
bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in)
9696
{
9797
switch(nSize) {
9898
case 0x00:

src/compressor.h

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,26 @@
66
#ifndef BITCOIN_COMPRESSOR_H
77
#define BITCOIN_COMPRESSOR_H
88

9+
#include <prevector.h>
910
#include <primitives/transaction.h>
1011
#include <script/script.h>
1112
#include <serialize.h>
1213
#include <span.h>
1314

14-
bool CompressScript(const CScript& script, std::vector<unsigned char> &out);
15+
/**
16+
* This saves us from making many heap allocations when serializing
17+
* and deserializing compressed scripts.
18+
*
19+
* This prevector size is determined by the largest .resize() in the
20+
* CompressScript function. The largest compressed script format is a
21+
* compressed public key, which is 33 bytes.
22+
*/
23+
using CompressedScript = prevector<33, unsigned char>;
24+
25+
26+
bool CompressScript(const CScript& script, CompressedScript& out);
1527
unsigned int GetSpecialScriptSize(unsigned int nSize);
16-
bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out);
28+
bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in);
1729

1830
/**
1931
* Compress amount.
@@ -51,7 +63,7 @@ struct ScriptCompression
5163

5264
template<typename Stream>
5365
void Ser(Stream &s, const CScript& script) {
54-
std::vector<unsigned char> compr;
66+
CompressedScript compr;
5567
if (CompressScript(script, compr)) {
5668
s << MakeSpan(compr);
5769
return;
@@ -66,7 +78,7 @@ struct ScriptCompression
6678
unsigned int nSize = 0;
6779
s >> VARINT(nSize);
6880
if (nSize < nSpecialScripts) {
69-
std::vector<unsigned char> vch(GetSpecialScriptSize(nSize), 0x00);
81+
CompressedScript vch(GetSpecialScriptSize(nSize), 0x00);
7082
s >> MakeSpan(vch);
7183
DecompressScript(script, nSize, vch);
7284
return;

src/test/compress_tests.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_ckey_id)
7272
CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
7373
BOOST_CHECK_EQUAL(script.size(), 25);
7474

75-
std::vector<unsigned char> out;
75+
CompressedScript out;
7676
bool done = CompressScript(script, out);
7777
BOOST_CHECK_EQUAL(done, true);
7878

@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id)
8989
script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
9090
BOOST_CHECK_EQUAL(script.size(), 23);
9191

92-
std::vector<unsigned char> out;
92+
CompressedScript out;
9393
bool done = CompressScript(script, out);
9494
BOOST_CHECK_EQUAL(done, true);
9595

@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id)
107107
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33)
108108
BOOST_CHECK_EQUAL(script.size(), 35);
109109

110-
std::vector<unsigned char> out;
110+
CompressedScript out;
111111
bool done = CompressScript(script, out);
112112
BOOST_CHECK_EQUAL(done, true);
113113

@@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
124124
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65)
125125
BOOST_CHECK_EQUAL(script.size(), 67); // 1 char code + 65 char pubkey + OP_CHECKSIG
126126

127-
std::vector<unsigned char> out;
127+
CompressedScript out;
128128
bool done = CompressScript(script, out);
129129
BOOST_CHECK_EQUAL(done, true);
130130

src/test/fuzz/script.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
3636
if (!script_opt) return;
3737
const CScript script{*script_opt};
3838

39-
std::vector<unsigned char> compressed;
39+
CompressedScript compressed;
4040
if (CompressScript(script, compressed)) {
4141
const unsigned int size = compressed[0];
4242
compressed.erase(compressed.begin());
@@ -94,10 +94,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
9494

9595
{
9696
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
97+
CompressedScript compressed_script;
98+
compressed_script.assign(bytes.begin(), bytes.end());
9799
// DecompressScript(..., ..., bytes) is not guaranteed to be defined if the bytes vector is too short
98-
if (bytes.size() >= 32) {
100+
if (compressed_script.size() >= 32) {
99101
CScript decompressed_script;
100-
DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), bytes);
102+
DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), compressed_script);
101103
}
102104
}
103105

0 commit comments

Comments
 (0)