OpenCBDC Transaction Processor
Loading...
Searching...
No Matches
parsec/agent/runners/evm/serialization.cpp
Go to the documentation of this file.
1// Copyright (c) 2022 MIT Digital Currency Initiative,
2// Federal Reserve Bank of Boston
3// Distributed under the MIT software license, see the accompanying
4// file COPYING or http://www.opensource.org/licenses/mit-license.php.
5
6#include "serialization.hpp"
7
8#include "crypto/sha256.h"
9#include "format.hpp"
10#include "hash.hpp"
11#include "messages.hpp"
12#include "rlp.hpp"
13#include "util.hpp"
14#include "util/common/hash.hpp"
16
17#include <optional>
18#include <secp256k1.h>
19
22 uint64_t chain_id) -> cbdc::hash_t {
23 auto tx_ser = tx_encode(tx, chain_id);
24 return keccak_data(tx_ser.data(), tx_ser.size());
25 }
26
27 auto is_valid_rlp_tx(evm_tx_type type, const rlp_value& rlp_tx) -> bool {
28 static constexpr size_t elements_in_dynamic_fee_transaction = 12;
29 static constexpr size_t elements_in_access_list_transaction = 11;
30 static constexpr size_t elements_in_legacy_transaction = 9;
31
33 && rlp_tx.size() != elements_in_dynamic_fee_transaction) {
34 return false;
35 }
37 && rlp_tx.size() != elements_in_access_list_transaction) {
38 return false;
39 }
40 if(type == evm_tx_type::legacy
41 && rlp_tx.size() != elements_in_legacy_transaction) {
42 return false;
43 }
44 return true;
45 }
46
48 const cbdc::buffer& buf,
49 const std::shared_ptr<logging::log>& logger,
50 const std::shared_ptr<cbdc::parsec::agent::runner::evm_tx>& tx)
51 -> std::optional<rlp_value> {
52 uint8_t type_byte{};
53 std::memcpy(&type_byte, buf.data_at(0), 1);
54 size_t rlp_offset = 0;
55 if(type_byte >= 1 && type_byte <= 2) {
56 tx->m_type = static_cast<evm_tx_type>(type_byte);
57 rlp_offset = 1;
58 }
59
60 auto buf_cpy = cbdc::buffer();
61 buf_cpy.append(buf.data_at(rlp_offset), buf.size() - rlp_offset);
62 auto maybe_rlp_tx = cbdc::from_buffer<rlp_value>(buf_cpy);
63 if(!maybe_rlp_tx.has_value()) {
64 return std::nullopt;
65 }
66 auto rlp_tx = maybe_rlp_tx.value();
67
68 if(!is_valid_rlp_tx(tx->m_type, rlp_tx)) {
69 if(logger) {
70 logger->error("tx is not valid rlp");
71 }
72 return std::nullopt;
73 }
74
75 return rlp_tx;
76 }
77
78 auto tx_decode(const cbdc::buffer& buf,
79 const std::shared_ptr<logging::log>& logger,
80 uint64_t chain_id)
81 -> std::optional<
82 std::shared_ptr<cbdc::parsec::agent::runner::evm_tx>> {
83 auto tx = std::make_shared<cbdc::parsec::agent::runner::evm_tx>();
84 auto maybe_rlp_tx = check_tx_decode(buf, logger, tx);
85 if(!maybe_rlp_tx.has_value()) {
86 return std::nullopt;
87 }
88 auto rlp_tx = maybe_rlp_tx.value();
89
90 auto element = size_t{};
91 if(tx->m_type == evm_tx_type::dynamic_fee
92 || tx->m_type == evm_tx_type::access_list) {
93 auto tx_chain_id
94 = rlp_tx.value_at(element++).value<evmc::uint256be>();
95 if(to_uint64(tx_chain_id) != chain_id) {
96 if(logger) {
97 logger->error("tx is wrong chain ID");
98 }
99 return std::nullopt;
100 }
101 }
102
103 tx->m_nonce = rlp_tx.value_at(element++).value<evmc::uint256be>();
104
105 if(tx->m_type == evm_tx_type::dynamic_fee) {
106 tx->m_gas_tip_cap
107 = rlp_tx.value_at(element++).value<evmc::uint256be>();
108 tx->m_gas_fee_cap
109 = rlp_tx.value_at(element++).value<evmc::uint256be>();
110 } else {
111 tx->m_gas_price
112 = rlp_tx.value_at(element++).value<evmc::uint256be>();
113 }
114 tx->m_gas_limit = rlp_tx.value_at(element++).value<evmc::uint256be>();
115 auto to = rlp_tx.value_at(element++);
116 if(to.size() > 0) {
117 tx->m_to = to.value<evmc::address>();
118 }
119 tx->m_value = rlp_tx.value_at(element++).value<evmc::uint256be>();
120 auto data = rlp_tx.value_at(element++);
121 tx->m_input.resize(data.size());
122 std::memcpy(tx->m_input.data(), data.data(), data.size());
123
124 if(tx->m_type == evm_tx_type::dynamic_fee
125 || tx->m_type == evm_tx_type::access_list) {
126 auto rlp_access_list = rlp_tx.value_at(element++);
127 auto access_list = rlp_decode_access_list(rlp_access_list);
128 if(access_list.has_value()) {
129 tx->m_access_list = access_list.value();
130 }
131 }
132
133 tx->m_sig.m_v = rlp_tx.value_at(element++).value<evmc::uint256be>();
134 if(tx->m_type == evm_tx_type::legacy) {
135 auto small_v = to_uint64(tx->m_sig.m_v);
136 if(small_v >= eip155_v_offset) {
137 auto tx_chain_id = (small_v - eip155_v_offset) / 2;
138 if(tx_chain_id != chain_id) {
139 if(logger) {
140 logger->error("tx is wrong chain ID (",
141 tx_chain_id,
142 ") where expected (",
143 chain_id);
144 }
145 return std::nullopt;
146 }
147 }
148 }
149
150 tx->m_sig.m_r = rlp_tx.value_at(element++).value<evmc::uint256be>();
151 tx->m_sig.m_s = rlp_tx.value_at(element++).value<evmc::uint256be>();
152 return tx;
153 }
154
156 uint64_t chain_id,
157 bool for_sighash) -> cbdc::buffer {
158 auto buf = cbdc::buffer();
159 auto ser = cbdc::buffer_serializer(buf);
160
161 auto data_buf = cbdc::buffer();
162 data_buf.extend(tx.m_input.size());
163 std::memcpy(data_buf.data(), tx.m_input.data(), tx.m_input.size());
164
165 auto to = make_rlp_value(evmc::uint256be(0), true);
166 if(tx.m_to.has_value()) {
167 to = make_rlp_value(tx.m_to.value(), false);
168 }
169
170 auto access_list = rlp_encode_access_list(tx.m_access_list);
171
172 auto rlp_tx = rlp_value(rlp_value_type::array);
173 if(tx.m_type == evm_tx_type::dynamic_fee
174 || tx.m_type == evm_tx_type::access_list) {
175 ser << std::byte(tx.m_type);
176 rlp_tx.push_back(make_rlp_value(evmc::uint256be(chain_id), true));
177 }
178
179 rlp_tx.push_back(make_rlp_value(tx.m_nonce, true));
180 if(tx.m_type == evm_tx_type::dynamic_fee) {
181 rlp_tx.push_back(make_rlp_value(tx.m_gas_tip_cap, true));
182 rlp_tx.push_back(make_rlp_value(tx.m_gas_fee_cap, true));
183 } else {
184 rlp_tx.push_back(make_rlp_value(tx.m_gas_price, true));
185 }
186 rlp_tx.push_back(make_rlp_value(tx.m_gas_limit, true));
187 rlp_tx.push_back(to);
188 rlp_tx.push_back(make_rlp_value(tx.m_value, true));
189 rlp_tx.push_back(rlp_value(data_buf));
190 if(tx.m_type == evm_tx_type::dynamic_fee
191 || tx.m_type == evm_tx_type::access_list) {
192 rlp_tx.push_back(access_list);
193 }
194 if(for_sighash && tx.m_type == evm_tx_type::legacy) {
195 rlp_tx.push_back(make_rlp_value(evmc::uint256be(chain_id), true));
196 rlp_tx.push_back(make_rlp_value(uint32_t(0), true));
197 rlp_tx.push_back(make_rlp_value(uint32_t(0), true));
198 } else if(!for_sighash) {
199 rlp_tx.push_back(make_rlp_value(tx.m_sig.m_v, true));
200 rlp_tx.push_back(make_rlp_value(tx.m_sig.m_r, true));
201 rlp_tx.push_back(make_rlp_value(tx.m_sig.m_s, true));
202 }
203
204 ser << rlp_tx;
205 return buf;
206 }
207
208 auto dryrun_tx_from_json(const Json::Value& json, uint64_t chain_id)
209 -> std::optional<
210 std::shared_ptr<cbdc::parsec::agent::runner::evm_dryrun_tx>> {
211 auto tx = tx_from_json(json, chain_id);
212 if(!tx) {
213 return std::nullopt;
214 }
215
216 auto drtx
217 = std::make_shared<cbdc::parsec::agent::runner::evm_dryrun_tx>();
218 drtx->m_tx = *tx.value();
219 auto maybe_from = address_from_json(json["from"]);
220 if(maybe_from) {
221 drtx->m_from = maybe_from.value();
222 }
223 return drtx;
224 }
225
226 auto address_from_json(const Json::Value& addr)
227 -> std::optional<evmc::address> {
228 if(!addr.empty() && addr.isString()) {
229 auto addr_str = addr.asString();
230 if(addr_str.size() > 2) {
231 auto maybe_addr_buf
232 = cbdc::buffer::from_hex(addr_str.substr(2));
233 if(maybe_addr_buf) {
234 auto maybe_addr = cbdc::from_buffer<evmc::address>(
235 maybe_addr_buf.value());
236 if(maybe_addr) {
237 return maybe_addr.value();
238 }
239 }
240 }
241 }
242 return std::nullopt;
243 }
244
245 auto uint256be_from_json(const Json::Value& val)
246 -> std::optional<evmc::uint256be> {
247 auto maybe_val_buf = buffer_from_json(val);
248 if(maybe_val_buf) {
249 auto maybe_val
250 = cbdc::from_buffer<evmc::uint256be>(maybe_val_buf.value());
251 if(maybe_val) {
252 return maybe_val.value();
253 }
254 }
255 return std::nullopt;
256 }
257
258 auto buffer_from_json(const Json::Value& val)
259 -> std::optional<cbdc::buffer> {
260 if(!val.empty() && val.isString()) {
261 auto val_str = val.asString();
262 if(val_str.size() > 2) {
263 return cbdc::buffer::from_hex(val_str.substr(2));
264 }
265 }
266 return std::nullopt;
267 }
268
269 auto uint256be_or_default(const Json::Value& val, evmc::uint256be def)
270 -> evmc::uint256be {
271 auto maybe_ui256 = uint256be_from_json(val);
272 if(maybe_ui256) {
273 return maybe_ui256.value();
274 }
275 return def;
276 }
277
278 auto raw_tx_from_json(const Json::Value& param) -> std::optional<
279 std::shared_ptr<cbdc::parsec::agent::runner::evm_tx>> {
280 if(!param.isString()) {
281 return std::nullopt;
282 }
283 auto params_str = param.asString();
284 auto maybe_raw_tx = cbdc::buffer::from_hex(params_str.substr(2));
285 if(!maybe_raw_tx.has_value()) {
286 return std::nullopt;
287 }
288
289 const std::shared_ptr<logging::log> nolog = nullptr;
290 return tx_decode(maybe_raw_tx.value(), nolog);
291 }
292
293 auto tx_from_json(const Json::Value& json, uint64_t /*chain_id*/)
294 -> std::optional<
295 std::shared_ptr<cbdc::parsec::agent::runner::evm_tx>> {
296 auto tx = std::make_shared<cbdc::parsec::agent::runner::evm_tx>();
297 tx->m_type = evm_tx_type::legacy;
298 if(!json["type"].empty() && json["type"].isNumeric()) {
299 tx->m_type = static_cast<evm_tx_type>(json["type"].asInt());
300 }
301
302 auto maybe_to = address_from_json(json["to"]);
303 if(maybe_to) {
304 tx->m_to = maybe_to.value();
305 }
306
307 tx->m_value = uint256be_or_default(json["value"], evmc::uint256be(0));
308 tx->m_nonce = uint256be_or_default(json["nonce"], evmc::uint256be(0));
309 tx->m_gas_price
310 = uint256be_or_default(json["gasPrice"], evmc::uint256be(0));
311 tx->m_gas_limit
312 = uint256be_or_default(json["gas"], evmc::uint256be(0));
313 tx->m_gas_tip_cap = uint256be_or_default(json["maxPriorityFeePerGas"],
314 evmc::uint256be(0));
315
316 tx->m_gas_fee_cap
317 = uint256be_or_default(json["maxFeePerGas"], evmc::uint256be(0));
318
319 // TODO
320 tx->m_access_list = evm_access_list{};
321
322 auto maybe_input = buffer_from_json(json["data"]);
323 if(maybe_input) {
324 tx->m_input.resize(maybe_input->size());
325 std::memcpy(tx->m_input.data(),
326 maybe_input->data(),
327 maybe_input->size());
328 }
329
330 tx->m_sig.m_r = uint256be_or_default(json["r"], evmc::uint256be(0));
331 tx->m_sig.m_s = uint256be_or_default(json["s"], evmc::uint256be(0));
332 tx->m_sig.m_v = uint256be_or_default(json["v"], evmc::uint256be(0));
333
334 return tx;
335 }
336
338 const std::shared_ptr<secp256k1_context>& ctx)
339 -> Json::Value {
340 auto res = Json::Value();
341 res["type"] = to_hex_trimmed(
342 evmc::uint256be(static_cast<uint64_t>(tx.m_type)));
343
344 if(tx.m_to.has_value()) {
345 res["to"] = "0x" + to_hex(tx.m_to.value());
346 }
347
348 res["value"] = to_hex_trimmed(tx.m_value);
349 res["nonce"] = to_hex_trimmed(tx.m_nonce);
350 res["gasPrice"] = to_hex_trimmed(tx.m_gas_price);
351 res["gas"] = to_hex_trimmed(tx.m_gas_limit);
352
353 if(tx.m_type == evm_tx_type::dynamic_fee) {
354 res["maxPriorityFeePerGas"] = to_hex_trimmed(tx.m_gas_tip_cap);
355 res["maxFeePerGas"] = to_hex_trimmed(tx.m_gas_fee_cap);
356 }
357
358 if(!tx.m_input.empty()) {
359 auto buf = cbdc::buffer();
360 buf.extend(tx.m_input.size());
361 std::memcpy(buf.data(), tx.m_input.data(), tx.m_input.size());
362 res["input"] = buf.to_hex_prefixed();
363 } else {
364 res["input"] = "0x";
365 }
366
367 if(tx.m_type != evm_tx_type::legacy) {
368 res["accessList"] = access_list_to_json(tx.m_access_list);
369 }
370
371 res["hash"] = "0x" + cbdc::to_string(tx_id(tx));
372 res["r"] = to_hex_trimmed(tx.m_sig.m_r);
373 res["s"] = to_hex_trimmed(tx.m_sig.m_s);
374 res["v"] = to_hex_trimmed(tx.m_sig.m_v);
375
376 res["chainId"] = to_hex_trimmed(evmc::uint256be(opencbdc_chain_id));
377
378 auto maybe_from_addr = check_signature(tx, ctx);
379 if(maybe_from_addr) {
380 res["from"] = "0x" + to_hex(maybe_from_addr.value());
381 }
382
383 return res;
384 }
385
387 const std::shared_ptr<secp256k1_context>& ctx)
388 -> Json::Value {
389 auto res = Json::Value();
390
391 auto txid = tx_id(rcpt.m_tx);
392
393 res["transaction"] = tx_to_json(rcpt.m_tx, ctx);
394 res["from"] = res["transaction"]["from"];
395 res["to"] = res["transaction"]["to"];
396 if(rcpt.m_create_address.has_value()) {
397 res["contractAddress"]
398 = "0x" + to_hex(rcpt.m_create_address.value());
399 }
400 res["gasUsed"] = to_hex_trimmed(rcpt.m_gas_used);
401 res["cumulativeGasUsed"] = to_hex_trimmed(rcpt.m_gas_used);
402 res["logs"] = Json::Value(Json::arrayValue);
403 res["status"] = rcpt.m_success ? "0x1" : "0x0";
404
405 auto bloom = cbdc::buffer();
406 constexpr auto bits_in_32_bytes = 256;
407 bloom.extend(bits_in_32_bytes);
408 for(auto& l : rcpt.m_logs) {
409 res["logs"].append(tx_log_to_json(l, rcpt.m_ticket_number, txid));
410 add_to_bloom(bloom, cbdc::make_buffer(l.m_addr));
411 for(auto& t : l.m_topics) {
413 }
414 }
415
416 res["logsBloom"] = bloom.to_hex_prefixed();
417
418 if(!rcpt.m_output_data.empty()) {
419 auto buf = cbdc::buffer();
420 buf.extend(rcpt.m_output_data.size());
421 std::memcpy(buf.data(),
422 rcpt.m_output_data.data(),
423 rcpt.m_output_data.size());
424 res["output_data"] = buf.to_hex_prefixed();
425 }
426
427 res["success"] = "0x1";
428 res["transactionIndex"] = "0x0";
429 res["transactionHash"] = "0x" + to_string(txid);
430 auto tn256 = evmc::uint256be(rcpt.m_ticket_number);
431 res["blockHash"] = "0x" + to_hex(tn256);
432 res["blockNumber"] = to_hex_trimmed(tn256);
433 return res;
434 }
435
438 cbdc::hash_t txid) -> Json::Value {
439 auto res = Json::Value();
440 res["address"] = "0x" + to_hex(log.m_addr);
441 if(!log.m_data.empty()) {
442 auto buf = cbdc::buffer();
443 buf.extend(log.m_data.size());
444 std::memcpy(buf.data(), log.m_data.data(), log.m_data.size());
445 res["data"] = buf.to_hex_prefixed();
446 } else {
447 res["data"] = "0x";
448 }
449 res["topics"] = Json::Value(Json::arrayValue);
450 for(auto& t : log.m_topics) {
451 res["topics"].append("0x" + to_hex(t));
452 }
453
454 auto tn256 = evmc::uint256be(tn);
455 res["blockHash"] = "0x" + to_hex(tn256);
456 res["blockNumber"] = to_hex_trimmed(tn256);
457
458 res["transactionIndex"] = "0x0";
459 res["transactionHash"] = "0x" + cbdc::to_string(txid);
460 res["logIndex"] = "0x0";
461 return res;
462 }
463
465 -> Json::Value {
466 auto res = Json::Value(Json::arrayValue);
467 for(auto& tuple : al) {
468 auto json_tuple = Json::Value();
469 json_tuple["address"] = to_hex(tuple.m_address);
470 json_tuple["storageKeys"] = Json::Value(Json::arrayValue);
471 for(auto& k : tuple.m_storage_keys) {
472 json_tuple["storageKeys"].append(to_hex(k));
473 }
474 res.append(json_tuple);
475 }
476 return res;
477 }
478}
Serializer implementation for buffer.
Buffer to store and retrieve byte data.
Definition buffer.hpp:15
static auto from_hex(const std::string &hex) -> std::optional< cbdc::buffer >
Creates a new buffer from the provided hex string.
Definition buffer.cpp:85
parsec::ticket_machine::ticket_number_type ticket_number_type
Type alias for a ticket number.
This class contains a value that can be serialized into, or was deserialized from,...
Definition rlp.hpp:32
auto tx_to_json(cbdc::parsec::agent::runner::evm_tx &tx, const std::shared_ptr< secp256k1_context > &ctx) -> Json::Value
Encodes the given transaction into a eth-RPC compatible representation in JSON - as Json::Value.
auto raw_tx_from_json(const Json::Value &param) -> std::optional< std::shared_ptr< cbdc::parsec::agent::runner::evm_tx > >
Converts a given Json::Value to an evm_tx.
auto to_hex_trimmed(const evmc::bytes32 &b, const std::string &prefix) -> std::string
auto address_from_json(const Json::Value &addr) -> std::optional< evmc::address >
Converts a given Json::Value to an evmc::address.
auto tx_encode(const cbdc::parsec::agent::runner::evm_tx &tx, uint64_t chain_id, bool for_sighash) -> cbdc::buffer
Converts the given transaction to an RLP encoded buffer conforming to Ethereums conventions.
auto tx_log_to_json(cbdc::parsec::agent::runner::evm_log &log, interface::ticket_number_type tn, cbdc::hash_t txid) -> Json::Value
Encodes the given transaction log into a eth-RPC compatible representation in JSON - as Json::Value.
auto check_tx_decode(const cbdc::buffer &buf, const std::shared_ptr< logging::log > &logger, const std::shared_ptr< cbdc::parsec::agent::runner::evm_tx > &tx) -> std::optional< rlp_value >
auto tx_receipt_to_json(cbdc::parsec::agent::runner::evm_tx_receipt &rcpt, const std::shared_ptr< secp256k1_context > &ctx) -> Json::Value
Encodes the given transaction receipt into a eth-RPC compatible representation in JSON - as Json::Val...
auto to_hex(const evmc::address &addr) -> std::string
auto check_signature(const cbdc::parsec::agent::runner::evm_tx &tx, const std::shared_ptr< secp256k1_context > &ctx, uint64_t chain_id) -> std::optional< evmc::address >
Checks the signature of an EVM transaction.
auto buffer_from_json(const Json::Value &val) -> std::optional< cbdc::buffer >
Converts a given Json::Value to a cbdc::buffer.
auto dryrun_tx_from_json(const Json::Value &json, uint64_t chain_id) -> std::optional< std::shared_ptr< cbdc::parsec::agent::runner::evm_dryrun_tx > >
Converts a given Json::Value to an evm_dryrun_tx.
auto tx_from_json(const Json::Value &json, uint64_t) -> std::optional< std::shared_ptr< cbdc::parsec::agent::runner::evm_tx > >
Converts a given Json::Value to an evm_tx.
auto is_valid_rlp_tx(evm_tx_type type, const rlp_value &rlp_tx) -> bool
auto uint256be_from_json(const Json::Value &val) -> std::optional< evmc::uint256be >
Converts a given Json::Value to an evmc::uint256be.
auto access_list_to_json(cbdc::parsec::agent::runner::evm_access_list &al) -> Json::Value
Encodes the given access list into a eth-RPC compatible representation in JSON - as Json::Value.
auto tx_decode(const cbdc::buffer &buf, const std::shared_ptr< logging::log > &logger, uint64_t chain_id) -> std::optional< std::shared_ptr< cbdc::parsec::agent::runner::evm_tx > >
Converts a given buffer to an evm_tx.
std::vector< evm_access_tuple > evm_access_list
Type alias for a list of storage key accesses.
auto uint256be_or_default(const Json::Value &val, evmc::uint256be def) -> evmc::uint256be
Converts a given Json::Value to an evmc::uint256be, returning a default value if none could be decode...
void add_to_bloom(cbdc::buffer &bloom, const cbdc::buffer &entry)
Adds an entry to a bloom value.
auto to_uint64(const evmc::uint256be &v) -> uint64_t
Converts an uint256be to a uint64_t, ignoring higher order bits.
auto tx_id(const cbdc::parsec::agent::runner::evm_tx &tx, uint64_t chain_id) -> cbdc::hash_t
Calculate ethereum-compatible txid.
auto rlp_decode_access_list(const rlp_value &rlp) -> std::optional< parsec::agent::runner::evm_access_list >
Decodes an access list from and rlp_value of type rlp_value_type::array.
auto rlp_encode_access_list(const parsec::agent::runner::evm_access_list &access_list) -> rlp_value
RLP encodes an access list.
auto from_buffer(nuraft::buffer &buf) -> std::optional< T >
Deserialize object of given type from a nuraft::buffer.
std::array< unsigned char, cbdc::hash_size > hash_t
SHA256 hash container.
auto make_rlp_value(const T &obj, bool trim_leading_zeroes=false) -> rlp_value
Turns an existing value into an rlp_value by first serializing it as a cbdc::buffer,...
Definition rlp.hpp:126
@ buffer
A singular RLP value (byte array)
@ array
A collection of RLP values.
auto keccak_data(const void *data, size_t len) -> hash_t
Calculates the Keccak256 hash of the specified data.
auto to_string(const hash_t &val) -> std::string
Converts a hash to a hexadecimal string.
auto make_buffer(const T &obj) -> std::enable_if_t< std::is_same_v< B, nuraft::ptr< nuraft::buffer > >, nuraft::ptr< nuraft::buffer > >
Serialize object into nuraft::buffer using a cbdc::nuraft_serializer.