/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 | 2.26M | void Ser(Stream &s, const CScript& script) { |
66 | 2.26M | CompressedScript compr; |
67 | 2.26M | if (CompressScript(script, compr)) { Branch (67:13): [True: 166, False: 11.8k]
Branch (67:13): [True: 166, False: 11.8k]
Branch (67:13): [True: 166, False: 11.8k]
Branch (67:13): [True: 0, False: 0]
Branch (67:13): [True: 570, False: 2.22M]
|
68 | 1.06k | s << std::span{compr}; |
69 | 1.06k | return; |
70 | 1.06k | } |
71 | 2.25M | unsigned int nSize = script.size() + nSpecialScripts; |
72 | 2.25M | s << VARINT(nSize); |
73 | 2.25M | s << std::span{script}; |
74 | 2.25M | } void ScriptCompression::Ser<SizeComputer>(SizeComputer&, CScript const&) Line | Count | Source | 65 | 12.0k | void Ser(Stream &s, const CScript& script) { | 66 | 12.0k | CompressedScript compr; | 67 | 12.0k | if (CompressScript(script, compr)) { Branch (67:13): [True: 166, False: 11.8k]
| 68 | 166 | s << std::span{compr}; | 69 | 166 | return; | 70 | 166 | } | 71 | 11.8k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 11.8k | s << VARINT(nSize); | 73 | 11.8k | s << std::span{script}; | 74 | 11.8k | } |
void ScriptCompression::Ser<HashWriter>(HashWriter&, CScript const&) Line | Count | Source | 65 | 12.0k | void Ser(Stream &s, const CScript& script) { | 66 | 12.0k | CompressedScript compr; | 67 | 12.0k | if (CompressScript(script, compr)) { Branch (67:13): [True: 166, False: 11.8k]
| 68 | 166 | s << std::span{compr}; | 69 | 166 | return; | 70 | 166 | } | 71 | 11.8k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 11.8k | s << VARINT(nSize); | 73 | 11.8k | s << std::span{script}; | 74 | 11.8k | } |
void ScriptCompression::Ser<BufferedWriter<AutoFile> >(BufferedWriter<AutoFile>&, CScript const&) Line | Count | Source | 65 | 12.0k | void Ser(Stream &s, const CScript& script) { | 66 | 12.0k | CompressedScript compr; | 67 | 12.0k | if (CompressScript(script, compr)) { Branch (67:13): [True: 166, False: 11.8k]
| 68 | 166 | s << std::span{compr}; | 69 | 166 | return; | 70 | 166 | } | 71 | 11.8k | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 11.8k | s << VARINT(nSize); | 73 | 11.8k | s << std::span{script}; | 74 | 11.8k | } |
Unexecuted instantiation: void ScriptCompression::Ser<AutoFile>(AutoFile&, CScript const&) void ScriptCompression::Ser<DataStream>(DataStream&, CScript const&) Line | Count | Source | 65 | 2.22M | void Ser(Stream &s, const CScript& script) { | 66 | 2.22M | CompressedScript compr; | 67 | 2.22M | if (CompressScript(script, compr)) { Branch (67:13): [True: 570, False: 2.22M]
| 68 | 570 | s << std::span{compr}; | 69 | 570 | return; | 70 | 570 | } | 71 | 2.22M | unsigned int nSize = script.size() + nSpecialScripts; | 72 | 2.22M | s << VARINT(nSize); | 73 | 2.22M | s << std::span{script}; | 74 | 2.22M | } |
|
75 | | |
76 | | template<typename Stream> |
77 | 29.5k | void Unser(Stream &s, CScript& script) { |
78 | 29.5k | unsigned int nSize = 0; |
79 | 29.5k | s >> VARINT(nSize); |
80 | 29.5k | if (nSize < nSpecialScripts) { Branch (80:13): [True: 353, False: 29.2k]
Branch (80:13): [True: 0, False: 0]
Branch (80:13): [True: 0, False: 0]
|
81 | 353 | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); |
82 | 353 | s >> std::span{vch}; |
83 | 353 | DecompressScript(script, nSize, vch); |
84 | 353 | return; |
85 | 353 | } |
86 | 29.2k | nSize -= nSpecialScripts; |
87 | 29.2k | if (nSize > MAX_SCRIPT_SIZE) { Branch (87:13): [True: 0, False: 29.2k]
Branch (87:13): [True: 0, False: 0]
Branch (87:13): [True: 0, False: 0]
|
88 | | // Overly long script, replace with a short invalid one |
89 | 0 | script << OP_RETURN; |
90 | 0 | s.ignore(nSize); |
91 | 29.2k | } else { |
92 | 29.2k | script.resize(nSize); |
93 | 29.2k | s >> std::span{script}; |
94 | 29.2k | } |
95 | 29.2k | } void ScriptCompression::Unser<HashVerifier<BufferedReader<AutoFile> > >(HashVerifier<BufferedReader<AutoFile> >&, CScript&) Line | Count | Source | 77 | 29.5k | void Unser(Stream &s, CScript& script) { | 78 | 29.5k | unsigned int nSize = 0; | 79 | 29.5k | s >> VARINT(nSize); | 80 | 29.5k | if (nSize < nSpecialScripts) { Branch (80:13): [True: 353, False: 29.2k]
| 81 | 353 | CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); | 82 | 353 | s >> std::span{vch}; | 83 | 353 | DecompressScript(script, nSize, vch); | 84 | 353 | return; | 85 | 353 | } | 86 | 29.2k | nSize -= nSpecialScripts; | 87 | 29.2k | if (nSize > MAX_SCRIPT_SIZE) { Branch (87:13): [True: 0, False: 29.2k]
| 88 | | // Overly long script, replace with a short invalid one | 89 | 0 | script << OP_RETURN; | 90 | 0 | s.ignore(nSize); | 91 | 29.2k | } else { | 92 | 29.2k | script.resize(nSize); | 93 | 29.2k | s >> std::span{script}; | 94 | 29.2k | } | 95 | 29.2k | } |
Unexecuted instantiation: void ScriptCompression::Unser<DataStream>(DataStream&, CScript&) Unexecuted instantiation: void ScriptCompression::Unser<AutoFile>(AutoFile&, CScript&) |
96 | | }; |
97 | | |
98 | | struct AmountCompression |
99 | | { |
100 | | template<typename Stream, typename I> void Ser(Stream& s, I val) |
101 | 2.26M | { |
102 | 2.26M | s << VARINT(CompressAmount(val)); |
103 | 2.26M | } void AmountCompression::Ser<SizeComputer, long>(SizeComputer&, long) Line | Count | Source | 101 | 12.0k | { | 102 | 12.0k | s << VARINT(CompressAmount(val)); | 103 | 12.0k | } |
void AmountCompression::Ser<HashWriter, long>(HashWriter&, long) Line | Count | Source | 101 | 12.0k | { | 102 | 12.0k | s << VARINT(CompressAmount(val)); | 103 | 12.0k | } |
void AmountCompression::Ser<BufferedWriter<AutoFile>, long>(BufferedWriter<AutoFile>&, long) Line | Count | Source | 101 | 12.0k | { | 102 | 12.0k | s << VARINT(CompressAmount(val)); | 103 | 12.0k | } |
Unexecuted instantiation: void AmountCompression::Ser<AutoFile, long>(AutoFile&, long) void AmountCompression::Ser<DataStream, long>(DataStream&, long) Line | Count | Source | 101 | 2.22M | { | 102 | 2.22M | s << VARINT(CompressAmount(val)); | 103 | 2.22M | } |
|
104 | | template<typename Stream, typename I> void Unser(Stream& s, I& val) |
105 | 29.5k | { |
106 | 29.5k | uint64_t v; |
107 | 29.5k | s >> VARINT(v); |
108 | 29.5k | val = DecompressAmount(v); |
109 | 29.5k | } void AmountCompression::Unser<HashVerifier<BufferedReader<AutoFile> >, long>(HashVerifier<BufferedReader<AutoFile> >&, long&) Line | Count | Source | 105 | 29.5k | { | 106 | 29.5k | uint64_t v; | 107 | 29.5k | s >> VARINT(v); | 108 | 29.5k | val = DecompressAmount(v); | 109 | 29.5k | } |
Unexecuted instantiation: void AmountCompression::Unser<DataStream, long>(DataStream&, long&) Unexecuted instantiation: void AmountCompression::Unser<AutoFile, long>(AutoFile&, long&) |
110 | | }; |
111 | | |
112 | | /** wrapper for CTxOut that provides a more compact serialization */ |
113 | | struct TxOutCompression |
114 | | { |
115 | 2.29M | 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 | 29.5k | 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 | 12.0k | 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 | 12.0k | 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 | 12.0k | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
Unexecuted instantiation: void TxOutCompression::SerializationOps<AutoFile, CTxOut const, ActionSerialize>(CTxOut const&, AutoFile&, ActionSerialize) Unexecuted instantiation: void TxOutCompression::SerializationOps<DataStream, CTxOut, ActionUnserialize>(CTxOut&, DataStream&, ActionUnserialize) void TxOutCompression::SerializationOps<DataStream, CTxOut const, ActionSerialize>(CTxOut const&, DataStream&, ActionSerialize) Line | Count | Source | 115 | 2.22M | FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } |
Unexecuted instantiation: void TxOutCompression::SerializationOps<AutoFile, CTxOut, ActionUnserialize>(CTxOut&, AutoFile&, ActionUnserialize) |
116 | | }; |
117 | | |
118 | | #endif // BITCOIN_COMPRESSOR_H |