compressor.h raw

   1  // Copyright (c) 2009-2010 Satoshi Nakamoto
   2  // Copyright (c) 2009-present 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 version for
  59       * transactions, in which case this value becomes dependent on version
  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 << std::span{compr};
  69              return;
  70          }
  71          unsigned int nSize = script.size() + nSpecialScripts;
  72          s << VARINT(nSize);
  73          s << std::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 >> std::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 >> std::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
 119