Wire Sysio Wire Sysion 1.0.0
Loading...
Searching...
No Matches
transaction.cpp
Go to the documentation of this file.
1#include <fc/io/raw.hpp>
2#include <fc/bitutil.hpp>
3#include <algorithm>
4
5#include <boost/range/adaptor/transformed.hpp>
6#include <boost/iostreams/filtering_stream.hpp>
7#include <boost/iostreams/device/back_inserter.hpp>
8#include <boost/iostreams/filter/zlib.hpp>
9
13
14namespace sysio { namespace chain {
15
18 "deferred_transaction_generation_context expects FC to support reflector_init" );
19
20
21 SYS_ASSERT( sender != account_name(), ill_formed_deferred_transaction_generation_context,
22 "Deferred transaction generation context extension must have a non-empty sender account",
23 );
24}
25
27 ref_block_num = fc::endian_reverse_u32(reference_block._hash[0]);
28 ref_block_prefix = reference_block._hash[1];
29}
30
31bool transaction_header::verify_reference_block( const block_id_type& reference_block )const {
32 return ref_block_num == (decltype(ref_block_num))fc::endian_reverse_u32(reference_block._hash[0]) &&
33 ref_block_prefix == (decltype(ref_block_prefix))reference_block._hash[1];
34}
35
38 "declared max_net_usage_words overflows when expanded to max net usage" );
39}
40
43 fc::raw::pack( enc, *this );
44 return enc.result();
45}
46
49 fc::raw::pack( enc, chain_id );
50 fc::raw::pack( enc, *this );
51 if( cfd.size() ) {
53 } else {
54 fc::raw::pack( enc, digest_type() );
55 }
56 return enc.result();
57}
58
60 const chain_id_type& chain_id, fc::time_point deadline, const vector<bytes>& cfd,
61 flat_set<public_key_type>& recovered_pub_keys, bool allow_duplicate_keys)const
62{ try {
63 auto start = fc::time_point::now();
64 recovered_pub_keys.clear();
65 const digest_type digest = sig_digest(chain_id, cfd);
66
67 for(const signature_type& sig : signatures) {
68 auto now = fc::time_point::now();
69 SYS_ASSERT( now < deadline, tx_cpu_usage_exceeded, "transaction signature verification executed for too long ${time}us",
70 ("time", now - start)("now", now)("deadline", deadline)("start", start) );
71 auto[ itr, successful_insertion ] = recovered_pub_keys.emplace( sig, digest );
72 SYS_ASSERT( allow_duplicate_keys || successful_insertion, tx_duplicate_sig,
73 "transaction includes more than one signature signed using the same key associated with public key: ${key}",
74 ("key", *itr ) );
75 }
76
77 return fc::time_point::now() - start;
79
80flat_multimap<uint16_t, transaction_extension> transaction::validate_and_extract_extensions()const {
82
83 flat_multimap<uint16_t, transaction_extension> results;
84
85 uint16_t id_type_lower_bound = 0;
86
87 for( size_t i = 0; i < transaction_extensions.size(); ++i ) {
88 const auto& e = transaction_extensions[i];
89 auto id = e.first;
90
91 SYS_ASSERT( id >= id_type_lower_bound, invalid_transaction_extension,
92 "Transaction extensions are not in the correct order (ascending id types required)"
93 );
94
95 auto iter = results.emplace(std::piecewise_construct,
96 std::forward_as_tuple(id),
97 std::forward_as_tuple()
98 );
99
100 auto match = decompose_t::extract<transaction_extension>( id, e.second, iter->second );
101 SYS_ASSERT( match, invalid_transaction_extension,
102 "Transaction extension with id type ${id} is not supported",
103 ("id", id)
104 );
105
106 if( match->enforce_unique ) {
107 SYS_ASSERT( i == 0 || id > id_type_lower_bound, invalid_transaction_extension,
108 "Transaction extension with id type ${id} is not allowed to repeat",
109 ("id", id)
110 );
111 }
112
113 id_type_lower_bound = id;
114 }
115
116 return results;
117}
118
120 signatures.push_back(key.sign(sig_digest(chain_id, context_free_data)));
121 return signatures.back();
122}
123
125 return key.sign(sig_digest(chain_id, context_free_data));
126}
127
130 flat_set<public_key_type>& recovered_pub_keys,
131 bool allow_duplicate_keys)const
132{
133 return transaction::get_signature_keys(signatures, chain_id, deadline, context_free_data, recovered_pub_keys, allow_duplicate_keys);
134}
135
137 uint64_t size = config::fixed_net_overhead_of_packed_trx;
138 size += packed_trx.size();
139 SYS_ASSERT( size <= std::numeric_limits<uint32_t>::max(), tx_too_big, "packed_transaction is too big" );
140 return static_cast<uint32_t>(size);
141}
142
144 uint64_t size = fc::raw::pack_size(signatures);
145 size += packed_context_free_data.size();
146 SYS_ASSERT( size <= std::numeric_limits<uint32_t>::max(), tx_too_big, "packed_transaction is too big" );
147 return static_cast<uint32_t>(size);
148}
149
151 // transaction is stored packed (only transaction minus signed_transaction members) and unpacked (signed_transaction),
152 // double packed size, packed cfd size, and signature size to account for signed_transaction unpacked_trx size
153 return sizeof(*this) +
154 (signatures.size() * sizeof( signature_type )) * 2 +
155 packed_context_free_data.size() * 2 +
156 packed_trx.size() * 2;
157}
158
159
161 digest_type::encoder prunable;
162 fc::raw::pack( prunable, signatures );
163 fc::raw::pack( prunable, packed_context_free_data );
164
166 fc::raw::pack( enc, compression );
167 fc::raw::pack( enc, packed_trx );
168 fc::raw::pack( enc, prunable.result() );
169
170 return enc.result();
171}
172
173namespace bio = boost::iostreams;
174
175template<size_t Limit>
177 using char_type = char;
178 using category = bio::multichar_output_filter_tag;
179
180 template<typename Sink>
181 size_t write(Sink &sink, const char* s, size_t count)
182 {
183 SYS_ASSERT(_total + count <= Limit, tx_decompression_error, "Exceeded maximum decompressed transaction size");
184 _total += count;
185 return bio::write(sink, s, count);
186 }
187
188 size_t _total = 0;
189};
190
191static vector<bytes> unpack_context_free_data(const bytes& data) {
192 if( data.size() == 0 )
193 return vector<bytes>();
194
195 return fc::raw::unpack< vector<bytes> >(data);
196}
197
198static transaction unpack_transaction(const bytes& data) {
199 return fc::raw::unpack<transaction>(data);
200}
201
202static bytes zlib_decompress(const bytes& data) {
203 try {
204 bytes out;
205 bio::filtering_ostream decomp;
206 decomp.push(bio::zlib_decompressor());
207 decomp.push(read_limiter<1*1024*1024>()); // limit to 1 meg decompressed for zip bomb protections
208 decomp.push(bio::back_inserter(out));
209 bio::write(decomp, data.data(), data.size());
210 bio::close(decomp);
211 return out;
212 } catch( fc::exception& er ) {
213 throw;
214 } catch( ... ) {
215 fc::unhandled_exception er( FC_LOG_MESSAGE( warn, "internal decompression error"), std::current_exception() );
216 throw er;
217 }
218}
219
220static vector<bytes> zlib_decompress_context_free_data(const bytes& data) {
221 if( data.size() == 0 )
222 return vector<bytes>();
223
224 bytes out = zlib_decompress(data);
225 return unpack_context_free_data(out);
226}
227
228static transaction zlib_decompress_transaction(const bytes& data) {
229 bytes out = zlib_decompress(data);
230 return unpack_transaction(out);
231}
232
233static bytes pack_transaction(const transaction& t) {
234 return fc::raw::pack(t);
235}
236
237static bytes pack_context_free_data(const vector<bytes>& cfd ) {
238 if( cfd.size() == 0 )
239 return bytes();
240
241 return fc::raw::pack(cfd);
242}
243
244static bytes zlib_compress_context_free_data(const vector<bytes>& cfd ) {
245 if( cfd.size() == 0 )
246 return bytes();
247
248 bytes in = pack_context_free_data(cfd);
249 bytes out;
250 bio::filtering_ostream comp;
251 comp.push(bio::zlib_compressor(bio::zlib::best_compression));
252 comp.push(bio::back_inserter(out));
253 bio::write(comp, in.data(), in.size());
254 bio::close(comp);
255 return out;
256}
257
258static bytes zlib_compress_transaction(const transaction& t) {
259 bytes in = pack_transaction(t);
260 bytes out;
261 bio::filtering_ostream comp;
262 comp.push(bio::zlib_compressor(bio::zlib::best_compression));
263 comp.push(bio::back_inserter(out));
264 bio::write(comp, in.data(), in.size());
265 bio::close(comp);
266 return out;
267}
268
270{
271 try {
272 switch(compression) {
274 return packed_trx;
276 return zlib_decompress(packed_trx);
277 default:
278 SYS_THROW(unknown_transaction_compression, "Unknown transaction compression algorithm");
279 }
280 } FC_CAPTURE_AND_RETHROW((compression)(packed_trx))
281}
282
284:signatures(std::move(sigs))
285,compression(_compression)
286,packed_context_free_data(std::move(packed_cfd))
287,packed_trx(std::move(packed_txn))
288{
289 local_unpack_transaction({});
290 if( !packed_context_free_data.empty() ) {
291 local_unpack_context_free_data();
292 }
293}
294
296:signatures(std::move(sigs))
297,compression(_compression)
298,packed_trx(std::move(packed_txn))
299{
300 local_unpack_transaction( std::move( cfd ) );
301 if( !unpacked_trx.context_free_data.empty() ) {
302 local_pack_context_free_data();
303 }
304}
305
307:signatures(std::move(sigs))
308,compression(_compression)
309,packed_context_free_data(std::move(packed_cfd))
310,unpacked_trx(std::move(t), signatures, {})
311,trx_id(unpacked_trx.id())
312{
313 local_pack_transaction();
314 if( !packed_context_free_data.empty() ) {
315 local_unpack_context_free_data();
316 }
317}
318
319void packed_transaction::reflector_init()
320{
321 // called after construction, but always on the same thread and before packed_transaction passed to any other threads
323 "FC unpack needs to call reflector_init otherwise unpacked_trx will not be initialized");
324 SYS_ASSERT( unpacked_trx.expiration == time_point_sec(), tx_decompression_error, "packed_transaction already unpacked" );
325 local_unpack_transaction({});
326 local_unpack_context_free_data();
327}
328
329void packed_transaction::local_unpack_transaction(vector<bytes>&& context_free_data)
330{
331 try {
332 switch( compression ) {
334 unpacked_trx = signed_transaction( unpack_transaction( packed_trx ), signatures, std::move(context_free_data) );
335 break;
337 unpacked_trx = signed_transaction( zlib_decompress_transaction( packed_trx ), signatures, std::move(context_free_data) );
338 break;
339 default:
340 SYS_THROW( unknown_transaction_compression, "Unknown transaction compression algorithm" );
341 }
342 trx_id = unpacked_trx.id();
343 } FC_CAPTURE_AND_RETHROW( (compression) )
344}
345
346void packed_transaction::local_unpack_context_free_data()
347{
348 try {
349 SYS_ASSERT(unpacked_trx.context_free_data.empty(), tx_decompression_error, "packed_transaction.context_free_data not empty");
350 switch( compression ) {
352 unpacked_trx.context_free_data = unpack_context_free_data( packed_context_free_data );
353 break;
355 unpacked_trx.context_free_data = zlib_decompress_context_free_data( packed_context_free_data );
356 break;
357 default:
358 SYS_THROW( unknown_transaction_compression, "Unknown transaction compression algorithm" );
359 }
360 } FC_CAPTURE_AND_RETHROW( (compression) )
361}
362
363void packed_transaction::local_pack_transaction()
364{
365 try {
366 switch(compression) {
368 packed_trx = pack_transaction(unpacked_trx);
369 break;
371 packed_trx = zlib_compress_transaction(unpacked_trx);
372 break;
373 default:
374 SYS_THROW(unknown_transaction_compression, "Unknown transaction compression algorithm");
375 }
376 } FC_CAPTURE_AND_RETHROW((compression))
377}
378
379void packed_transaction::local_pack_context_free_data()
380{
381 try {
382 switch(compression) {
384 packed_context_free_data = pack_context_free_data(unpacked_trx.context_free_data);
385 break;
387 packed_context_free_data = zlib_compress_context_free_data(unpacked_trx.context_free_data);
388 break;
389 default:
390 SYS_THROW(unknown_transaction_compression, "Unknown transaction compression algorithm");
391 }
392 } FC_CAPTURE_AND_RETHROW((compression))
393}
394
395
396} } // sysio::chain
#define SYS_THROW(exc_type, FORMAT,...)
#define SYS_ASSERT(expr, exc_type, FORMAT,...)
Definition exceptions.hpp:7
Used to generate a useful error report when an exception is thrown.
Definition exception.hpp:58
static sha256 hash(const char *d, uint32_t dlen)
Definition sha256.cpp:44
uint64_t _hash[4]
Definition sha256.hpp:100
static time_point now()
Definition time.cpp:14
re-thrown whenever an unhandled exception is caught.Any exceptions thrown by 3rd party libraries that...
#define FC_CAPTURE_AND_RETHROW(...)
int * count
#define FC_LOG_MESSAGE(LOG_LEVEL, FORMAT,...)
A helper method for generating log messages.
void unpack(Stream &s, std::deque< T > &value)
Definition raw.hpp:540
constexpr bool has_feature_reflector_init_on_unpacked_reflected_types
Definition raw.hpp:440
void pack(Stream &s, const std::deque< T > &value)
Definition raw.hpp:531
size_t pack_size(const T &v)
Definition raw.hpp:671
fc::sha256 digest(const T &value)
Definition digest.hpp:9
uint32_t endian_reverse_u32(uint32_t x)
Definition bitutil.hpp:19
Definition name.hpp:106
checksum_type digest_type
Definition types.hpp:237
key Invalid authority Invalid transaction Invalid block ID Invalid packed transaction Invalid chain ID Invalid symbol Signature type is not a currently activated type Block can not be found Unlinkable block Block does not guarantee concurrent execution without conflicts Block exhausted allowed resources Block is from the future Block is not signed by expected producer Block includes an ill formed protocol feature activation extension Block includes an ill formed additional block signature extension transaction_exception
name account_name
Definition types.hpp:120
vector< char > bytes
Definition types.hpp:243
unsigned short uint16_t
Definition stdint.h:125
unsigned int uint32_t
Definition stdint.h:126
#define UINT32_MAX
Definition stdint.h:188
unsigned __int64 uint64_t
Definition stdint.h:136
uint32_t value
Definition varint.hpp:17
digest_type packed_digest() const
size_t write(Sink &sink, const char *s, size_t count)
bio::multichar_output_filter_tag category
vector< bytes > context_free_data
for each context-free action, there is an entry here
fc::microseconds get_signature_keys(const chain_id_type &chain_id, fc::time_point deadline, flat_set< public_key_type > &recovered_pub_keys, bool allow_duplicate_keys=false) const
const signature_type & sign(const private_key_type &key, const chain_id_type &chain_id)
void set_reference_block(const block_id_type &reference_block)
uint16_t ref_block_num
specifies a block num in the last 2^16 blocks.
uint32_t ref_block_prefix
specifies the lower 32 bits of the blockid at get_ref_blocknum
bool verify_reference_block(const block_id_type &reference_block) const
time_point_sec expiration
the time at which a transaction expires
extensions_type transaction_extensions
fc::microseconds get_signature_keys(const vector< signature_type > &signatures, const chain_id_type &chain_id, fc::time_point deadline, const vector< bytes > &cfd, flat_set< public_key_type > &recovered_pub_keys, bool allow_duplicate_keys=false) const
digest_type sig_digest(const chain_id_type &chain_id, const vector< bytes > &cfd=vector< bytes >()) const
transaction_id_type id() const
flat_multimap< uint16_t, transaction_extension > validate_and_extract_extensions() const
account_query_db::get_accounts_by_authorizers_result results
char * s