Branch data 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