mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-08-03 22:53:19 +02:00
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 <jb55@jb55.com>
This commit is contained in:
@@ -52,7 +52,7 @@ static bool IsToPubKey(const CScript& script, CPubKey &pubkey)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CompressScript(const CScript& script, std::vector<unsigned char> &out)
|
bool CompressScript(const CScript& script, CompressedScript& out)
|
||||||
{
|
{
|
||||||
CKeyID keyID;
|
CKeyID keyID;
|
||||||
if (IsToKeyID(script, keyID)) {
|
if (IsToKeyID(script, keyID)) {
|
||||||
@@ -92,7 +92,7 @@ unsigned int GetSpecialScriptSize(unsigned int nSize)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &in)
|
bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in)
|
||||||
{
|
{
|
||||||
switch(nSize) {
|
switch(nSize) {
|
||||||
case 0x00:
|
case 0x00:
|
||||||
|
@@ -6,14 +6,26 @@
|
|||||||
#ifndef BITCOIN_COMPRESSOR_H
|
#ifndef BITCOIN_COMPRESSOR_H
|
||||||
#define BITCOIN_COMPRESSOR_H
|
#define BITCOIN_COMPRESSOR_H
|
||||||
|
|
||||||
|
#include <prevector.h>
|
||||||
#include <primitives/transaction.h>
|
#include <primitives/transaction.h>
|
||||||
#include <script/script.h>
|
#include <script/script.h>
|
||||||
#include <serialize.h>
|
#include <serialize.h>
|
||||||
#include <span.h>
|
#include <span.h>
|
||||||
|
|
||||||
bool CompressScript(const CScript& script, std::vector<unsigned char> &out);
|
/**
|
||||||
|
* This saves us from making many heap allocations when serializing
|
||||||
|
* and deserializing compressed scripts.
|
||||||
|
*
|
||||||
|
* This prevector size is determined by the largest .resize() in the
|
||||||
|
* CompressScript function. The largest compressed script format is a
|
||||||
|
* compressed public key, which is 33 bytes.
|
||||||
|
*/
|
||||||
|
using CompressedScript = prevector<33, unsigned char>;
|
||||||
|
|
||||||
|
|
||||||
|
bool CompressScript(const CScript& script, CompressedScript& out);
|
||||||
unsigned int GetSpecialScriptSize(unsigned int nSize);
|
unsigned int GetSpecialScriptSize(unsigned int nSize);
|
||||||
bool DecompressScript(CScript& script, unsigned int nSize, const std::vector<unsigned char> &out);
|
bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Compress amount.
|
* Compress amount.
|
||||||
@@ -51,7 +63,7 @@ struct ScriptCompression
|
|||||||
|
|
||||||
template<typename Stream>
|
template<typename Stream>
|
||||||
void Ser(Stream &s, const CScript& script) {
|
void Ser(Stream &s, const CScript& script) {
|
||||||
std::vector<unsigned char> compr;
|
CompressedScript compr;
|
||||||
if (CompressScript(script, compr)) {
|
if (CompressScript(script, compr)) {
|
||||||
s << MakeSpan(compr);
|
s << MakeSpan(compr);
|
||||||
return;
|
return;
|
||||||
@@ -66,7 +78,7 @@ struct ScriptCompression
|
|||||||
unsigned int nSize = 0;
|
unsigned int nSize = 0;
|
||||||
s >> VARINT(nSize);
|
s >> VARINT(nSize);
|
||||||
if (nSize < nSpecialScripts) {
|
if (nSize < nSpecialScripts) {
|
||||||
std::vector<unsigned char> vch(GetSpecialScriptSize(nSize), 0x00);
|
CompressedScript vch(GetSpecialScriptSize(nSize), 0x00);
|
||||||
s >> MakeSpan(vch);
|
s >> MakeSpan(vch);
|
||||||
DecompressScript(script, nSize, vch);
|
DecompressScript(script, nSize, vch);
|
||||||
return;
|
return;
|
||||||
|
@@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_ckey_id)
|
|||||||
CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
CScript script = CScript() << OP_DUP << OP_HASH160 << ToByteVector(pubkey.GetID()) << OP_EQUALVERIFY << OP_CHECKSIG;
|
||||||
BOOST_CHECK_EQUAL(script.size(), 25);
|
BOOST_CHECK_EQUAL(script.size(), 25);
|
||||||
|
|
||||||
std::vector<unsigned char> out;
|
CompressedScript out;
|
||||||
bool done = CompressScript(script, out);
|
bool done = CompressScript(script, out);
|
||||||
BOOST_CHECK_EQUAL(done, true);
|
BOOST_CHECK_EQUAL(done, true);
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_cscript_id)
|
|||||||
script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
|
script << OP_HASH160 << ToByteVector(CScriptID(redeemScript)) << OP_EQUAL;
|
||||||
BOOST_CHECK_EQUAL(script.size(), 23);
|
BOOST_CHECK_EQUAL(script.size(), 23);
|
||||||
|
|
||||||
std::vector<unsigned char> out;
|
CompressedScript out;
|
||||||
bool done = CompressScript(script, out);
|
bool done = CompressScript(script, out);
|
||||||
BOOST_CHECK_EQUAL(done, true);
|
BOOST_CHECK_EQUAL(done, true);
|
||||||
|
|
||||||
@@ -107,7 +107,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_compressed_pubkey_id)
|
|||||||
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33)
|
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // COMPRESSED_PUBLIC_KEY_SIZE (33)
|
||||||
BOOST_CHECK_EQUAL(script.size(), 35);
|
BOOST_CHECK_EQUAL(script.size(), 35);
|
||||||
|
|
||||||
std::vector<unsigned char> out;
|
CompressedScript out;
|
||||||
bool done = CompressScript(script, out);
|
bool done = CompressScript(script, out);
|
||||||
BOOST_CHECK_EQUAL(done, true);
|
BOOST_CHECK_EQUAL(done, true);
|
||||||
|
|
||||||
@@ -124,7 +124,7 @@ BOOST_AUTO_TEST_CASE(compress_script_to_uncompressed_pubkey_id)
|
|||||||
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65)
|
CScript script = CScript() << ToByteVector(key.GetPubKey()) << OP_CHECKSIG; // PUBLIC_KEY_SIZE (65)
|
||||||
BOOST_CHECK_EQUAL(script.size(), 67); // 1 char code + 65 char pubkey + OP_CHECKSIG
|
BOOST_CHECK_EQUAL(script.size(), 67); // 1 char code + 65 char pubkey + OP_CHECKSIG
|
||||||
|
|
||||||
std::vector<unsigned char> out;
|
CompressedScript out;
|
||||||
bool done = CompressScript(script, out);
|
bool done = CompressScript(script, out);
|
||||||
BOOST_CHECK_EQUAL(done, true);
|
BOOST_CHECK_EQUAL(done, true);
|
||||||
|
|
||||||
|
@@ -36,7 +36,7 @@ void test_one_input(const std::vector<uint8_t>& buffer)
|
|||||||
if (!script_opt) return;
|
if (!script_opt) return;
|
||||||
const CScript script{*script_opt};
|
const CScript script{*script_opt};
|
||||||
|
|
||||||
std::vector<unsigned char> compressed;
|
CompressedScript compressed;
|
||||||
if (CompressScript(script, compressed)) {
|
if (CompressScript(script, compressed)) {
|
||||||
const unsigned int size = compressed[0];
|
const unsigned int size = compressed[0];
|
||||||
compressed.erase(compressed.begin());
|
compressed.erase(compressed.begin());
|
||||||
@@ -94,10 +94,12 @@ void test_one_input(const std::vector<uint8_t>& buffer)
|
|||||||
|
|
||||||
{
|
{
|
||||||
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
|
const std::vector<uint8_t> bytes = ConsumeRandomLengthByteVector(fuzzed_data_provider);
|
||||||
|
CompressedScript compressed_script;
|
||||||
|
compressed_script.assign(bytes.begin(), bytes.end());
|
||||||
// DecompressScript(..., ..., bytes) is not guaranteed to be defined if the bytes vector is too short
|
// DecompressScript(..., ..., bytes) is not guaranteed to be defined if the bytes vector is too short
|
||||||
if (bytes.size() >= 32) {
|
if (compressed_script.size() >= 32) {
|
||||||
CScript decompressed_script;
|
CScript decompressed_script;
|
||||||
DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), bytes);
|
DecompressScript(decompressed_script, fuzzed_data_provider.ConsumeIntegral<unsigned int>(), compressed_script);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user