From ca34c5cba5fbb9b046b074a234f06ecf6ed5d610 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 18 Jan 2020 07:32:31 -0800 Subject: [PATCH 1/2] Add FORMATTER_METHODS, similar to SERIALIZE_METHODS, but for formatters --- src/serialize.h | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/serialize.h b/src/serialize.h index 56c324c527791..7fa669ebdb519 100644 --- a/src/serialize.h +++ b/src/serialize.h @@ -199,6 +199,30 @@ template const X& ReadWriteAsHelper(const X& x) { return x; } SerializationOp(s, CSerActionUnserialize()); \ } +/** + * Implement the Ser and Unser methods needed for implementing a formatter (see Using below). + * + * Both Ser and Unser are delegated to a single static method SerializationOps, which is polymorphic + * in the serialized/deserialized type (allowing it to be const when serializing, and non-const when + * deserializing). + * + * Example use: + * struct FooFormatter { + * FORMATTER_METHODS(Class, obj) { READWRITE(obj.val1, VARINT(obj.val2)); } + * } + * would define a class FooFormatter that defines a serialization of Class objects consisting + * of serializing its val1 member using the default serialization, and its val2 member using + * VARINT serialization. That FooFormatter can then be used in statements like + * READWRITE(Using(obj.bla)). + */ +#define FORMATTER_METHODS(cls, obj) \ + template \ + static void Ser(Stream& s, const cls& obj) { SerializationOps(obj, s, CSerActionSerialize()); } \ + template \ + static void Unser(Stream& s, cls& obj) { SerializationOps(obj, s, CSerActionUnserialize()); } \ + template \ + static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \ + /** * Implement the Serialize and Unserialize methods by delegating to a single templated * static method that takes the to-be-(de)serialized object as a parameter. This approach @@ -211,17 +235,15 @@ template const X& ReadWriteAsHelper(const X& x) { return x; } void Serialize(Stream& s) const \ { \ static_assert(std::is_same::value, "Serialize type mismatch"); \ - SerializationOps(*this, s, CSerActionSerialize()); \ + Ser(s, *this); \ } \ template \ void Unserialize(Stream& s) \ { \ static_assert(std::is_same::value, "Unserialize type mismatch"); \ - SerializationOps(*this, s, CSerActionUnserialize()); \ + Unser(s, *this); \ } \ - template \ - static inline void SerializationOps(Type& obj, Stream& s, Operation ser_action) \ - + FORMATTER_METHODS(cls, obj) #ifndef CHAR_EQUALS_INT8 template inline void Serialize(Stream& s, char a ) { ser_writedata8(s, a); } // TODO Get rid of bare char From 4de934b9b5b4be1bac8fe205f4ee9a79e772dc34 Mon Sep 17 00:00:00 2001 From: Pieter Wuille Date: Sat, 18 Jan 2020 07:32:58 -0800 Subject: [PATCH 2/2] Convert compression.h to new serialization framework --- src/coins.h | 4 +-- src/compressor.h | 53 +++++++++++++---------------------- src/test/fuzz/deserialize.cpp | 2 +- src/txdb.cpp | 2 +- src/undo.h | 4 +-- 5 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/coins.h b/src/coins.h index 68f759674506e..e71c8a47bcb9b 100644 --- a/src/coins.h +++ b/src/coins.h @@ -61,7 +61,7 @@ class Coin assert(!IsSpent()); uint32_t code = nHeight * 2 + fCoinBase; ::Serialize(s, VARINT(code)); - ::Serialize(s, CTxOutCompressor(REF(out))); + ::Serialize(s, Using(out)); } template @@ -70,7 +70,7 @@ class Coin ::Unserialize(s, VARINT(code)); nHeight = code >> 1; fCoinBase = code & 1; - ::Unserialize(s, CTxOutCompressor(out)); + ::Unserialize(s, Using(out)); } bool IsSpent() const { diff --git a/src/compressor.h b/src/compressor.h index c1eda503c8d82..7bb60d311e6b6 100644 --- a/src/compressor.h +++ b/src/compressor.h @@ -11,10 +11,6 @@ #include #include -class CKeyID; -class CPubKey; -class CScriptID; - bool CompressScript(const CScript& script, std::vector &out); unsigned int GetSpecialScriptSize(unsigned int nSize); bool DecompressScript(CScript& script, unsigned int nSize, const std::vector &out); @@ -33,9 +29,8 @@ uint64_t DecompressAmount(uint64_t nAmount); * 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 +struct ScriptCompression { -private: /** * make this static for now (there are only 6 special scripts defined) * this can potentially be extended together with a new nVersion for @@ -44,12 +39,8 @@ class CScriptCompressor */ static const unsigned int nSpecialScripts = 6; - CScript &script; -public: - explicit CScriptCompressor(CScript &scriptIn) : script(scriptIn) { } - template - void Serialize(Stream &s) const { + void Ser(Stream &s, const CScript& script) { std::vector compr; if (CompressScript(script, compr)) { s << MakeSpan(compr); @@ -61,7 +52,7 @@ class CScriptCompressor } template - void Unserialize(Stream &s) { + void Unser(Stream &s, CScript& script) { unsigned int nSize = 0; s >> VARINT(nSize); if (nSize < nSpecialScripts) { @@ -82,30 +73,24 @@ class CScriptCompressor } }; -/** wrapper for CTxOut that provides a more compact serialization */ -class CTxOutCompressor +struct AmountCompression { -private: - CTxOut &txout; - -public: - explicit 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); + template void Ser(Stream& s, I val) + { + s << VARINT(CompressAmount(val)); + } + template void Unser(Stream& s, I& val) + { + uint64_t v; + s >> VARINT(v); + val = DecompressAmount(v); } }; +/** wrapper for CTxOut that provides a more compact serialization */ +struct TxOutCompression +{ + FORMATTER_METHODS(CTxOut, obj) { READWRITE(Using(obj.nValue), Using(obj.scriptPubKey)); } +}; + #endif // BITCOIN_COMPRESSOR_H diff --git a/src/test/fuzz/deserialize.cpp b/src/test/fuzz/deserialize.cpp index bd05283b785e3..f06f339b9d01d 100644 --- a/src/test/fuzz/deserialize.cpp +++ b/src/test/fuzz/deserialize.cpp @@ -206,7 +206,7 @@ void test_one_input(const std::vector& buffer) DeserializeFromFuzzingInput(buffer, dbi); #elif TXOUTCOMPRESSOR_DESERIALIZE CTxOut to; - CTxOutCompressor toc(to); + auto toc = Using(to); DeserializeFromFuzzingInput(buffer, toc); #elif BLOCKTRANSACTIONS_DESERIALIZE BlockTransactions bt; diff --git a/src/txdb.cpp b/src/txdb.cpp index 956825114951a..35bbdab00d635 100644 --- a/src/txdb.cpp +++ b/src/txdb.cpp @@ -336,7 +336,7 @@ class CCoins vout.assign(vAvail.size(), CTxOut()); for (unsigned int i = 0; i < vAvail.size(); i++) { if (vAvail[i]) - ::Unserialize(s, CTxOutCompressor(vout[i])); + ::Unserialize(s, Using(vout[i])); } // coinbase height ::Unserialize(s, VARINT(nHeight, VarIntMode::NONNEGATIVE_SIGNED)); diff --git a/src/undo.h b/src/undo.h index 3f50f4caad90e..2009c721ab132 100644 --- a/src/undo.h +++ b/src/undo.h @@ -32,7 +32,7 @@ class TxInUndoSerializer // Required to maintain compatibility with older undo format. ::Serialize(s, (unsigned char)0); } - ::Serialize(s, CTxOutCompressor(REF(txout->out))); + ::Serialize(s, Using(REF(txout->out))); } explicit TxInUndoSerializer(const Coin* coin) : txout(coin) {} @@ -56,7 +56,7 @@ class TxInUndoDeserializer unsigned int nVersionDummy; ::Unserialize(s, VARINT(nVersionDummy)); } - ::Unserialize(s, CTxOutCompressor(REF(txout->out))); + ::Unserialize(s, Using(REF(txout->out))); } explicit TxInUndoDeserializer(Coin* coin) : txout(coin) {}