Skip to content

Commit bca6641

Browse files
committed
Complete wire serialization unit test matrix (and fixes).
1 parent b1d3cf1 commit bca6641

9 files changed

Lines changed: 322 additions & 59 deletions

File tree

include/bitcoin/database/impl/query/wire.ipp

Lines changed: 172 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ namespace database {
2828

2929
// Wire serialized objects.
3030
// ----------------------------------------------------------------------------
31+
// Due to the idiotic segwit serialization of witness after output there is are
32+
// duplicated navigations to store_.ins and store_.input by the witness reader.
33+
// This normalized approach is also the most efficient.
3134

3235
TEMPLATE
3336
bool CLASS::get_wire_header(byteflipper& flipper,
@@ -41,60 +44,207 @@ bool CLASS::get_wire_header(byteflipper& flipper,
4144
return false;
4245
}
4346

44-
// Genesis header parent is defaulted, others must be looked up.
45-
if (header.parent_fk != schema::header::link::terminal)
47+
// Genesis header parent is defaulted.
48+
if (header.parent_fk == schema::header::link::terminal)
49+
return true;
50+
51+
flipper.set_position(start);
52+
table::header::wire_key key{ {}, flipper };
53+
if (!store_.header.get(header.parent_fk, key))
4654
{
47-
flipper.set_position(start);
48-
table::header::wire_key key{ {}, flipper };
49-
if (!store_.header.get(header.parent_fk, key))
50-
{
51-
flipper.invalidate();
52-
return false;
53-
}
54-
55-
flipper.set_position(start + system::chain::header::serialized_size());
55+
flipper.invalidate();
56+
return false;
57+
}
58+
59+
return true;
60+
}
61+
62+
TEMPLATE
63+
bool CLASS::get_wire_input(byteflipper& flipper,
64+
const point_link& link) const NOEXCEPT
65+
{
66+
// [point]
67+
table::point::wire_point point{ {}, flipper };
68+
if (!store_.point.get(link, point))
69+
{
70+
flipper.invalidate();
71+
return false;
5672
}
5773

74+
// [[size]script]
75+
table::ins::get_input ins{};
76+
table::input::wire_script script{ {}, flipper };
77+
if (!store_.ins.get(link, ins) ||
78+
!store_.input.get(ins.input_fk, script))
79+
{
80+
flipper.invalidate();
81+
return false;
82+
}
83+
84+
// [sequence]
85+
flipper.write_4_bytes_little_endian(ins.sequence);
5886
return true;
5987
}
6088

6189
TEMPLATE
62-
bool CLASS::get_wire_tx(byteflipper& , const tx_link& ,
63-
bool ) const NOEXCEPT
90+
bool CLASS::get_wire_output(byteflipper& flipper,
91+
const output_link& link) const NOEXCEPT
6492
{
65-
return {};
93+
// [value][[[size]script]]
94+
table::output::wire_script out{ {}, flipper };
95+
if (!store_.output.get(link, out))
96+
{
97+
flipper.invalidate();
98+
return false;
99+
}
100+
101+
return true;
102+
}
103+
104+
TEMPLATE
105+
bool CLASS::get_wire_witness(byteflipper& flipper,
106+
const point_link& link) const NOEXCEPT
107+
{
108+
// [count][[[size]element]]
109+
table::ins::get_input ins{};
110+
table::input::wire_witness wire{ {}, flipper };
111+
if (!store_.ins.get(link, ins) ||
112+
!store_.input.get(ins.input_fk, wire))
113+
{
114+
flipper.invalidate();
115+
return false;
116+
}
117+
118+
return true;
119+
}
120+
121+
TEMPLATE
122+
bool CLASS::get_wire_tx(byteflipper& flipper, const tx_link& link,
123+
bool witness) const NOEXCEPT
124+
{
125+
table::transaction::record tx{};
126+
if (!store_.tx.get(link, tx))
127+
{
128+
flipper.invalidate();
129+
return false;
130+
}
131+
132+
table::outs::record outs{};
133+
outs.out_fks.resize(tx.outs_count);
134+
if (!store_.outs.get(tx.outs_fk, outs))
135+
{
136+
flipper.invalidate();
137+
return false;
138+
}
139+
140+
// Point links are contiguous (computed).
141+
const auto ins_begin = tx.point_fk;
142+
const auto ins_count = tx.ins_count;
143+
const auto ins_final = ins_begin + ins_count;
144+
const auto witnessed = witness && (tx.heavy != tx.light);
145+
146+
flipper.write_4_bytes_little_endian(tx.version);
147+
148+
if (witnessed)
149+
{
150+
flipper.write_byte(system::chain::witness_marker);
151+
flipper.write_byte(system::chain::witness_enabled);
152+
}
153+
154+
flipper.write_variable(ins_count);
155+
for (auto fk = ins_begin; fk < ins_final; ++fk)
156+
if (!get_wire_input(flipper, fk))
157+
return false;
158+
159+
flipper.write_variable(outs.out_fks.size());
160+
for (const auto& fk: outs.out_fks)
161+
if (!get_wire_output(flipper, fk))
162+
return false;
163+
164+
if (witnessed)
165+
{
166+
for (auto fk = ins_begin; fk < ins_final; ++fk)
167+
if (!get_wire_witness(flipper, fk))
168+
return false;
169+
}
170+
171+
flipper.write_4_bytes_little_endian(tx.locktime);
172+
return true;
66173
}
67174

68175
TEMPLATE
69-
bool CLASS::get_wire_block(byteflipper& , const header_link& ,
70-
bool ) const NOEXCEPT
176+
bool CLASS::get_wire_block(byteflipper& flipper, const header_link& link,
177+
bool witness) const NOEXCEPT
71178
{
72-
return {};
179+
if (!get_wire_header(flipper, link))
180+
return false;
181+
182+
const auto txs = to_transactions(link);
183+
if (txs.empty())
184+
{
185+
flipper.invalidate();
186+
return false;
187+
}
188+
189+
flipper.write_variable(txs.size());
190+
for (const auto& tx_link: txs)
191+
if (!get_wire_tx(flipper, tx_link, witness))
192+
return false;
193+
194+
return true;
73195
}
74196

197+
// These convenience wrappers are made practical by size caching for block and
198+
// tx for both nominal and witness wire encodings (and fixed size headers).
199+
// Intermediate objects (input, output, witness) have a size prefix
200+
75201
TEMPLATE
76202
data_chunk CLASS::get_wire_header(const header_link& link) const NOEXCEPT
77203
{
78204
using namespace system;
79205
data_chunk data(chain::header::serialized_size());
206+
80207
stream::flip::fast ostream(data);
81208
flip::bytes::fast out(ostream);
82209
if (!get_wire_header(out, link) || !out)
83-
data.clear();
210+
return {};
84211

85212
return data;
86213
}
87214

88215
TEMPLATE
89-
data_chunk CLASS::get_wire_tx(const tx_link& , bool ) const NOEXCEPT
216+
data_chunk CLASS::get_wire_tx(const tx_link& link, bool witness) const NOEXCEPT
90217
{
91-
return {};
218+
using namespace system;
219+
size_t size{};
220+
if (!get_tx_size(size, link, witness))
221+
return {};
222+
223+
data_chunk data(size);
224+
stream::flip::fast ostream(data);
225+
flip::bytes::fast out(ostream);
226+
if (!get_wire_tx(out, link, witness) || !out)
227+
return {};
228+
229+
return data;
92230
}
93231

94232
TEMPLATE
95-
data_chunk CLASS::get_wire_block(const header_link& , bool ) const NOEXCEPT
233+
data_chunk CLASS::get_wire_block(const header_link& link,
234+
bool witness) const NOEXCEPT
96235
{
97-
return {};
236+
using namespace system;
237+
size_t size{};
238+
if (!get_block_size(size, link, witness))
239+
return {};
240+
241+
data_chunk data(size);
242+
stream::flip::fast ostream(data);
243+
flip::bytes::fast out(ostream);
244+
if (!get_wire_block(out, link, witness) || !out)
245+
return {};
246+
247+
return data;
98248
}
99249

100250
} // namespace database

include/bitcoin/database/query.hpp

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -350,14 +350,10 @@ class query
350350
size_t position) const NOEXCEPT;
351351

352352
/// Sizes.
353-
bool get_tx_size(size_t& out, const tx_link& link,
354-
bool witness) const NOEXCEPT;
355-
bool get_block_size(size_t& out, const header_link& link,
356-
bool witness) const NOEXCEPT;
357-
bool get_block_sizes(size_t& light, size_t& heavy,
358-
const header_link& link) const NOEXCEPT;
359-
bool get_tx_sizes(size_t& light, size_t& heavy,
360-
const tx_link& link) const NOEXCEPT;
353+
bool get_tx_size(size_t& out, const tx_link& link, bool witness) const NOEXCEPT;
354+
bool get_block_size(size_t& out, const header_link& link, bool witness) const NOEXCEPT;
355+
bool get_block_sizes(size_t& light, size_t& heavy, const header_link& link) const NOEXCEPT;
356+
bool get_tx_sizes(size_t& light, size_t& heavy, const tx_link& link) const NOEXCEPT;
361357

362358
/// Heights.
363359
height_link get_height(const hash_digest& key) const NOEXCEPT;
@@ -377,17 +373,17 @@ class query
377373
/// Wire.
378374
/// -----------------------------------------------------------------------
379375

380-
data_chunk get_wire_header(const header_link& link) const NOEXCEPT;
381-
bool get_wire_header(byteflipper& flipper,
382-
const header_link& link) const NOEXCEPT;
383-
384-
data_chunk get_wire_tx(const tx_link& link, bool witness) const NOEXCEPT;
385-
bool get_wire_tx(byteflipper& flipper, const tx_link& link,
376+
bool get_wire_input(byteflipper& flipper, const point_link& link) const NOEXCEPT;
377+
bool get_wire_output(byteflipper& flipper, const output_link& link) const NOEXCEPT;
378+
bool get_wire_witness(byteflipper& flipper, const point_link& link) const NOEXCEPT;
379+
bool get_wire_header(byteflipper& flipper, const header_link& link) const NOEXCEPT;
380+
bool get_wire_tx(byteflipper& flipper, const tx_link& link, bool witness) const NOEXCEPT;
381+
bool get_wire_block(byteflipper& flipper, const header_link& link,
386382
bool witness) const NOEXCEPT;
387383

384+
data_chunk get_wire_header(const header_link& link) const NOEXCEPT;
385+
data_chunk get_wire_tx(const tx_link& link, bool witness) const NOEXCEPT;
388386
data_chunk get_wire_block(const header_link& link, bool witness) const NOEXCEPT;
389-
bool get_wire_block(byteflipper& flipper, const header_link& link,
390-
bool witness) const NOEXCEPT;
391387

392388
/// Objects.
393389
/// -----------------------------------------------------------------------

include/bitcoin/database/tables/archives/header.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -327,10 +327,12 @@ struct header
327327
{
328328
inline bool from_data(reader& source) NOEXCEPT
329329
{
330+
const auto time_bits_nonce_size = 3u * sizeof(uint32_t);
330331
const auto version_size = sizeof(uint32_t);
331332
source.rewind_bytes(sk);
332333
flipper.skip_bytes(version_size);
333334
flipper.write_bytes(source.read_hash());
335+
flipper.skip_bytes(schema::hash + time_bits_nonce_size);
334336
return source;
335337
}
336338

@@ -342,8 +344,8 @@ struct header
342344
{
343345
inline bool from_data(reader& source) NOEXCEPT
344346
{
345-
const auto version_size = sizeof(uint32_t);
346347
const auto time_bits_nonce_size = 3u * sizeof(uint32_t);
348+
const auto version_size = sizeof(uint32_t);
347349
source.skip_bytes(skip_to_parent);
348350
parent_fk = to_parent(source.read_little_endian<link::integer, link::size>());
349351
flipper.write_bytes(source.read_bytes(version_size));

include/bitcoin/database/tables/archives/ins.hpp

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,19 @@ struct ins
132132
const tx::integer parent_fk{};
133133
const system::chain::transaction& tx_{};
134134
};
135+
136+
struct wire_sequence
137+
: public schema::ins
138+
{
139+
inline bool from_data(reader& source) NOEXCEPT
140+
{
141+
const auto sequence_size = sizeof(uint32_t);
142+
flipper.write_bytes(source.read_bytes(sequence_size));
143+
return source;
144+
}
145+
146+
system::byteflipper& flipper;
147+
};
135148
};
136149

137150
} // namespace table

include/bitcoin/database/tables/archives/output.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -239,8 +239,8 @@ struct output
239239
// skip: parent_fk
240240
source.skip_bytes(tx::size);
241241

242-
// value
243-
flipper.write_variable(source.read_variable());
242+
// value (translates from variable to fixed width)
243+
flipper.write_8_bytes_little_endian(source.read_variable());
244244

245245
// script (prefixed)
246246
const auto length = source.read_size();

0 commit comments

Comments
 (0)