Skip to content

Commit ed68490

Browse files
committed
GH-1472 Initial impl of packed_transaction enforcement of extra data and no compression
1 parent 297a4af commit ed68490

8 files changed

Lines changed: 75 additions & 9 deletions

File tree

libraries/chain/controller.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3832,6 +3832,8 @@ struct controller_impl {
38323832

38333833
pending->_block_report.start_time = start;
38343834

3835+
const bool validate_ptrx = is_builtin_activated(builtin_protocol_feature_t::packed_transaction_restrictions);
3836+
38353837
// validated in accept_block()
38363838
std::get<building_block>(pending->_block_stage).trx_mroot_or_receipt_digests() = b->transaction_mroot;
38373839

@@ -3878,6 +3880,11 @@ struct controller_impl {
38783880
: (!!std::get<0>(trx_metas.at(packed_idx))
38793881
? std::get<0>(trx_metas.at(packed_idx))
38803882
: std::get<1>(trx_metas.at(packed_idx)).get()));
3883+
if (validate_ptrx) {
3884+
const auto& ptrx = trx_meta->packed_trx();
3885+
ptrx->validate_no_extra_data();
3886+
ptrx->validate_no_compression();
3887+
}
38813888
trace = push_transaction(trx_meta, fc::time_point::maximum(), fc::microseconds::maximum(),
38823889
receipt.cpu_usage_us, true, 0);
38833890
++packed_idx;

libraries/chain/include/eosio/chain/exceptions.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,10 @@ namespace eosio { namespace chain {
298298
3040017, "Transaction includes disallowed extensions (invalid block)" )
299299
FC_DECLARE_DERIVED_EXCEPTION( tx_resource_exhaustion, transaction_exception,
300300
3040018, "Transaction exceeded transient resource limit" )
301+
FC_DECLARE_DERIVED_EXCEPTION( tx_extra_data_error, transaction_exception,
302+
3040019, "Packed Transaction contains extraneous data" )
303+
FC_DECLARE_DERIVED_EXCEPTION( tx_compression_error, transaction_exception,
304+
3040020, "Packed Transaction compression not allowed" )
301305

302306

303307
FC_DECLARE_DERIVED_EXCEPTION( action_validate_exception, chain_exception,

libraries/chain/include/eosio/chain/protocol_feature_manager.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ enum class builtin_protocol_feature_t : uint32_t {
3939
disable_deferred_trxs_stage_1 = 22,
4040
disable_deferred_trxs_stage_2 = 23,
4141
savanna = 24,
42+
packed_transaction_restrictions = 25,
4243
reserved_private_fork_protocol_features = 500000,
4344
};
4445

libraries/chain/include/eosio/chain/transaction.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,14 @@ namespace eosio { namespace chain {
158158
packed_transaction( bytes&& packed_txn, vector<signature_type>&& sigs, vector<bytes>&& cfd, compression_type _compression );
159159
packed_transaction( transaction&& t, vector<signature_type>&& sigs, bytes&& packed_cfd, compression_type _compression );
160160

161+
// uncompress if compressed, validate no extra data
162+
void normalize();
163+
164+
// throw tx_extra_data_error exception if extra data in packed_transaction
165+
void validate_no_extra_data() const;
166+
// throw tx_compression_error exception if packed_transaction is not compression_type::none
167+
void validate_no_compression() const;
168+
161169
friend bool operator==(const packed_transaction& lhs, const packed_transaction& rhs) {
162170
return std::tie(lhs.signatures, lhs.compression, lhs.packed_context_free_data, lhs.packed_trx) ==
163171
std::tie(rhs.signatures, rhs.compression, rhs.packed_context_free_data, rhs.packed_trx);
@@ -202,6 +210,7 @@ namespace eosio { namespace chain {
202210
// cache unpacked trx, for thread safety do not modify after construction
203211
signed_transaction unpacked_trx;
204212
transaction_id_type trx_id;
213+
bool extra_data = false;
205214
};
206215

207216
using packed_transaction_ptr = std::shared_ptr<const packed_transaction>;

libraries/chain/protocol_feature_manager.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,18 @@ host function call will trigger a transition to the Savanna consensus algorithm.
363363
builtin_protocol_feature_t::disable_deferred_trxs_stage_2
364364
}
365365
} )
366+
( builtin_protocol_feature_t::packed_transaction_restrictions, builtin_protocol_feature_spec{
367+
"PACKED_TRANSACTION_RESTRICTIONS",
368+
fc::variant("b0025cdde6af98ca57393dfd6af8846e67965072a41d1b0d95ede3d59042356c").as<digest_type>(),
369+
// SHA256 hash of the raw message below within the comment delimiters (exclude newline after /*) (do not modify message below).
370+
/*
371+
Builtin protocol feature: PACKED_TRANSACTION_RESTRICTIONS
372+
373+
Do not allow extra-data in packed_transaction.
374+
Do not allow on-chain zlib compressed packed_transaction.
375+
*/
376+
{builtin_protocol_feature_t::savanna}
377+
} )
366378
;
367379

368380

libraries/chain/transaction.cpp

Lines changed: 35 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,14 @@ static vector<bytes> unpack_context_free_data(const bytes& data) {
198198
return fc::raw::unpack< vector<bytes> >(data);
199199
}
200200

201-
static transaction unpack_transaction(const bytes& data) {
202-
return fc::raw::unpack<transaction>(data);
201+
static transaction unpack_transaction(const bytes& data, bool& extra_data) {
202+
try {
203+
transaction tmp;
204+
fc::datastream<const char*> ds( data.data(), size_t(data.size()) );
205+
fc::raw::unpack(ds,tmp);
206+
extra_data = ds.remaining();
207+
return tmp;
208+
} FC_RETHROW_EXCEPTIONS( warn, "error unpacking transaction" )
203209
}
204210

205211
static bytes zlib_decompress(const bytes& data) {
@@ -228,9 +234,9 @@ static vector<bytes> zlib_decompress_context_free_data(const bytes& data) {
228234
return unpack_context_free_data(out);
229235
}
230236

231-
static transaction zlib_decompress_transaction(const bytes& data) {
237+
static transaction zlib_decompress_transaction(const bytes& data, bool& extra_data) {
232238
bytes out = zlib_decompress(data);
233-
return unpack_transaction(out);
239+
return unpack_transaction(out, extra_data);
234240
}
235241

236242
static bytes pack_transaction(const transaction& t) {
@@ -305,6 +311,29 @@ packed_transaction::packed_transaction( transaction&& t, vector<signature_type>&
305311
}
306312
}
307313

314+
void packed_transaction::normalize() {
315+
validate_no_extra_data();
316+
317+
switch( compression ) {
318+
case compression_type::none:
319+
return;
320+
case compression_type::zlib:
321+
break;
322+
default:
323+
EOS_THROW( unknown_transaction_compression, "Unknown transaction compression algorithm" );
324+
}
325+
packed_trx = zlib_decompress(packed_trx);
326+
compression = compression_type::none;
327+
}
328+
329+
void packed_transaction::validate_no_extra_data() const {
330+
EOS_ASSERT(!extra_data, tx_extra_data_error, "Extra-data not allowed in packed_transaction");
331+
}
332+
333+
void packed_transaction::validate_no_compression() const {
334+
EOS_ASSERT(get_compression() == compression_type::none, tx_compression_error, "Compressed packed_transaction not allowed");
335+
}
336+
308337
void packed_transaction::reflector_init()
309338
{
310339
// called after construction, but always on the same thread and before packed_transaction passed to any other threads
@@ -320,10 +349,10 @@ void packed_transaction::local_unpack_transaction(vector<bytes>&& context_free_d
320349
try {
321350
switch( compression ) {
322351
case compression_type::none:
323-
unpacked_trx = signed_transaction( unpack_transaction( packed_trx ), signatures, std::move(context_free_data) );
352+
unpacked_trx = signed_transaction( unpack_transaction( packed_trx, extra_data ), signatures, std::move(context_free_data) );
324353
break;
325354
case compression_type::zlib:
326-
unpacked_trx = signed_transaction( zlib_decompress_transaction( packed_trx ), signatures, std::move(context_free_data) );
355+
unpacked_trx = signed_transaction( zlib_decompress_transaction( packed_trx, extra_data ), signatures, std::move(context_free_data) );
327356
break;
328357
default:
329358
EOS_THROW( unknown_transaction_compression, "Unknown transaction compression algorithm" );

plugins/chain_plugin/chain_plugin.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2119,13 +2119,14 @@ void read_write::push_block(read_write::push_block_params&& params, next_functio
21192119

21202120
void read_write::push_transaction(const read_write::push_transaction_params& params, next_function<read_write::push_transaction_results> next) {
21212121
try {
2122-
auto pretty_input = std::make_shared<packed_transaction>();
2122+
auto ptrx = std::make_shared<packed_transaction>();
21232123
auto resolver = caching_resolver(make_resolver(db, abi_serializer_max_time, throw_on_yield::yes));
21242124
try {
2125-
abi_serializer::from_variant(params, *pretty_input, resolver, abi_serializer_max_time);
2125+
abi_serializer::from_variant(params, *ptrx, resolver, abi_serializer_max_time);
21262126
} EOS_RETHROW_EXCEPTIONS(chain::packed_transaction_type_exception, "Invalid packed transaction")
2127+
ptrx->normalize();
21272128

2128-
app().get_method<incoming::methods::transaction_async>()(pretty_input, true, transaction_metadata::trx_type::input, false,
2129+
app().get_method<incoming::methods::transaction_async>()(ptrx, true, transaction_metadata::trx_type::input, false,
21292130
[this, next](const next_function_variant<transaction_trace_ptr>& result) -> void {
21302131
if (std::holds_alternative<fc::exception_ptr>(result)) {
21312132
next(std::get<fc::exception_ptr>(result));
@@ -2247,6 +2248,7 @@ void api_base::send_transaction_gen(API &api, send_transaction_params_t params,
22472248
try {
22482249
abi_serializer::from_variant(params.transaction, *ptrx, resolver, api.abi_serializer_max_time);
22492250
} EOS_RETHROW_EXCEPTIONS(packed_transaction_type_exception, "Invalid packed transaction")
2251+
ptrx->normalize();
22502252

22512253
bool retry = false;
22522254
std::optional<uint16_t> retry_num_blocks;

plugins/net_plugin/net_plugin.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3229,6 +3229,8 @@ namespace eosio {
32293229
const auto& tid = ptr->id();
32303230
peer_dlog( this, "received packed_transaction ${id}", ("id", tid) );
32313231

3232+
ptr->normalize();
3233+
32323234
if (message_length < def_trx_notice_min_size) {
32333235
// transfer packed transaction is ~170 bytes, transaction notice is 41 bytes
32343236
fc_dlog( logger, "trx notice not sent, trx size ${s}", ("s", message_length));

0 commit comments

Comments
 (0)