1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Copyright (c) 2009-2020 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 nVersion for
59      * transactions, in which case this value becomes dependent on nVersion
60      * and nHeight of the enclosing transaction.
61      */
62     static const unsigned int nSpecialScripts = 6;
63 
64     template<typename Stream>
SerScriptCompression65     void Ser(Stream &s, const CScript& script) {
66         CompressedScript compr;
67         if (CompressScript(script, compr)) {
68             s << MakeSpan(compr);
69             return;
70         }
71         unsigned int nSize = script.size() + nSpecialScripts;
72         s << VARINT(nSize);
73         s << MakeSpan(script);
74     }
75 
76     template<typename Stream>
UnserScriptCompression77     void Unser(Stream &s, CScript& script) {
78         unsigned int nSize = 0;
79         s >> VARINT(nSize);
80         if (nSize < nSpecialScripts) {
81             CompressedScript vch(GetSpecialScriptSize(nSize), 0x00);
82             s >> MakeSpan(vch);
83             DecompressScript(script, nSize, vch);
84             return;
85         }
86         nSize -= nSpecialScripts;
87         if (nSize > MAX_SCRIPT_SIZE) {
88             // Overly long script, replace with a short invalid one
89             script << OP_RETURN;
90             s.ignore(nSize);
91         } else {
92             script.resize(nSize);
93             s >> MakeSpan(script);
94         }
95     }
96 };
97 
98 struct AmountCompression
99 {
SerAmountCompression100     template<typename Stream, typename I> void Ser(Stream& s, I val)
101     {
102         s << VARINT(CompressAmount(val));
103     }
UnserAmountCompression104     template<typename Stream, typename I> void Unser(Stream& s, I& val)
105     {
106         uint64_t v;
107         s >> VARINT(v);
108         val = DecompressAmount(v);
109     }
110 };
111 
112 /** wrapper for CTxOut that provides a more compact serialization */
113 struct TxOutCompression
114 {
FORMATTER_METHODSTxOutCompression115     FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); }
116 };
117 
118 #endif // BITCOIN_COMPRESSOR_H
119