952383e16c
1e44169 Add tests for CCoins deserialization (Pieter Wuille) 5d0434d Fix OOM bug: UTXO entries with invalid script length (Pieter Wuille) 4bf631e CDataStream::ignore Throw exception instead of assert on negative nSize. (Patrick Strateman) 4f87af6 Treat overly long scriptPubKeys as unspendable (Pieter Wuille) f8e6fb1 Introduce constant for maximum CScript length (Pieter Wuille)
130 lines
4.2 KiB
C++
130 lines
4.2 KiB
C++
// Copyright (c) 2009-2010 Satoshi Nakamoto
|
|
// Copyright (c) 2009-2014 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#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<unsigned char> &out) const;
|
|
unsigned int GetSpecialSize(unsigned int nSize) const;
|
|
bool Decompress(unsigned int nSize, const std::vector<unsigned char> &out);
|
|
public:
|
|
CScriptCompressor(CScript &scriptIn) : script(scriptIn) { }
|
|
|
|
unsigned int GetSerializeSize(int nType, int nVersion) const {
|
|
std::vector<unsigned char> compr;
|
|
if (Compress(compr))
|
|
return compr.size();
|
|
unsigned int nSize = script.size() + nSpecialScripts;
|
|
return script.size() + VARINT(nSize).GetSerializeSize(nType, nVersion);
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Serialize(Stream &s, int nType, int nVersion) const {
|
|
std::vector<unsigned char> compr;
|
|
if (Compress(compr)) {
|
|
s << CFlatData(compr);
|
|
return;
|
|
}
|
|
unsigned int nSize = script.size() + nSpecialScripts;
|
|
s << VARINT(nSize);
|
|
s << CFlatData(script);
|
|
}
|
|
|
|
template<typename Stream>
|
|
void Unserialize(Stream &s, int nType, int nVersion) {
|
|
unsigned int nSize = 0;
|
|
s >> VARINT(nSize);
|
|
if (nSize < nSpecialScripts) {
|
|
std::vector<unsigned char> 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 <typename Stream, typename Operation>
|
|
inline void SerializationOp(Stream& s, Operation ser_action, int nType, int nVersion) {
|
|
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
|