Skip to content
Merged
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
6 changes: 3 additions & 3 deletions src/bench/checkqueue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
#include <key.h>
#include <prevector.h>
#include <random.h>
#include <script/script.h>

#include <cstddef>
#include <cstdint>
Expand All @@ -16,7 +17,6 @@

static const size_t BATCHES = 101;
static const size_t BATCH_SIZE = 30;
static const int PREVECTOR_SIZE = 28;
static const unsigned int QUEUE_BATCH_SIZE = 128;

// This Benchmark tests the CheckQueue with a slightly realistic workload,
Expand All @@ -30,9 +30,9 @@ static void CCheckQueueSpeedPrevectorJob(benchmark::Bench& bench)
ECC_Context ecc_context{};

struct PrevectorJob {
prevector<PREVECTOR_SIZE, uint8_t> p;
prevector<CScriptBase::STATIC_SIZE, uint8_t> p;
explicit PrevectorJob(FastRandomContext& insecure_rand){
p.resize(insecure_rand.randrange(PREVECTOR_SIZE*2));
p.resize(insecure_rand.randrange(CScriptBase::STATIC_SIZE * 2));
}
std::optional<int> operator()()
{
Expand Down
41 changes: 22 additions & 19 deletions src/bench/prevector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,20 @@
#include <prevector.h>

#include <bench/bench.h>
#include <script/script.h>
#include <serialize.h>
#include <streams.h>

#include <type_traits>
#include <vector>

struct nontrivial_t {
struct nontrivial_t
{
int x{-1};
nontrivial_t() = default;
SERIALIZE_METHODS(nontrivial_t, obj) { READWRITE(obj.x); }
};

static_assert(!std::is_trivially_default_constructible_v<nontrivial_t>,
"expected nontrivial_t to not be trivially constructible");

Expand All @@ -27,35 +30,35 @@ template <typename T>
static void PrevectorDestructor(benchmark::Bench& bench)
{
bench.batch(2).run([&] {
prevector<28, T> t0;
prevector<28, T> t1;
t0.resize(28);
t1.resize(29);
prevector<CScriptBase::STATIC_SIZE, T> t0;
prevector<CScriptBase::STATIC_SIZE, T> t1;
t0.resize(CScriptBase::STATIC_SIZE);
t1.resize(CScriptBase::STATIC_SIZE + 1);
});
}

template <typename T>
static void PrevectorClear(benchmark::Bench& bench)
{
prevector<28, T> t0;
prevector<28, T> t1;
prevector<CScriptBase::STATIC_SIZE, T> t0;
prevector<CScriptBase::STATIC_SIZE, T> t1;
bench.batch(2).run([&] {
t0.resize(28);
t0.resize(CScriptBase::STATIC_SIZE);
t0.clear();
t1.resize(29);
t1.resize(CScriptBase::STATIC_SIZE + 1);
t1.clear();
});
}

template <typename T>
static void PrevectorResize(benchmark::Bench& bench)
{
prevector<28, T> t0;
prevector<28, T> t1;
prevector<CScriptBase::STATIC_SIZE, T> t0;
prevector<CScriptBase::STATIC_SIZE, T> t1;
bench.batch(4).run([&] {
t0.resize(28);
t0.resize(CScriptBase::STATIC_SIZE);
t0.resize(0);
t1.resize(29);
t1.resize(CScriptBase::STATIC_SIZE + 1);
t1.resize(0);
});
}
Expand All @@ -64,8 +67,8 @@ template <typename T>
static void PrevectorDeserialize(benchmark::Bench& bench)
{
DataStream s0{};
prevector<28, T> t0;
t0.resize(28);
prevector<CScriptBase::STATIC_SIZE, T> t0;
t0.resize(CScriptBase::STATIC_SIZE);
for (auto x = 0; x < 900; ++x) {
s0 << t0;
}
Expand All @@ -74,7 +77,7 @@ static void PrevectorDeserialize(benchmark::Bench& bench)
s0 << t0;
}
bench.batch(1000).run([&] {
prevector<28, T> t1;
prevector<CScriptBase::STATIC_SIZE, T> t1;
for (auto x = 0; x < 1000; ++x) {
s0 >> t1;
}
Expand All @@ -86,7 +89,7 @@ template <typename T>
static void PrevectorFillVectorDirect(benchmark::Bench& bench)
{
bench.run([&] {
std::vector<prevector<28, T>> vec;
std::vector<prevector<CScriptBase::STATIC_SIZE, T>> vec;
vec.reserve(260);
for (size_t i = 0; i < 260; ++i) {
vec.emplace_back();
Expand All @@ -99,11 +102,11 @@ template <typename T>
static void PrevectorFillVectorIndirect(benchmark::Bench& bench)
{
bench.run([&] {
std::vector<prevector<28, T>> vec;
std::vector<prevector<CScriptBase::STATIC_SIZE, T>> vec;
vec.reserve(260);
for (size_t i = 0; i < 260; ++i) {
// force allocation
vec.emplace_back(29, T{});
vec.emplace_back(CScriptBase::STATIC_SIZE + 1, T{});
}
});
}
Expand Down
2 changes: 2 additions & 0 deletions src/prevector.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class prevector {
static_assert(std::is_trivially_copyable_v<T>);

public:
static constexpr unsigned int STATIC_SIZE{N};

typedef Size size_type;
typedef Diff difference_type;
typedef T value_type;
Expand Down
4 changes: 1 addition & 3 deletions src/script/script.h
Original file line number Diff line number Diff line change
Expand Up @@ -403,10 +403,8 @@ class CScriptNum
/**
* We use a prevector for the script to reduce the considerable memory overhead
* of vectors in cases where they normally contain a small number of small elements.
* Tests in October 2015 showed use of this reduced dbcache memory usage by 23%
* and made an initial sync 13% faster.
*/
typedef prevector<28, unsigned char> CScriptBase;
using CScriptBase = prevector<36, uint8_t>;

bool GetScriptOp(CScriptBase::const_iterator& pc, CScriptBase::const_iterator end, opcodetype& opcodeRet, std::vector<unsigned char>* pvchRet);

Expand Down
101 changes: 101 additions & 0 deletions src/test/script_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1151,6 +1151,107 @@ BOOST_AUTO_TEST_CASE(script_CHECKMULTISIG23)
BOOST_CHECK_MESSAGE(err == SCRIPT_ERR_INVALID_STACK_OPERATION, ScriptErrorString(err));
}

/** Return the TxoutType of a script without exposing Solver details. */
static TxoutType GetTxoutType(const CScript& output_script)
{
std::vector<std::vector<uint8_t>> unused;
return Solver(output_script, unused);
}

#define CHECK_SCRIPT_STATIC_SIZE(script, expected_size) \
do { \
BOOST_CHECK_EQUAL((script).size(), (expected_size)); \
BOOST_CHECK_EQUAL((script).capacity(), CScriptBase::STATIC_SIZE); \
BOOST_CHECK_EQUAL((script).allocated_memory(), 0); \
} while (0)

#define CHECK_SCRIPT_DYNAMIC_SIZE(script, expected_size, expected_extra) \
do { \
BOOST_CHECK_EQUAL((script).size(), (expected_size)); \
BOOST_CHECK_EQUAL((script).capacity(), (expected_extra)); \
BOOST_CHECK_EQUAL((script).allocated_memory(), (expected_extra)); \
} while (0)

BOOST_AUTO_TEST_CASE(script_size_and_capacity_test)
{
BOOST_CHECK_EQUAL(sizeof(CompressedScript), 40);
BOOST_CHECK_EQUAL(sizeof(CScriptBase), 40);
BOOST_CHECK_NE(sizeof(CScriptBase), sizeof(prevector<CScriptBase::STATIC_SIZE + 1, uint8_t>)); // CScriptBase size should be set to avoid wasting space in padding
BOOST_CHECK_EQUAL(sizeof(CScript), 40);
BOOST_CHECK_EQUAL(sizeof(CTxOut), 48);

CKey dummy_key;
dummy_key.MakeNewKey(/*fCompressed=*/true);
const CPubKey dummy_pubkey{dummy_key.GetPubKey()};

// Small OP_RETURN has direct allocation
{
const auto script{CScript() << OP_RETURN << std::vector<uint8_t>(10, 0xaa)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::NULL_DATA);
CHECK_SCRIPT_STATIC_SIZE(script, 12);
}

// P2WPKH has direct allocation
{
const auto script{GetScriptForDestination(WitnessV0KeyHash{PKHash{dummy_pubkey}})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::WITNESS_V0_KEYHASH);
CHECK_SCRIPT_STATIC_SIZE(script, 22);
}

// P2SH has direct allocation
{
const auto script{GetScriptForDestination(ScriptHash{CScript{} << OP_TRUE})};
BOOST_CHECK(script.IsPayToScriptHash());
CHECK_SCRIPT_STATIC_SIZE(script, 23);
}

// P2PKH has direct allocation
{
const auto script{GetScriptForDestination(PKHash{dummy_pubkey})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEYHASH);
CHECK_SCRIPT_STATIC_SIZE(script, 25);
}

// P2WSH has direct allocation
{
const auto script{GetScriptForDestination(WitnessV0ScriptHash{CScript{} << OP_TRUE})};
BOOST_CHECK(script.IsPayToWitnessScriptHash());
CHECK_SCRIPT_STATIC_SIZE(script, 34);
}

// P2TR has direct allocation
{
const auto script{GetScriptForDestination(WitnessV1Taproot{XOnlyPubKey{dummy_pubkey}})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::WITNESS_V1_TAPROOT);
CHECK_SCRIPT_STATIC_SIZE(script, 34);
}

// Compressed P2PK has direct allocation
{
const auto script{GetScriptForRawPubKey(dummy_pubkey)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEY);
CHECK_SCRIPT_STATIC_SIZE(script, 35);
}

// Uncompressed P2PK needs extra allocation
{
CKey uncompressed_key;
uncompressed_key.MakeNewKey(/*fCompressed=*/false);
const CPubKey uncompressed_pubkey{uncompressed_key.GetPubKey()};

const auto script{GetScriptForRawPubKey(uncompressed_pubkey)};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::PUBKEY);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 67, 67);
}

// Bare multisig needs extra allocation
{
const auto script{GetScriptForMultisig(1, std::vector{2, dummy_pubkey})};
BOOST_CHECK_EQUAL(GetTxoutType(script), TxoutType::MULTISIG);
CHECK_SCRIPT_DYNAMIC_SIZE(script, 71, 103);
}
}

/* Wrapper around ProduceSignature to combine two scriptsigs */
SignatureData CombineSignatures(const CTxOut& txout, const CMutableTransaction& tx, const SignatureData& scriptSig1, const SignatureData& scriptSig2)
{
Expand Down
5 changes: 2 additions & 3 deletions src/test/validation_flush_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,8 @@ BOOST_AUTO_TEST_CASE(getcoinscachesizestate)
LOCK(::cs_main);
auto& view = chainstate.CoinsTip();

// The number of bytes consumed by coin's heap data, i.e. CScript
// (prevector<28, unsigned char>) when assigned 56 bytes of data per above.
//
// The number of bytes consumed by coin's heap data, i.e.
// CScript (prevector<36, unsigned char>) when assigned 56 bytes of data per above.
// See also: Coin::DynamicMemoryUsage().
constexpr unsigned int COIN_SIZE = is_64_bit ? 80 : 64;

Expand Down
Loading