/ src / compressor.h
compressor.h
  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      void Ser(Stream &s, const CScript& script) {
 66          CompressedScript compr;
 67          if (CompressScript(script, compr)) {
 68              s << Span{compr};
 69              return;
 70          }
 71          unsigned int nSize = script.size() + nSpecialScripts;
 72          s << VARINT(nSize);
 73          s << Span{script};
 74      }
 75  
 76      template<typename Stream>
 77      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 >> Span{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 >> Span{script};
 94          }
 95      }
 96  };
 97  
 98  struct AmountCompression
 99  {
100      template<typename Stream, typename I> void Ser(Stream& s, I val)
101      {
102          s << VARINT(CompressAmount(val));
103      }
104      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  {
115      FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using<AmountCompression>(obj.nValue), Using<ScriptCompression>(obj.scriptPubKey)); }
116  };
117  
118  #endif // BITCOIN_COMPRESSOR_H