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