/tmp/bitcoin/src/compressor.h
Line | Count | Source |
1 | | // Copyright (c) 2009-2010 Satoshi Nakamoto |
2 | | // Copyright (c) 2009-present The Bitcoin Core developers |
3 | | // Distributed under the MIT software license, see the accompanying |
4 | | // file COPYING or http://www.opensource.org/licenses/mit-license.php. |
5 | | |
6 | | #ifndef BITCOIN_COMPRESSOR_H |
7 | | #define BITCOIN_COMPRESSOR_H |
8 | | |
9 | | #include <prevector.h> |
10 | | #include <primitives/transaction.h> |
11 | | #include <script/script.h> |
12 | | #include <serialize.h> |
13 | | #include <span.h> |
14 | | |
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); |
27 | | unsigned int GetSpecialScriptSize(unsigned int nSize); |
28 | | bool DecompressScript(CScript& script, unsigned int nSize, const CompressedScript& in); |
29 | | |
30 | | /** |
31 | | * Compress amount. |
32 | | * |
33 | | * nAmount is of type uint64_t and thus cannot be negative. If you're passing in |
34 | | * a CAmount (int64_t), make sure to properly handle the case where the amount |
35 | | * is negative before calling CompressAmount(...). |
36 | | * |
37 | | * @pre Function defined only for 0 <= nAmount <= MAX_MONEY. |
38 | | */ |
39 | | uint64_t CompressAmount(uint64_t nAmount); |
40 | | |
41 | | uint64_t DecompressAmount(uint64_t nAmount); |
42 | | |
43 | | /** Compact serializer for scripts. |
44 | | * |
45 | | * It detects common cases and encodes them much more efficiently. |
46 | | * 3 special cases are defined: |
47 | | * * Pay to pubkey hash (encoded as 21 bytes) |
48 | | * * Pay to script hash (encoded as 21 bytes) |
49 | | * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) |
50 | | * |
51 | | * Other scripts up to 121 bytes require 1 byte + script length. Above |
52 | | * that, scripts up to 16505 bytes require 2 bytes + script length. |
53 | | */ |
54 | | struct ScriptCompression |
55 | | { |
56 | | /** |
57 | | * make this static for now (there are only 6 special scripts defined) |
58 | | * this can potentially be extended together with a new version for |
59 | | * transactions, in which case this value becomes dependent on version |
60 | | * and nHeight of the enclosing transaction. |
61 | | */ |
62 | | static const unsigned int nSpecialScripts = 6; |
63 | | |
64 | | template<typename Stream> |
65 | 475k | void Ser(Stream &s, const CScript& script) { |
66 | 475k | CompressedScript compr; |
67 | 475k | if (CompressScript(script, compr)) { |
68 | 126k | s << std::span{compr}; |
69 | 126k | return; |
70 | 126k | } |
71 | 348k | unsigned int nSize = script.size() + nSpecialScripts; |
72 | 348k | s << VARINT(nSize); |
73 | 348k | s << std::span{script}; |
74 | 348k | } void ScriptCompression::Ser<SizeComputer>(SizeComputer&, CScript const&) Line | Count | Source | 65 | 65.6k | void Ser(Stream &s, const CScript& script) { | 66 | 65.6k | CompressedScript compr; | 67 | 65.6k | if (CompressScript(script, compr)) { | 68 | 8.87k | s << std::span{compr}; | 69 | 8.87k | return; | 70 | 8.87k | } | 71 | 56.7k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 56.7k | s << VARINT(nSize); | 73 | 56.7k | s << std::span{script}; | 74 | 56.7k | } |
void ScriptCompression::Ser<HashWriter>(HashWriter&, CScript const&) Line | Count | Source | 65 | 65.6k | void Ser(Stream &s, const CScript& script) { | 66 | 65.6k | CompressedScript compr; | 67 | 65.6k | if (CompressScript(script, compr)) { | 68 | 8.87k | s << std::span{compr}; | 69 | 8.87k | return; | 70 | 8.87k | } | 71 | 56.7k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 56.7k | s << VARINT(nSize); | 73 | 56.7k | s << std::span{script}; | 74 | 56.7k | } |
void ScriptCompression::Ser<BufferedWriter<AutoFile>>(BufferedWriter<AutoFile>&, CScript const&) Line | Count | Source | 65 | 65.6k | void Ser(Stream &s, const CScript& script) { | 66 | 65.6k | CompressedScript compr; | 67 | 65.6k | if (CompressScript(script, compr)) { | 68 | 8.87k | s << std::span{compr}; | 69 | 8.87k | return; | 70 | 8.87k | } | 71 | 56.7k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 56.7k | s << VARINT(nSize); | 73 | 56.7k | s << std::span{script}; | 74 | 56.7k | } |
void ScriptCompression::Ser<AutoFile>(AutoFile&, CScript const&) Line | Count | Source | 65 | 6.58k | void Ser(Stream &s, const CScript& script) { | 66 | 6.58k | CompressedScript compr; | 67 | 6.58k | if (CompressScript(script, compr)) { | 68 | 6.11k | s << std::span{compr}; | 69 | 6.11k | return; | 70 | 6.11k | } | 71 | 472 | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 472 | s << VARINT(nSize); | 73 | 472 | s << std::span{script}; | 74 | 472 | } |
void ScriptCompression::Ser<DataStream>(DataStream&, CScript const&) Line | Count | Source | 65 | 272k | void Ser(Stream &s, const CScript& script) { | 66 | 272k | CompressedScript compr; | 67 | 272k | if (CompressScript(script, compr)) { | 68 | 94.0k | s << std::span{compr}; | 69 | 94.0k | return; | 70 | 94.0k | } | 71 | 178k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 178k | s << VARINT(nSize); | 73 | 178k | s << std::span{script}; | 74 | 178k | } |
|
75 | | |
76 | | template<typename Stream> |
77 | 342k | void Unser(Stream &s, CScript& script) { |
78 | 342k | unsigned int nSize = 0; |
79 | 342k | s >> VARINT(nSize); |
80 | 342k | if (nSize < nSpecialScripts) { |
81 | 87.5k | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); |
82 | 87.5k | s >> std::span{vch}; |
83 | 87.5k | DecompressScript(script, nSize, vch); |
84 | 87.5k | return; |
85 | 87.5k | } |
86 | 254k | nSize -= nSpecialScripts; |
87 | 254k | if (nSize > MAX_SCRIPT_SIZE) { |
88 | | // Overly long script, replace with a short invalid one |
89 | 1 | script << OP_RETURN; |
90 | 1 | s.ignore(nSize); |
91 | 254k | } else { |
92 | 254k | script.resize(nSize); |
93 | 254k | s >> std::span{script}; |
94 | 254k | } |
95 | 254k | } void ScriptCompression::Unser<SpanReader>(SpanReader&, CScript&) Line | Count | Source | 77 | 82.2k | void Unser(Stream &s, CScript& script) { | 78 | 82.2k | unsigned int nSize = 0; | 79 | 82.2k | s >> VARINT(nSize); | 80 | 82.2k | if (nSize < nSpecialScripts) { | 81 | 4.78k | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); | 82 | 4.78k | s >> std::span{vch}; | 83 | 4.78k | DecompressScript(script, nSize, vch); | 84 | 4.78k | return; | 85 | 4.78k | } | 86 | 77.4k | nSize -= nSpecialScripts; | 87 | 77.4k | if (nSize > MAX_SCRIPT_SIZE) { | 88 | | // Overly long script, replace with a short invalid one | 89 | 1 | script << OP_RETURN; | 90 | 1 | s.ignore(nSize); | 91 | 77.4k | } else { | 92 | 77.4k | script.resize(nSize); | 93 | 77.4k | s >> std::span{script}; | 94 | 77.4k | } | 95 | 77.4k | } |
void ScriptCompression::Unser<AutoFile>(AutoFile&, CScript&) Line | Count | Source | 77 | 6.35k | void Unser(Stream &s, CScript& script) { | 78 | 6.35k | unsigned int nSize = 0; | 79 | 6.35k | s >> VARINT(nSize); | 80 | 6.35k | if (nSize < nSpecialScripts) { | 81 | 5.61k | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); | 82 | 5.61k | s >> std::span{vch}; | 83 | 5.61k | DecompressScript(script, nSize, vch); | 84 | 5.61k | return; | 85 | 5.61k | } | 86 | 736 | nSize -= nSpecialScripts; | 87 | 736 | if (nSize > MAX_SCRIPT_SIZE) { | 88 | | // Overly long script, replace with a short invalid one | 89 | 0 | script << OP_RETURN; | 90 | 0 | s.ignore(nSize); | 91 | 736 | } else { | 92 | 736 | script.resize(nSize); | 93 | 736 | s >> std::span{script}; | 94 | 736 | } | 95 | 736 | } |
void ScriptCompression::Unser<HashVerifier<BufferedReader<AutoFile>>>(HashVerifier<BufferedReader<AutoFile>>&, CScript&) Line | Count | Source | 77 | 27.2k | void Unser(Stream &s, CScript& script) { | 78 | 27.2k | unsigned int nSize = 0; | 79 | 27.2k | s >> VARINT(nSize); | 80 | 27.2k | if (nSize < nSpecialScripts) { | 81 | 5.02k | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); | 82 | 5.02k | s >> std::span{vch}; | 83 | 5.02k | DecompressScript(script, nSize, vch); | 84 | 5.02k | return; | 85 | 5.02k | } | 86 | 22.2k | nSize -= nSpecialScripts; | 87 | 22.2k | if (nSize > MAX_SCRIPT_SIZE) { | 88 | | // Overly long script, replace with a short invalid one | 89 | 0 | script << OP_RETURN; | 90 | 0 | s.ignore(nSize); | 91 | 22.2k | } else { | 92 | 22.2k | script.resize(nSize); | 93 | 22.2k | s >> std::span{script}; | 94 | 22.2k | } | 95 | 22.2k | } |
void ScriptCompression::Unser<DataStream>(DataStream&, CScript&) Line | Count | Source | 77 | 226k | void Unser(Stream &s, CScript& script) { | 78 | 226k | unsigned int nSize = 0; | 79 | 226k | s >> VARINT(nSize); | 80 | 226k | if (nSize < nSpecialScripts) { | 81 | 72.0k | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); | 82 | 72.0k | s >> std::span{vch}; | 83 | 72.0k | DecompressScript(script, nSize, vch); | 84 | 72.0k | return; | 85 | 72.0k | } | 86 | 154k | nSize -= nSpecialScripts; | 87 | 154k | if (nSize > MAX_SCRIPT_SIZE) { | 88 | | // Overly long script, replace with a short invalid one | 89 | 0 | script << OP_RETURN; | 90 | 0 | s.ignore(nSize); | 91 | 154k | } else { | 92 | 154k | script.resize(nSize); | 93 | 154k | s >> std::span{script}; | 94 | 154k | } | 95 | 154k | } |
|
96 | | }; |
97 | | |
98 | | struct AmountCompression |
99 | | { |
100 | | template<typename Stream, typename I> void Ser(Stream& s, I val) |
101 | 475k | { |
102 | 475k | s << VARINT(CompressAmount(val)); |
103 | 475k | } void AmountCompression::Ser<SizeComputer, long>(SizeComputer&, long) Line | Count | Source | 101 | 65.6k | { | 102 | 65.6k | s << VARINT(CompressAmount(val)); | 103 | 65.6k | } |
void AmountCompression::Ser<HashWriter, long>(HashWriter&, long) Line | Count | Source | 101 | 65.6k | { | 102 | 65.6k | s << VARINT(CompressAmount(val)); | 103 | 65.6k | } |
void AmountCompression::Ser<BufferedWriter<AutoFile>, long>(BufferedWriter<AutoFile>&, long) Line | Count | Source | 101 | 65.6k | { | 102 | 65.6k | s << VARINT(CompressAmount(val)); | 103 | 65.6k | } |
void AmountCompression::Ser<AutoFile, long>(AutoFile&, long) Line | Count | Source | 101 | 6.58k | { | 102 | 6.58k | s << VARINT(CompressAmount(val)); | 103 | 6.58k | } |
void AmountCompression::Ser<DataStream, long>(DataStream&, long) Line | Count | Source | 101 | 272k | { | 102 | 272k | s << VARINT(CompressAmount(val)); | 103 | 272k | } |
|
104 | | template<typename Stream, typename I> void Unser(Stream& s, I& val) |
105 | 342k | { |
106 | 342k | uint64_t v; |
107 | 342k | s >> VARINT(v); |
108 | 342k | val = DecompressAmount(v); |
109 | 342k | } void AmountCompression::Unser<SpanReader, long>(SpanReader&, long&) Line | Count | Source | 105 | 82.2k | { | 106 | 82.2k | uint64_t v; | 107 | 82.2k | s >> VARINT(v); | 108 | 82.2k | val = DecompressAmount(v); | 109 | 82.2k | } |
void AmountCompression::Unser<AutoFile, long>(AutoFile&, long&) Line | Count | Source | 105 | 6.35k | { | 106 | 6.35k | uint64_t v; | 107 | 6.35k | s >> VARINT(v); | 108 | 6.35k | val = DecompressAmount(v); | 109 | 6.35k | } |
void AmountCompression::Unser<HashVerifier<BufferedReader<AutoFile>>, long>(HashVerifier<BufferedReader<AutoFile>>&, long&) Line | Count | Source | 105 | 27.2k | { | 106 | 27.2k | uint64_t v; | 107 | 27.2k | s >> VARINT(v); | 108 | 27.2k | val = DecompressAmount(v); | 109 | 27.2k | } |
void AmountCompression::Unser<DataStream, long>(DataStream&, long&) Line | Count | Source | 105 | 226k | { | 106 | 226k | uint64_t v; | 107 | 226k | s >> VARINT(v); | 108 | 226k | val = DecompressAmount(v); | 109 | 226k | } |
|
110 | | }; |
111 | | |
112 | | /** wrapper for CTxOut that provides a more compact serialization */ |
113 | | struct TxOutCompression |
114 | | { |
115 | 818k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); }void TxOutCompression::SerializationOps<SpanReader, CTxOut, ActionUnserialize>(CTxOut&, SpanReader&, ActionUnserialize) Line | Count | Source | 115 | 82.2k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<AutoFile, CTxOut, ActionUnserialize>(CTxOut&, AutoFile&, ActionUnserialize) Line | Count | Source | 115 | 6.35k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<HashVerifier<BufferedReader<AutoFile>>, CTxOut, ActionUnserialize>(CTxOut&, HashVerifier<BufferedReader<AutoFile>>&, ActionUnserialize) Line | Count | Source | 115 | 27.2k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<SizeComputer, CTxOut const, ActionSerialize>(CTxOut const&, SizeComputer&, ActionSerialize) Line | Count | Source | 115 | 65.6k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<HashWriter, CTxOut const, ActionSerialize>(CTxOut const&, HashWriter&, ActionSerialize) Line | Count | Source | 115 | 65.6k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<BufferedWriter<AutoFile>, CTxOut const, ActionSerialize>(CTxOut const&, BufferedWriter<AutoFile>&, ActionSerialize) Line | Count | Source | 115 | 65.6k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<AutoFile, CTxOut const, ActionSerialize>(CTxOut const&, AutoFile&, ActionSerialize) Line | Count | Source | 115 | 6.58k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<DataStream, CTxOut const, ActionSerialize>(CTxOut const&, DataStream&, ActionSerialize) Line | Count | Source | 115 | 272k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
void TxOutCompression::SerializationOps<DataStream, CTxOut, ActionUnserialize>(CTxOut&, DataStream&, ActionUnserialize) Line | Count | Source | 115 | 226k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
|
116 | | }; |
117 | | |
118 | | #endif // BITCOIN_COMPRESSOR_H |