Line data Source code
1 : // Copyright (c) 2009-2010 Satoshi Nakamoto 2 : // Copyright (c) 2009-2021 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> 65 0 : void Ser(Stream &s, const CScript& script) { 66 0 : CompressedScript compr; 67 0 : if (CompressScript(script, compr)) { 68 0 : s << Span{compr}; 69 0 : return; 70 : } 71 0 : unsigned int nSize = script.size() + nSpecialScripts; 72 0 : s << VARINT(nSize); 73 0 : s << Span{script}; 74 0 : } 75 : 76 : template<typename Stream> 77 0 : void Unser(Stream &s, CScript& script) { 78 0 : unsigned int nSize = 0; 79 0 : s >> VARINT(nSize); 80 0 : if (nSize < nSpecialScripts) { 81 0 : CompressedScript vch(GetSpecialScriptSize(nSize), 0x00); 82 0 : s >> Span{vch}; 83 0 : DecompressScript(script, nSize, vch); 84 : return; 85 0 : } 86 0 : nSize -= nSpecialScripts; 87 0 : 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 0 : } else { 92 0 : script.resize(nSize); 93 0 : s >> Span{script}; 94 : } 95 0 : } 96 : }; 97 : 98 : struct AmountCompression 99 : { 100 0 : template<typename Stream, typename I> void Ser(Stream& s, I val) 101 : { 102 0 : s << VARINT(CompressAmount(val)); 103 0 : } 104 0 : template<typename Stream, typename I> void Unser(Stream& s, I& val) 105 : { 106 : uint64_t v; 107 0 : s >> VARINT(v); 108 0 : val = DecompressAmount(v); 109 0 : } 110 : }; 111 : 112 : /** wrapper for CTxOut that provides a more compact serialization */ 113 : struct TxOutCompression 114 : { 115 0 : FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); } 116 : }; 117 : 118 : #endif // BITCOIN_COMPRESSOR_H