Program Listing for File message.cuh#
↰ Return to documentation for file (src/message.cuh
)
// cuEVM: CUDA Ethereum Virtual Machine implementation
// Copyright 2023 Stefan-Dan Ciocirlan (SBIP - Singapore Blockchain Innovation Programme)
// Author: Stefan-Dan Ciocirlan
// Data: 2023-11-30
// SPDX-License-Identifier: MIT
#ifndef _MESSAGE_H_
#define _MESSAGE_H_
#include "utils.h"
#include "state.cuh"
#include "keccak.cuh"
template <class params>
class message_t
{
public:
typedef arith_env_t<params> arith_t;
typedef typename arith_t::bn_t bn_t;
typedef cgbn_mem_t<params::BITS> evm_word_t;
typedef keccak::keccak_t keccak_t;
static const uint32_t HASH_BYTES = 32;
typedef struct
{
evm_word_t sender;
evm_word_t recipient;
evm_word_t contract_address;
evm_word_t gas_limit;
evm_word_t value;
uint32_t depth;
uint8_t call_type;
evm_word_t storage_address;
data_content_t data;
data_content_t byte_code;
evm_word_t return_data_offset;
evm_word_t return_data_size;
uint32_t static_env;
} message_data_t;
message_data_t *_content;
arith_t _arith;
__host__ __device__ __forceinline__ message_t(
arith_t &arith,
bn_t &sender,
bn_t &recipient,
bn_t &contract_address,
bn_t &gas_limit,
bn_t &value,
uint32_t depth,
uint8_t call_type,
bn_t &storage_address,
uint8_t *data,
size_t data_size,
uint8_t *byte_code,
size_t byte_code_size,
bn_t &return_data_offset,
bn_t &return_data_size,
uint32_t static_env = 0
) : _arith(arith)
{
SHARED_MEMORY message_data_t *content;
ONE_THREAD_PER_INSTANCE(
content = new message_data_t;)
_content = content;
cgbn_store(_arith._env, &(_content->sender), sender);
cgbn_store(_arith._env, &(_content->recipient), recipient);
cgbn_store(_arith._env, &(_content->contract_address), contract_address);
cgbn_store(_arith._env, &(_content->gas_limit), gas_limit);
cgbn_store(_arith._env, &(_content->value), value);
_content->depth = depth;
_content->call_type = call_type;
cgbn_store(_arith._env, &(_content->storage_address), storage_address);
_content->data.size = data_size;
_content->byte_code.size = byte_code_size;
ONE_THREAD_PER_INSTANCE(
if (data_size > 0) {
_content->data.data = new uint8_t[data_size];
memcpy(_content->data.data, data, sizeof(uint8_t) * data_size);
} else {
_content->data.data = NULL;
}
if (byte_code_size > 0) {
_content->byte_code.data = new uint8_t[byte_code_size];
memcpy(_content->byte_code.data, byte_code, sizeof(uint8_t) * byte_code_size);
} else {
_content->byte_code.data = NULL;
})
cgbn_store(_arith._env, &(_content->return_data_offset), return_data_offset);
cgbn_store(_arith._env, &(_content->return_data_size), return_data_size);
_content->static_env = static_env;
}
__host__ __device__ __forceinline__ ~message_t()
{
ONE_THREAD_PER_INSTANCE(
if (_content->data.size > 0) {
delete[] _content->data.data;
_content->data.size = 0;
_content->data.data = NULL;
}
if (_content->byte_code.size > 0) {
delete[] _content->byte_code.data;
_content->byte_code.size = 0;
_content->byte_code.data = NULL;
}
delete _content;)
_content = NULL;
}
__host__ __device__ __forceinline__ void get_sender(
bn_t &sender)
{
cgbn_load(_arith._env, sender, &(_content->sender));
}
__host__ __device__ __forceinline__ void get_recipient(
bn_t &recipient)
{
cgbn_load(_arith._env, recipient, &(_content->recipient));
}
__host__ __device__ __forceinline__ void get_contract_address(
bn_t &contract_address)
{
cgbn_load(_arith._env, contract_address, &(_content->contract_address));
}
__host__ __device__ __forceinline__ void get_gas_limit(
bn_t &gas_limit)
{
cgbn_load(_arith._env, gas_limit, &(_content->gas_limit));
}
__host__ __device__ __forceinline__ void get_value(
bn_t &value)
{
cgbn_load(_arith._env, value, &(_content->value));
}
__host__ __device__ __forceinline__ uint32_t get_depth()
{
return _content->depth;
}
__host__ __device__ __forceinline__ uint8_t get_call_type()
{
return _content->call_type;
}
__host__ __device__ __forceinline__ void get_storage_address(
bn_t &storage_address)
{
cgbn_load(_arith._env, storage_address, &(_content->storage_address));
}
__host__ __device__ __forceinline__ size_t get_data_size()
{
return _content->data.size;
}
__host__ __device__ __forceinline__ uint8_t *get_data(
bn_t &index,
bn_t &length,
size_t &available_size)
{
available_size = 0;
size_t index_s;
int32_t overflow = _arith.size_t_from_cgbn(index_s, index);
if (
(overflow != 0) ||
(index_s >= _content->data.size))
{
return NULL;
}
else
{
size_t length_s;
overflow = _arith.size_t_from_cgbn(length_s, length);
if (
(overflow != 0) ||
(length_s > _content->data.size - index_s))
{
available_size = _content->data.size - index_s;
return _content->data.data + index_s;
}
else
{
available_size = length_s;
return _content->data.data + index_s;
}
}
}
__host__ __device__ __forceinline__ size_t get_code_size()
{
return _content->byte_code.size;
}
__host__ __device__ __forceinline__ uint8_t *get_byte_code()
{
return _content->byte_code.data;
}
__host__ __device__ __forceinline__ uint8_t *get_byte_code_data(
bn_t &index,
bn_t &length,
size_t &available_size
)
{
return _arith.get_data(
_content->byte_code,
index,
length,
available_size);
}
__host__ __device__ __forceinline__ void get_return_data_offset(
bn_t &return_data_offset)
{
cgbn_load(_arith._env, return_data_offset, &(_content->return_data_offset));
}
__host__ __device__ __forceinline__ void get_return_data_size(
bn_t &return_data_size)
{
cgbn_load(_arith._env, return_data_size, &(_content->return_data_size));
}
__host__ __device__ __forceinline__ uint32_t get_static_env()
{
return _content->static_env;
}
__host__ __device__ __forceinline__ void set_gas_limit(
bn_t &gas_limit)
{
cgbn_store(_arith._env, &(_content->gas_limit), gas_limit);
}
__host__ __device__ __forceinline__ void set_data(
uint8_t *data,
size_t data_size)
{
ONE_THREAD_PER_INSTANCE(
if (_content->data.size > 0) {
delete[] _content->data.data;
_content->data.size = 0;
_content->data.data = NULL;
}
if (data_size > 0) {
_content->data.data = new uint8_t[data_size];
memcpy(_content->data.data, data, sizeof(uint8_t) * data_size);
} else {
_content->data.data = NULL;
}
_content->data.size = data_size;)
}
__host__ __device__ __forceinline__ void set_byte_code(
uint8_t *byte_code,
size_t byte_code_size)
{
ONE_THREAD_PER_INSTANCE(
if (_content->byte_code.size > 0) {
delete[] _content->byte_code.data;
_content->byte_code.size = 0;
_content->byte_code.data = NULL;
}
if (byte_code_size > 0) {
_content->byte_code.data = new uint8_t[byte_code_size];
memcpy(_content->byte_code.data, byte_code, sizeof(uint8_t) * byte_code_size);
} else {
_content->byte_code.data = NULL;
})
_content->byte_code.size = byte_code_size;
}
__host__ __device__ __forceinline__ void set_return_data_offset(
bn_t &return_data_offset)
{
cgbn_store(_arith._env, &(_content->return_data_offset), return_data_offset);
}
__host__ __device__ __forceinline__ void set_return_data_size(
bn_t &return_data_size)
{
cgbn_store(_arith._env, &(_content->return_data_size), return_data_size);
}
__host__ __device__ __forceinline__ static void get_create_contract_address(
arith_t &arith,
bn_t &contract_address,
bn_t &sender_address,
bn_t &sender_nonce,
keccak_t &keccak
)
{
SHARED_MEMORY uint8_t sender_address_bytes[arith_t::BYTES];
arith.memory_from_cgbn(
&(sender_address_bytes[0]),
sender_address);
SHARED_MEMORY uint8_t sender_nonce_bytes[arith_t::BYTES];
arith.memory_from_cgbn(
&(sender_nonce_bytes[0]),
sender_nonce);
uint8_t nonce_bytes;
for (nonce_bytes = arith_t::BYTES; nonce_bytes > 0; nonce_bytes--)
{
if (sender_nonce_bytes[arith_t::BYTES - nonce_bytes] != 0)
{
break;
}
}
// TODO: this might work only for arith_t::BYTES == 32
SHARED_MEMORY uint8_t rlp_list[1 + 1 + arith_t::ADDRESS_BYTES + 1 + arith_t::BYTES];
// the adress has only 20 bytes
rlp_list[1] = 0x80 + arith_t::ADDRESS_BYTES;
for (uint8_t idx = 0; idx < arith_t::ADDRESS_BYTES; idx++)
{
rlp_list[2 + idx] = sender_address_bytes[arith_t::BYTES - arith_t::ADDRESS_BYTES + idx];
}
uint8_t rlp_list_length;
// 21 is from the address the 20 bytes is the length of the address
// and the 1 byte is the 0x80 + length of the address (20)
if (cgbn_compare_ui32(arith._env, sender_nonce, 128) < 0)
{
rlp_list_length = 1 + arith_t::ADDRESS_BYTES + 1;
if (cgbn_compare_ui32(arith._env, sender_nonce, 0) == 0)
{
rlp_list[2 + arith_t::ADDRESS_BYTES] = 0x80; // special case for nonce 0
}
else
{
rlp_list[2 + arith_t::ADDRESS_BYTES] = sender_nonce_bytes[arith_t::BYTES - 1];
}
}
else
{
// 1 byte for the length of the nonce
// 0x80 + length of the nonce
rlp_list_length = 21 + 1 + nonce_bytes;
rlp_list[2 + arith_t::ADDRESS_BYTES] = 0x80 + nonce_bytes;
for (uint8_t idx = 0; idx < nonce_bytes; idx++)
{
rlp_list[2 + arith_t::ADDRESS_BYTES + 1 + idx] = sender_nonce_bytes[arith_t::BYTES - nonce_bytes + idx];
}
}
rlp_list[0] = 0xc0 + rlp_list_length;
/*
ONE_THREAD_PER_INSTANCE(
print_bytes(&(rlp_list[0]), rlp_list_length + 1);
)
*/
SHARED_MEMORY uint8_t address_bytes[HASH_BYTES];
keccak.sha3(
&(rlp_list[0]),
rlp_list_length + 1,
&(address_bytes[0]),
HASH_BYTES);
for (uint8_t idx = 0; idx < arith_t::BYTES - arith_t::ADDRESS_BYTES; idx++)
{
address_bytes[idx] = 0;
}
arith.cgbn_from_memory(
contract_address,
&(address_bytes[0]));
}
__host__ __device__ __forceinline__ static void get_create2_contract_address(
arith_t &arith,
bn_t &contract_address,
bn_t &sender_address,
bn_t &salt,
data_content_t &byte_code,
keccak_t &keccak
)
{
SHARED_MEMORY uint8_t sender_address_bytes[arith_t::BYTES];
arith.memory_from_cgbn(
&(sender_address_bytes[0]),
sender_address);
SHARED_MEMORY uint8_t salt_bytes[arith_t::BYTES];
arith.memory_from_cgbn(
&(salt_bytes[0]),
salt);
size_t total_bytes = 1 + arith_t::ADDRESS_BYTES + arith_t::BYTES + HASH_BYTES;
SHARED_MEMORY uint8_t hash_code[HASH_BYTES];
keccak.sha3(
byte_code.data,
byte_code.size,
&(hash_code[0]),
HASH_BYTES);
SHARED_MEMORY uint8_t input_data[1 + arith_t::ADDRESS_BYTES + arith_t::BYTES + HASH_BYTES];
input_data[0] = 0xff;
ONE_THREAD_PER_INSTANCE(
memcpy(
&(input_data[1]),
&(sender_address_bytes[arith_t::BYTES - arith_t::ADDRESS_BYTES]),
arith_t::ADDRESS_BYTES);
memcpy(
&(input_data[1 + arith_t::ADDRESS_BYTES]),
&(salt_bytes[0]),
arith_t::BYTES);
memcpy(
&(input_data[1 + arith_t::ADDRESS_BYTES + arith_t::BYTES]),
&(hash_code[0]),
HASH_BYTES);
)
SHARED_MEMORY uint8_t address_bytes[HASH_BYTES];
keccak.sha3(
input_data,
total_bytes,
&(address_bytes[0]),
HASH_BYTES);
for (uint8_t idx = 0; idx < arith_t::BYTES - arith_t::ADDRESS_BYTES; idx++)
{
address_bytes[idx] = 0;
}
arith.cgbn_from_memory(
contract_address,
&(address_bytes[0]));
}
__host__ __device__ __forceinline__ void print()
{
printf("SENDER: ");
_arith.print_cgbn_memory(_content->sender);
printf("RECIPIENT: ");
_arith.print_cgbn_memory(_content->recipient);
printf("CONTRACT_ADDRESS: ");
_arith.print_cgbn_memory(_content->contract_address);
printf("GAS_LIMIT: ");
_arith.print_cgbn_memory(_content->gas_limit);
printf("VALUE: ");
_arith.print_cgbn_memory(_content->value);
printf("DEPTH: %d\n", _content->depth);
printf("CALL_TYPE: %d\n", _content->call_type);
printf("STORAGE_ADDRESS: ");
_arith.print_cgbn_memory(_content->storage_address);
printf("DATA_SIZE: %lu\n", _content->data.size);
if (_content->data.size > 0)
{
printf("DATA: ");
print_bytes(_content->data.data, _content->data.size);
printf("\n");
}
printf("BYTE_CODE_SIZE: %lu\n", _content->byte_code.size);
if (_content->byte_code.size > 0)
{
printf("BYTE_CODE: ");
print_bytes(_content->byte_code.data, _content->byte_code.size);
printf("\n");
}
printf("RETURN_DATA_OFFSET: ");
_arith.print_cgbn_memory(_content->return_data_offset);
printf("RETURN_DATA_SIZE: ");
_arith.print_cgbn_memory(_content->return_data_size);
printf("STATIC_ENV: %d\n", _content->static_env);
}
};
template <class params>
class transaction_t
{
public:
typedef arith_env_t<params> arith_t;
typedef typename arith_t::bn_t bn_t;
typedef cgbn_mem_t<params::BITS> evm_word_t;
typedef message_t<params> message_t;
typedef world_state_t<params> world_state_t;
typedef accessed_state_t<params> accessed_state_t;
typedef touch_state_t<params> touch_state_t;
typedef world_state_t::account_t account_t;
typedef keccak::keccak_t keccak_t;
static const size_t MAX_TRANSACTION_COUNT = 2000;
typedef struct
{
evm_word_t address;
uint32_t no_storage_keys;
evm_word_t *storage_keys;
} access_list_account_t;
typedef struct
{
uint32_t no_accounts;
access_list_account_t *accounts;
} access_list_t;
typedef struct alignas(32)
{
uint8_t type;
evm_word_t nonce;
evm_word_t gas_limit;
evm_word_t to;
evm_word_t value;
evm_word_t sender;
access_list_t access_list;
evm_word_t max_fee_per_gas;
evm_word_t max_priority_fee_per_gas;
evm_word_t gas_price;
data_content_t data_init;
} transaction_data_t;
transaction_data_t *_content;
arith_t _arith;
__host__ __device__ __forceinline__ transaction_t(
arith_t arith,
transaction_data_t *content) : _arith(arith),
_content(content)
{
}
__host__ __device__ __forceinline__ ~transaction_t()
{
_content = NULL;
}
__host__ __device__ __forceinline__ void get_nonce(
bn_t &nonce)
{
cgbn_load(_arith._env, nonce, &(_content->nonce));
}
__host__ __device__ __forceinline__ void get_gas_limit(
bn_t &gas_limit)
{
cgbn_load(_arith._env, gas_limit, &(_content->gas_limit));
}
__host__ __device__ __forceinline__ void get_to(
bn_t &to)
{
cgbn_load(_arith._env, to, &(_content->to));
}
__host__ __device__ __forceinline__ void get_value(
bn_t &value)
{
cgbn_load(_arith._env, value, &(_content->value));
}
__host__ __device__ __forceinline__ void get_sender(
bn_t &sender)
{
cgbn_load(_arith._env, sender, &(_content->sender));
}
__host__ __device__ __forceinline__ void get_max_fee_per_gas(
bn_t &max_fee_per_gas)
{
cgbn_load(_arith._env, max_fee_per_gas, &(_content->max_fee_per_gas));
}
__host__ __device__ __forceinline__ void get_max_priority_fee_per_gas(
bn_t &max_priority_fee_per_gas)
{
cgbn_load(_arith._env, max_priority_fee_per_gas, &(_content->max_priority_fee_per_gas));
}
__host__ __device__ __forceinline__ void get_gas_price(
bn_t &gas_price)
{
cgbn_load(_arith._env, gas_price, &(_content->gas_price));
}
__host__ __device__ __forceinline__ void get_intrisinc_gas(
bn_t &intrisinc_gas,
accessed_state_t &accessed_state)
{
// set the initial cost to the transaction cost
cgbn_set_ui32(_arith._env, intrisinc_gas, GAS_TRANSACTION);
// see if a contract is being created
bn_t to;
get_to(to);
if (cgbn_compare_ui32(_arith._env, to, 0) == 0)
{
// contract creation
cgbn_add_ui32(_arith._env, intrisinc_gas, intrisinc_gas, GAS_TX_CREATE);
}
// go through call data cost
for (size_t idx=0; idx < _content->data_init.size; idx++)
{
if (_content->data_init.data[idx] == 0)
{
cgbn_add_ui32(_arith._env, intrisinc_gas, intrisinc_gas, GAS_TX_DATA_ZERO);
}
else
{
cgbn_add_ui32(_arith._env, intrisinc_gas, intrisinc_gas, GAS_TX_DATA_NONZERO);
}
}
// if has the access list add the cost and
// add the accounts and storage keys to the warm accessed accounts and storage keys
bn_t address;
bn_t key;
bn_t value;
if (_content->type > 0)
{
for (size_t idx=0; idx < _content->access_list.no_accounts; idx++)
{
cgbn_add_ui32(
_arith._env,
intrisinc_gas,
intrisinc_gas,
GAS_ACCESS_LIST_ADDRESS);
cgbn_load(
_arith._env,
address,
&(_content->access_list.accounts[idx].address));
// get the account in warm accessed accounts
accessed_state.get_account(address, READ_NONE);
for (
size_t jdx=0;
jdx < _content->access_list.accounts[idx].no_storage_keys;
jdx++)
{
cgbn_add_ui32(
_arith._env,
intrisinc_gas,
intrisinc_gas,
GAS_ACCESS_LIST_STORAGE);
cgbn_load(
_arith._env,
key,
&(_content->access_list.accounts[idx].storage_keys[jdx]));
// get the storage in warm accessed storage keys
accessed_state.get_value(address, key, value);
}
}
}
// if create transaction add the cost
// EIP-3869
if (cgbn_compare_ui32(_arith._env, to, 0) == 0)
{
// compute the dynamic gas cost for initcode
bn_t initcode_gas;
bn_t initcode_length;
_arith.cgbn_from_size_t(initcode_length, _content->data_init.size);
// evm word INITCODE_WORD_COST * ceil(len(initcode) / 32)
cgbn_add_ui32(_arith._env, initcode_gas, initcode_length, 31);
cgbn_div_ui32(_arith._env, initcode_gas, initcode_gas, 32);
cgbn_mul_ui32(_arith._env, initcode_gas, initcode_gas, GAS_INITCODE_WORD_COST);
cgbn_add(_arith._env, intrisinc_gas, intrisinc_gas, initcode_gas);
}
}
__host__ __device__ __forceinline__ uint32_t get_transaction_fees(
bn_t &gas_value,
bn_t &gas_limit,
bn_t &gas_price,
bn_t &gas_priority_fee,
bn_t &up_front_cost,
bn_t &m,
bn_t &block_base_fee
)
{
bn_t max_priority_fee_per_gas; // YP: \f$T_{f}\f$
bn_t max_fee_per_gas; // YP: \f$T_{m}\f$
bn_t value; // YP: \f$T_{v}\f$
get_max_priority_fee_per_gas(max_priority_fee_per_gas);
get_max_fee_per_gas(max_fee_per_gas);
get_value(value);
get_gas_limit(gas_limit); // YP: \f$T_{g}\f$
if (
(_content->type == 0) ||
(_content->type == 1)
)
{
// \f$p = T_{p}\f$
get_gas_price(gas_price);
// \f$f = T_{p} - H_{f}\f$
cgbn_sub(_arith._env, gas_priority_fee, gas_price, block_base_fee);
// \f$v_{0} = T_{p} * T_{g} + T_{v}\f$
cgbn_mul(_arith._env, up_front_cost, gas_price, gas_limit);
cgbn_add(_arith._env, up_front_cost, up_front_cost, value);
// \f$m = T_{p}\f$
cgbn_set(_arith._env, m, gas_price);
} else if (_content->type == 2)
{
// \f$T_{m} - H_{f}\f$
cgbn_sub(_arith._env, gas_priority_fee, max_fee_per_gas, block_base_fee);
// \f$f=min(T_{f}, T_{m} - H_{f})\f$
if (cgbn_compare(_arith._env, gas_priority_fee, max_priority_fee_per_gas) > 0)
{
cgbn_set(_arith._env, gas_priority_fee, max_priority_fee_per_gas);
}
// \f$p = f + H_{f}\f$
cgbn_add(_arith._env, gas_price, gas_priority_fee, block_base_fee);
// \f$v_{0} = T_{m} * T_{g} + T_{v}\f$
cgbn_mul(_arith._env, up_front_cost, max_fee_per_gas, gas_limit);
cgbn_add(_arith._env, up_front_cost, up_front_cost, value);
// \f$m = T_{m}\f$
cgbn_set(_arith._env, m, max_fee_per_gas);
} else {
return ERROR_TRANSACTION_TYPE;
}
// gas value \f$= T_{g} \dot p\f$
cgbn_mul(_arith._env, gas_value, gas_limit, gas_price);
return ERR_NONE;
}
__host__ __device__ __forceinline__ void get_computed_gas_price(
bn_t &gas_price,
bn_t &block_base_fee,
uint32_t &error_code
)
{
bn_t max_priority_fee_per_gas; // YP: \f$T_{f}\f$
bn_t max_fee_per_gas; // YP: \f$T_{m}\f$
bn_t gas_priority_fee; // YP: \f$f\f$
get_max_priority_fee_per_gas(max_priority_fee_per_gas);
get_max_fee_per_gas(max_fee_per_gas);
if (
(_content->type == 0) ||
(_content->type == 1)
)
{
// \f$p = T_{p}\f$
get_gas_price(gas_price);
} else if (_content->type == 2)
{
// \f$T_{m} - H_{f}\f$
cgbn_sub(_arith._env, gas_priority_fee, max_fee_per_gas, block_base_fee);
// \f$f=min(T_{f}, T_{m} - H_{f})\f$
if (cgbn_compare(_arith._env, gas_priority_fee, max_priority_fee_per_gas) > 0)
{
cgbn_set(_arith._env, gas_priority_fee, max_priority_fee_per_gas);
}
// \f$p = f + H_{f}\f$
cgbn_add(_arith._env, gas_price, gas_priority_fee, block_base_fee);
} else {
error_code = ERROR_TRANSACTION_TYPE;
}
}
__host__ __device__ void validate_transaction(
touch_state_t &touch_state,
bn_t &gas_used,
bn_t &gas_price,
bn_t &gas_priority_fee,
uint32_t &error_code,
bn_t &block_base_fee,
bn_t &block_gas_limit
)
{
bn_t intrisinc_gas;
// get the intrisinc gas and update the accessed state
// with the access list if present
get_intrisinc_gas(intrisinc_gas, *touch_state._accessed_state);
bn_t up_front_cost; // YP: \f$v_{0}\f$
bn_t m; // YP: \f$m\f$
bn_t gas_value; // YP: \f$p \dot T_{g}\f$
bn_t gas_limit; // YP: \f$T_{g}\f$
error_code = get_transaction_fees(
gas_value,
gas_limit,
gas_price,
gas_priority_fee,
up_front_cost,
m,
block_base_fee
);
bn_t sender_address;
account_t *sender_account;
get_sender(sender_address);
error_code = ERR_NONE;
// get the world state account
sender_account = touch_state._accessed_state->_world_state->get_account(
sender_address,
error_code
);
bn_t sender_balance;
cgbn_load(_arith._env, sender_balance, &(sender_account->balance));
bn_t sender_nonce;
cgbn_load(_arith._env, sender_nonce, &(sender_account->nonce));
bn_t transaction_nonce;
get_nonce(transaction_nonce);
bn_t max_fee_per_gas;
get_max_fee_per_gas(max_fee_per_gas);
bn_t max_priority_fee_per_gas;
get_max_priority_fee_per_gas(max_priority_fee_per_gas);
// sender is an empty account YP: \f$\sigma(T_{s}) \neq \varnothing\f$
error_code = (error_code != ERR_NONE) ?
error_code :
((error_code == ERR_STATE_INVALID_ADDRESS) ?
ERROR_TRANSACTION_SENDER_EMPTY : ERR_NONE);
// sender is a contract YP: \f$\sigma(T_{s})_{c} \eq KEC(())\f$
error_code = (error_code != ERR_NONE) ?
error_code :
( (sender_account->code_size > 0) ?
ERROR_TRANSACTION_SENDER_CODE : ERR_NONE);
// nonce are different YP: \f$T_{n} \eq \sigma(T_{s})_{n}\f$
error_code = (error_code != ERR_NONE) ?
error_code :
((cgbn_compare(_arith._env, transaction_nonce, sender_nonce) != 0) ?
ERROR_TRANSACTION_NONCE : ERR_NONE);
// sent gas is less than intrinisec gas YP: \f$T_{g} \geq g_{0}\f$
error_code = (error_code != ERR_NONE) ?
error_code :
((cgbn_compare(_arith._env, gas_limit, intrisinc_gas) < 0) ?
ERROR_TRANSACTION_GAS : ERR_NONE);
// balance is less than up front cost YP: \f$\sigma(T_{s})_{b} \geq v_{0}\f$
error_code = (error_code != ERR_NONE) ?
error_code :
((cgbn_compare(_arith._env, sender_balance, up_front_cost) < 0) ?
ERROR_TRANSACTION_SENDER_BALANCE : ERR_NONE);
// gas fee is less than than block base fee YP: \f$m \geq H_{f}\f$
error_code = (error_code != ERR_NONE) ?
error_code :
((cgbn_compare(_arith._env, m, block_base_fee) < 0) ?
ERROR_TRANSACTION_GAS_PRICE : ERR_NONE);
// Max priority fee per gas is higher than max fee per gas YP: \f$T_{m} \geq T_{f}\f$
error_code = (error_code != ERR_NONE) ?
error_code :
(((_content->type == 2) &&
(cgbn_compare(_arith._env, max_fee_per_gas, max_priority_fee_per_gas) < 0)) ?
ERROR_TRANSACTION_GAS_PRIORITY : ERR_NONE);
// the other verification is about the block gas limit
// YP: \f$T_{g} \leq H_{l}\f$ ... different because it takes in account
// previous transactions
error_code = (error_code != ERR_NONE) ?
error_code :
((cgbn_compare(_arith._env, gas_limit, block_gas_limit) > 0) ?
ERROR_TRANSACTION_BLOCK_GAS_LIMIT : ERR_NONE);
// if transaction is valid update the touch state
if (error_code == ERR_NONE)
{
// \f$\simga(T_{s})_{b} = \simga(T_{s})_{b} - (p \dot T_{g})\f$
cgbn_sub(_arith._env, sender_balance, sender_balance, gas_value);
touch_state.set_account_balance(sender_address, sender_balance);
// \f$\simga(T_{s})_{n} = \simga(T_{s})_{n} + 1\f$
cgbn_add_ui32(_arith._env, sender_nonce, sender_nonce, 1);
touch_state.set_account_nonce(sender_address, sender_nonce);
// set the gas used to the intrisinc gas
cgbn_set(_arith._env, gas_used, intrisinc_gas);
}
}
__host__ __device__ message_t *get_message_call(
accessed_state_t &accessed_state,
keccak_t &keccak
)
{
bn_t sender, to, gas_limit, value;
get_sender(sender);
get_to(to);
get_gas_limit(gas_limit);
get_value(value);
uint32_t depth = 0;
uint8_t call_type = OP_CALL;
uint8_t *byte_code = NULL;
size_t byte_code_size = 0;
if (cgbn_compare_ui32(_arith._env, to, 0) == 0)
{
// is CREATE type not CREATE2 because no salt
call_type = OP_CREATE;
byte_code = _content->data_init.data;
byte_code_size = _content->data_init.size;
// TODO: code size does not execede the maximum allowed
account_t *account = accessed_state.get_account(sender, READ_NONCE);
bn_t sender_nonce;
// nonce is -1 in YP but here is before validating the transaction
// and increasing the nonce
cgbn_load(_arith._env, sender_nonce, &(account->nonce));
message_t::get_create_contract_address(
_arith,
to,
sender,
sender_nonce,
keccak);
}
else
{
account_t *account = accessed_state.get_account(to, READ_CODE);
byte_code = account->bytecode;
byte_code_size = account->code_size;
}
uint32_t static_env = 0;
bn_t return_data_offset;
cgbn_set_ui32(_arith._env, return_data_offset, 0);
bn_t return_data_size;
cgbn_set_ui32(_arith._env, return_data_size, 0);
return new message_t(
_arith,
sender,
to,
to,
gas_limit,
value,
depth,
call_type,
to,
_content->data_init.data,
_content->data_init.size,
byte_code,
byte_code_size,
return_data_offset,
return_data_size,
static_env);
}
__host__ __device__ static void print_transaction_data_t(
arith_t &arith,
transaction_data_t &transaction_data
)
{
printf("TYPE: %hhu\nNONCE: ", transaction_data.type);
arith.print_cgbn_memory(transaction_data.nonce);
printf("GAS_LIMIT: ");
arith.print_cgbn_memory(transaction_data.gas_limit);
printf("TO: ");
arith.print_cgbn_memory(transaction_data.to);
printf("VALUE: ");
arith.print_cgbn_memory(transaction_data.value);
printf("SENDER: ");
arith.print_cgbn_memory(transaction_data.sender);
if (transaction_data.type >= 1)
{
printf("ACCESS_LIST: ");
for (size_t idx = 0; idx < transaction_data.access_list.no_accounts; idx++)
{
printf("ADDRESS: ");
arith.print_cgbn_memory(transaction_data.access_list.accounts[idx].address);
printf("NO_STORAGE_KEYS: %d", transaction_data.access_list.accounts[idx].no_storage_keys);
for (size_t jdx = 0; jdx < transaction_data.access_list.accounts[idx].no_storage_keys; jdx++)
{
printf("STORAGE_KEY[%lu]: ", jdx);
arith.print_cgbn_memory(transaction_data.access_list.accounts[idx].storage_keys[jdx]);
}
}
}
if (transaction_data.type == 2)
{
printf("MAX_FEE_PER_GAS: ");
arith.print_cgbn_memory(transaction_data.max_fee_per_gas);
printf("MAX_PRIORITY_FEE_PER_GAS: ");
arith.print_cgbn_memory(transaction_data.max_priority_fee_per_gas);
}
else
{
printf("GAS_PRICE: ");
arith.print_cgbn_memory(transaction_data.gas_price);
}
printf("DATA_INIT: ");
print_data_content_t(transaction_data.data_init);
}
__host__ __device__ void print()
{
print_transaction_data_t(_arith, *_content);
}
__host__ cJSON *json()
{
cJSON *transaction_json = cJSON_CreateObject();
char *hex_string_ptr = new char[arith_t::BYTES * 2 + 3];
char *bytes_string = NULL;
// set the type
cJSON_AddNumberToObject(transaction_json, "type", _content->type);
// set the nonce
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->nonce);
cJSON_AddStringToObject(transaction_json, "nonce", hex_string_ptr);
// set the gas limit
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->gas_limit);
cJSON_AddStringToObject(transaction_json, "gasLimit", hex_string_ptr);
// set the to
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->to, 5);
cJSON_AddStringToObject(transaction_json, "to", hex_string_ptr);
// set the value
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->value);
cJSON_AddStringToObject(transaction_json, "value", hex_string_ptr);
// set the sender
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->sender, 5);
cJSON_AddStringToObject(transaction_json, "sender", hex_string_ptr);
// TODO: delete this from revmi comparator
cJSON_AddStringToObject(transaction_json, "origin", hex_string_ptr);
// set the access list
if (_content->type >= 1)
{
cJSON *access_list_json = cJSON_CreateArray();
cJSON_AddItemToObject(transaction_json, "accessList", access_list_json);
for (size_t idx = 0; idx < _content->access_list.no_accounts; idx++)
{
cJSON *account_json = cJSON_CreateObject();
cJSON_AddItemToArray(access_list_json, account_json);
_arith.hex_string_from_cgbn_memory(
hex_string_ptr,
_content->access_list.accounts[idx].address,
5);
cJSON_AddStringToObject(account_json, "address", hex_string_ptr);
cJSON *storage_keys_json = cJSON_CreateArray();
cJSON_AddItemToObject(account_json, "storageKeys", storage_keys_json);
for (size_t jdx = 0; jdx < _content->access_list.accounts[idx].no_storage_keys; jdx++)
{
_arith.hex_string_from_cgbn_memory(
hex_string_ptr,
_content->access_list.accounts[idx].storage_keys[jdx]);
cJSON_AddItemToArray(storage_keys_json, cJSON_CreateString(hex_string_ptr));
}
}
}
// set the gas price
if (_content->type == 2)
{
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->max_fee_per_gas);
cJSON_AddStringToObject(transaction_json, "maxFeePerGas", hex_string_ptr);
// set the max priority fee per gas
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->max_priority_fee_per_gas);
cJSON_AddStringToObject(transaction_json, "maxPriorityFeePerGas", hex_string_ptr);
}
else
{
// set the gas price
_arith.hex_string_from_cgbn_memory(hex_string_ptr, _content->gas_price);
cJSON_AddStringToObject(transaction_json, "gasPrice", hex_string_ptr);
}
// set the data init
if (_content->data_init.size > 0)
{
bytes_string = hex_from_data_content(_content->data_init);
cJSON_AddStringToObject(transaction_json, "data", bytes_string);
delete[] bytes_string;
bytes_string = NULL;
}
else
{
cJSON_AddStringToObject(transaction_json, "data", "0x");
}
delete[] hex_string_ptr;
hex_string_ptr = NULL;
return transaction_json;
}
__host__ static size_t get_no_transaction(
const cJSON *test)
{
const cJSON *transaction_json = cJSON_GetObjectItemCaseSensitive(test, "transaction");
const cJSON *data_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "data");
size_t data_counts = cJSON_GetArraySize(data_json);
const cJSON *gas_limit_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "gasLimit");
size_t gas_limit_counts = cJSON_GetArraySize(gas_limit_json);
const cJSON *value_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "value");
size_t value_counts = cJSON_GetArraySize(value_json);
return data_counts * gas_limit_counts * value_counts;
}
__host__ static void get_transactions(
transaction_data_t *&transactions,
const cJSON *test,
size_t &count,
size_t start_index = 0)
{
const cJSON *transaction_json = cJSON_GetObjectItemCaseSensitive(test, "transaction");
arith_t arith(cgbn_report_monitor, 0);
//transaction_data_t *transactions = NULL;
size_t available_no_transactions = get_no_transaction(test);
if (start_index >= available_no_transactions)
{
count = 0;
transactions = NULL;
return;
}
// set the number of transactions
count = available_no_transactions - start_index;
count = (count > MAX_TRANSACTION_COUNT) ? MAX_TRANSACTION_COUNT : count;
#ifndef ONLY_CPU
CUDA_CHECK(cudaMallocManaged(
(void **)&(transactions),
count * sizeof(transaction_data_t)));
#else
transactions = new transaction_data_t[count];
#endif
transaction_data_t *template_transaction = new transaction_data_t;
memset(template_transaction, 0, sizeof(transaction_data_t));
uint8_t type;
size_t data_index, gas_limit_index, value_index;
size_t idx = 0, jdx = 0;
type = 0;
const cJSON *nonce_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "nonce");
arith.cgbn_memory_from_hex_string(template_transaction->nonce, nonce_json->valuestring);
const cJSON *gas_limit_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "gasLimit");
size_t gas_limit_counts = cJSON_GetArraySize(gas_limit_json);
const cJSON *to_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "to");
if (strlen(to_json->valuestring) == 0)
{
arith.cgbn_memory_from_size_t(template_transaction->to, 0);
}
else
{
arith.cgbn_memory_from_hex_string(template_transaction->to, to_json->valuestring);
}
const cJSON *value_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "value");
size_t value_counts = cJSON_GetArraySize(value_json);
const cJSON *sender_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "sender");
arith.cgbn_memory_from_hex_string(template_transaction->sender, sender_json->valuestring);
const cJSON *access_list_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "accessList");
if (access_list_json != NULL)
{
size_t accounts_counts = cJSON_GetArraySize(access_list_json);
template_transaction->access_list.no_accounts = accounts_counts;
#ifndef ONLY_CPU
CUDA_CHECK(cudaMallocManaged(
(void **)&(template_transaction->access_list.accounts),
accounts_counts * sizeof(access_list_account_t)));
#else
template_transaction->access_list.accounts = new access_list_account_t[accounts_counts];
#endif
for (idx = 0; idx < accounts_counts; idx++)
{
const cJSON *account_json = cJSON_GetArrayItem(access_list_json, idx);
const cJSON *address_json = cJSON_GetObjectItemCaseSensitive(account_json, "address");
arith.cgbn_memory_from_hex_string(
template_transaction->access_list.accounts[idx].address,
address_json->valuestring);
const cJSON *storage_keys_json = cJSON_GetObjectItemCaseSensitive(account_json, "storageKeys");
size_t storage_keys_counts = cJSON_GetArraySize(storage_keys_json);
template_transaction->access_list.accounts[idx].no_storage_keys = storage_keys_counts;
#ifndef ONLY_CPU
CUDA_CHECK(cudaMallocManaged(
(void **)&(template_transaction->access_list.accounts[idx].storage_keys),
storage_keys_counts * sizeof(evm_word_t)));
#else
template_transaction->access_list.accounts[idx].storage_keys = new evm_word_t[storage_keys_counts];
#endif
for (jdx = 0; jdx < storage_keys_counts; jdx++)
{
const cJSON *storage_key_json = cJSON_GetArrayItem(storage_keys_json, jdx);
arith.cgbn_memory_from_hex_string(
template_transaction->access_list.accounts[idx].storage_keys[jdx],
storage_key_json->valuestring);
}
}
}
const cJSON *max_fee_per_gas_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "maxFeePerGas");
const cJSON *max_priority_fee_per_gas_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "maxPriorityFeePerGas");
const cJSON *gas_price_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "gasPrice");
if (
(max_fee_per_gas_json != NULL) &&
(max_priority_fee_per_gas_json != NULL) &&
(gas_price_json == NULL))
{
type = 2;
arith.cgbn_memory_from_hex_string(
template_transaction->max_fee_per_gas,
max_fee_per_gas_json->valuestring);
arith.cgbn_memory_from_hex_string(
template_transaction->max_priority_fee_per_gas,
max_priority_fee_per_gas_json->valuestring);
arith.cgbn_memory_from_size_t(template_transaction->gas_price, 0);
}
else if (
(max_fee_per_gas_json == NULL) &&
(max_priority_fee_per_gas_json == NULL) &&
(gas_price_json != NULL))
{
if (access_list_json == NULL)
{
type = 0;
}
else
{
type = 1;
}
arith.cgbn_memory_from_size_t(template_transaction->max_fee_per_gas, 0);
arith.cgbn_memory_from_size_t(template_transaction->max_priority_fee_per_gas, 0);
arith.cgbn_memory_from_hex_string(
template_transaction->gas_price,
gas_price_json->valuestring);
}
else
{
printf("ERROR: invalid transaction type\n");
exit(1);
}
const cJSON *data_json = cJSON_GetObjectItemCaseSensitive(transaction_json, "data");
size_t data_counts = cJSON_GetArraySize(data_json);
template_transaction->type = type;
size_t index;
char *bytes_string = NULL;
for (idx = 0; idx < count; idx++)
{
index = start_index + idx;
data_index = index % data_counts;
gas_limit_index = (index / data_counts) % gas_limit_counts;
value_index = (index / (data_counts * gas_limit_counts)) % value_counts;
memcpy(&(transactions[idx]), template_transaction, sizeof(transaction_data_t));
arith.cgbn_memory_from_hex_string(
transactions[idx].gas_limit,
cJSON_GetArrayItem(gas_limit_json, gas_limit_index)->valuestring);
arith.cgbn_memory_from_hex_string(
transactions[idx].value,
cJSON_GetArrayItem(value_json, value_index)->valuestring);
bytes_string = cJSON_GetArrayItem(data_json, data_index)->valuestring;
transactions[idx].data_init.size = adjusted_length(&bytes_string);
if (transactions[idx].data_init.size > 0)
{
#ifndef ONLY_CPU
CUDA_CHECK(cudaMallocManaged(
(void **)&(transactions[idx].data_init.data),
transactions[idx].data_init.size * sizeof(uint8_t)));
#else
transactions[idx].data_init.data = new uint8_t[transactions[idx].data_init.size];
#endif
hex_to_bytes(
bytes_string,
transactions[idx].data_init.data,
2 * transactions[idx].data_init.size);
}
else
{
transactions[idx].data_init.data = NULL;
}
}
delete template_transaction;
template_transaction = NULL;
//return transactions;
}
__host__ static void free_instances(
transaction_data_t *transactions,
size_t count)
{
#ifndef ONLY_CPU
if (transactions[0].access_list.no_accounts > 0)
{
for (size_t jdx = 0; jdx < transactions[0].access_list.no_accounts; jdx++)
{
if (transactions[0].access_list.accounts[jdx].no_storage_keys > 0)
{
CUDA_CHECK(cudaFree(transactions[0].access_list.accounts[jdx].storage_keys));
transactions[0].access_list.accounts[jdx].no_storage_keys = 0;
transactions[0].access_list.accounts[jdx].storage_keys = NULL;
}
}
CUDA_CHECK(cudaFree(transactions[0].access_list.accounts));
}
for (size_t idx = 0; idx < count; idx++)
{
if (transactions[idx].data_init.size > 0)
{
CUDA_CHECK(cudaFree(transactions[idx].data_init.data));
transactions[idx].data_init.size = 0;
}
if (transactions[idx].access_list.no_accounts > 0)
{
transactions[idx].access_list.accounts = NULL;
transactions[idx].access_list.no_accounts = 0;
}
}
CUDA_CHECK(cudaFree(transactions));
#else
if (transactions[0].access_list.no_accounts > 0)
{
for (size_t jdx = 0; jdx < transactions[0].access_list.no_accounts; jdx++)
{
if (transactions[0].access_list.accounts[jdx].no_storage_keys > 0)
{
delete[] transactions[0].access_list.accounts[jdx].storage_keys;
transactions[0].access_list.accounts[jdx].no_storage_keys = 0;
transactions[0].access_list.accounts[jdx].storage_keys = NULL;
}
}
delete[] transactions[0].access_list.accounts;
}
for (size_t idx = 0; idx < count; idx++)
{
if (transactions[idx].data_init.size > 0)
{
delete[] transactions[idx].data_init.data;
transactions[idx].data_init.size = 0;
}
if (transactions[idx].access_list.no_accounts > 0)
{
transactions[idx].access_list.accounts = NULL;
transactions[idx].access_list.no_accounts = 0;
}
}
delete[] transactions;
#endif
}
};
#endif