// Copyright (c) 2009-2010 Satoshi Nakamoto // Copyright (c) 2009-2014 The Bitcoin Core developers // Distributed under the GPLv3 software license, see the accompanying // file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html /****************************************************************************** * Copyright © 2014-2019 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ #ifndef BITCOIN_COMPRESSOR_H #define BITCOIN_COMPRESSOR_H #include "primitives/transaction.h" #include "script/script.h" #include "serialize.h" class CKeyID; class CPubKey; class CScriptID; /** Compact serializer for scripts. * * It detects common cases and encodes them much more efficiently. * 3 special cases are defined: * * Pay to pubkey hash (encoded as 21 bytes) * * Pay to script hash (encoded as 21 bytes) * * Pay to pubkey starting with 0x02, 0x03 or 0x04 (encoded as 33 bytes) * * Other scripts up to 121 bytes require 1 byte + script length. Above * that, scripts up to 16505 bytes require 2 bytes + script length. */ class CScriptCompressor { private: /** * make this static for now (there are only 6 special scripts defined) * this can potentially be extended together with a new nVersion for * transactions, in which case this value becomes dependent on nVersion * and nHeight of the enclosing transaction. */ static const unsigned int nSpecialScripts = 6; CScript &script; protected: /** * These check for scripts for which a special case with a shorter encoding is defined. * They are implemented separately from the CScript test, as these test for exact byte * sequence correspondences, and are more strict. For example, IsToPubKey also verifies * whether the public key is valid (as invalid ones cannot be represented in compressed * form). */ bool IsToKeyID(CKeyID &hash) const; bool IsToScriptID(CScriptID &hash) const; bool IsToPubKey(CPubKey &pubkey) const; bool Compress(std::vector &out) const; unsigned int GetSpecialSize(unsigned int nSize) const; bool Decompress(unsigned int nSize, const std::vector &out); public: CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } template void Serialize(Stream &s) const { std::vector compr; if (Compress(compr)) { s << CFlatData(compr); return; } unsigned int nSize = script.size() + nSpecialScripts; s << VARINT(nSize); s << CFlatData(script); } template void Unserialize(Stream &s) { unsigned int nSize = 0; s >> VARINT(nSize); if (nSize < nSpecialScripts) { std::vector vch(GetSpecialSize(nSize), 0x00); s >> REF(CFlatData(vch)); Decompress(nSize, vch); return; } nSize -= nSpecialScripts; if (nSize > MAX_SCRIPT_SIZE) { // Overly long script, replace with a short invalid one script << OP_RETURN; s.ignore(nSize); } else { script.resize(nSize); s >> REF(CFlatData(script)); } } }; /** wrapper for CTxOut that provides a more compact serialization */ class CTxOutCompressor { private: CTxOut &txout; public: static uint64_t CompressAmount(uint64_t nAmount); static uint64_t DecompressAmount(uint64_t nAmount); CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) { } ADD_SERIALIZE_METHODS; template inline void SerializationOp(Stream& s, Operation ser_action) { if (!ser_action.ForRead()) { uint64_t nVal = CompressAmount(txout.nValue); READWRITE(VARINT(nVal)); } else { uint64_t nVal = 0; READWRITE(VARINT(nVal)); txout.nValue = DecompressAmount(nVal); } CScriptCompressor cscript(REF(txout.scriptPubKey)); READWRITE(cscript); } }; #endif // BITCOIN_COMPRESSOR_H