Changeset View
Changeset View
Standalone View
Standalone View
src/compressor.h
// Copyright (c) 2009-2010 Satoshi Nakamoto | // Copyright (c) 2009-2010 Satoshi Nakamoto | ||||
// Copyright (c) 2009-2016 The Bitcoin Core developers | // Copyright (c) 2009-2016 The Bitcoin Core developers | ||||
// Distributed under the MIT software license, see the accompanying | // Distributed under the MIT software license, see the accompanying | ||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php. | // file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
#ifndef BITCOIN_COMPRESSOR_H | #ifndef BITCOIN_COMPRESSOR_H | ||||
#define BITCOIN_COMPRESSOR_H | #define BITCOIN_COMPRESSOR_H | ||||
#include <primitives/transaction.h> | #include <primitives/transaction.h> | ||||
#include <script/script.h> | #include <script/script.h> | ||||
#include <serialize.h> | #include <serialize.h> | ||||
#include <span.h> | #include <span.h> | ||||
class CKeyID; | |||||
class CPubKey; | |||||
class CScriptID; | |||||
bool CompressScript(const CScript &script, std::vector<uint8_t> &out); | bool CompressScript(const CScript &script, std::vector<uint8_t> &out); | ||||
unsigned int GetSpecialScriptSize(unsigned int nSize); | unsigned int GetSpecialScriptSize(unsigned int nSize); | ||||
bool DecompressScript(CScript &script, unsigned int nSize, | bool DecompressScript(CScript &script, unsigned int nSize, | ||||
const std::vector<uint8_t> &out); | const std::vector<uint8_t> &out); | ||||
uint64_t CompressAmount(Amount nAmount); | uint64_t CompressAmount(Amount nAmount); | ||||
Amount DecompressAmount(uint64_t nAmount); | Amount DecompressAmount(uint64_t nAmount); | ||||
/** | /** | ||||
* Compact serializer for scripts. | * Compact serializer for scripts. | ||||
* | * | ||||
* It detects common cases and encodes them much more efficiently. | * It detects common cases and encodes them much more efficiently. | ||||
* 3 special cases are defined: | * 3 special cases are defined: | ||||
* * Pay to pubkey hash (encoded as 21 bytes) | * * Pay to pubkey hash (encoded as 21 bytes) | ||||
* * Pay to script 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) | * * 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, | * Other scripts up to 121 bytes require 1 byte + script length. Above that, | ||||
* scripts up to 16505 bytes require 2 bytes + script length. | * scripts up to 16505 bytes require 2 bytes + script length. | ||||
*/ | */ | ||||
class CScriptCompressor { | struct ScriptCompression { | ||||
private: | |||||
/** | /** | ||||
* make this static for now (there are only 6 special scripts defined) this | * make this static for now (there are only 6 special scripts defined) this | ||||
* can potentially be extended together with a new nVersion for | * can potentially be extended together with a new nVersion for | ||||
* transactions, in which case this value becomes dependent on nVersion and | * transactions, in which case this value becomes dependent on nVersion and | ||||
* nHeight of the enclosing transaction. | * nHeight of the enclosing transaction. | ||||
*/ | */ | ||||
static const unsigned int nSpecialScripts = 6; | static const unsigned int nSpecialScripts = 6; | ||||
CScript &script; | template <typename Stream> | ||||
void Ser(Stream &s, const CScript &script) const { | |||||
public: | |||||
explicit CScriptCompressor(CScript &scriptIn) : script(scriptIn) {} | |||||
template <typename Stream> void Serialize(Stream &s) const { | |||||
std::vector<uint8_t> compr; | std::vector<uint8_t> compr; | ||||
if (CompressScript(script, compr)) { | if (CompressScript(script, compr)) { | ||||
s << MakeSpan(compr); | s << MakeSpan(compr); | ||||
return; | return; | ||||
} | } | ||||
unsigned int nSize = script.size() + nSpecialScripts; | unsigned int nSize = script.size() + nSpecialScripts; | ||||
s << VARINT(nSize); | s << VARINT(nSize); | ||||
s << MakeSpan(script); | s << MakeSpan(script); | ||||
} | } | ||||
template <typename Stream> void Unserialize(Stream &s) { | template <typename Stream> void Unser(Stream &s, CScript &script) { | ||||
unsigned int nSize = 0; | unsigned int nSize = 0; | ||||
s >> VARINT(nSize); | s >> VARINT(nSize); | ||||
if (nSize < nSpecialScripts) { | if (nSize < nSpecialScripts) { | ||||
std::vector<uint8_t> vch(GetSpecialScriptSize(nSize), 0x00); | std::vector<uint8_t> vch(GetSpecialScriptSize(nSize), 0x00); | ||||
s >> MakeSpan(vch); | s >> MakeSpan(vch); | ||||
DecompressScript(script, nSize, vch); | DecompressScript(script, nSize, vch); | ||||
return; | return; | ||||
} | } | ||||
nSize -= nSpecialScripts; | nSize -= nSpecialScripts; | ||||
if (nSize > MAX_SCRIPT_SIZE) { | if (nSize > MAX_SCRIPT_SIZE) { | ||||
// Overly long script, replace with a short invalid one | // Overly long script, replace with a short invalid one | ||||
script << OP_RETURN; | script << OP_RETURN; | ||||
s.ignore(nSize); | s.ignore(nSize); | ||||
} else { | } else { | ||||
script.resize(nSize); | script.resize(nSize); | ||||
s >> MakeSpan(script); | s >> MakeSpan(script); | ||||
} | } | ||||
} | } | ||||
}; | }; | ||||
/** wrapper for CTxOut that provides a more compact serialization */ | struct AmountCompression { | ||||
class CTxOutCompressor { | template <typename Stream, typename I> void Ser(Stream &s, I val) { | ||||
private: | s << VARINT(CompressAmount(val)); | ||||
CTxOut &txout; | } | ||||
template <typename Stream, typename I> void Unser(Stream &s, I &val) { | |||||
public: | uint64_t v; | ||||
explicit CTxOutCompressor(CTxOut &txoutIn) : txout(txoutIn) {} | s >> VARINT(v); | ||||
val = DecompressAmount(v); | |||||
ADD_SERIALIZE_METHODS; | |||||
template <typename Stream, typename Operation> | |||||
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); | |||||
/** | |||||
* wrapper for CTxOut that provides a more compact serialization | |||||
*/ | |||||
struct TxOutCompression { | |||||
FORMATTER_METHODS(CTxOut, obj) { | |||||
READWRITE(Using<AmountCompression>(obj.nValue), | |||||
Using<ScriptCompression>(obj.scriptPubKey)); | |||||
} | } | ||||
}; | }; | ||||
#endif // BITCOIN_COMPRESSOR_H | #endif // BITCOIN_COMPRESSOR_H |