Program Listing for File env_operations.cuh#
↰ Return to documentation for file (src/env_operations.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 _ENVIRONMENTAL_OP_H_
#define _ENVIRONMENTAL_OP_H_
#include "utils.h"
#include "stack.cuh"
#include "block.cuh"
#include "state.cuh"
#include "message.cuh"
// 40s: Block Information
template <class params>
class block_operations
{
public:
typedef arith_env_t<params> arith_t;
typedef typename arith_t::bn_t bn_t;
typedef stack_t<params> stack_t;
typedef block_t<params> block_t;
__host__ __device__ __forceinline__ static void operation_BLOCKHASH(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BLOCKHASH);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t number;
stack.pop(number, error_code);
bn_t hash;
// even if error of invalid number/index, the hash is set to zero
uint32_t tmp_error_code;
block.get_previous_hash(hash, number, tmp_error_code);
stack.push(hash, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_COINBASE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t coinbase;
block.get_coin_base(coinbase);
stack.push(coinbase, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_TIMESTAMP(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t timestamp;
block.get_time_stamp(timestamp);
stack.push(timestamp, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_NUMBER(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t number;
block.get_number(number);
stack.push(number, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_PREVRANDAO(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t prev_randao;
// TODO: to change depending on the evm version
// block.get_prev_randao(prev_randao);
block.get_difficulty(prev_randao);
stack.push(prev_randao, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_GASLIMIT(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t gas_limit;
block.get_gas_limit(gas_limit);
stack.push(gas_limit, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CHAINID(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t chain_id;
block.get_chain_id(chain_id);
stack.push(chain_id, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_BASEFEE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t base_fee;
block.get_base_fee(base_fee);
stack.push(base_fee, error_code);
pc = pc + 1;
}
}
};
template <class params>
class environmental_operations
{
public:
typedef arith_env_t<params> arith_t;
typedef typename arith_t::bn_t bn_t;
typedef stack_t<params> stack_t;
typedef block_t<params> block_t;
typedef touch_state_t<params> touch_state_t;
typedef memory_t<params> memory_t;
typedef transaction_t<params> transaction_t;
typedef message_t<params> message_t;
typedef keccak::keccak_t keccak_t;
static const uint32_t HASH_BYTES = 32;
__host__ __device__ __forceinline__ static void operation_SHA3(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
keccak_t &keccak,
memory_t &memory)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_KECCAK256);
// Get the offset and length from the stack
bn_t offset, length;
stack.pop(offset, error_code);
stack.pop(length, error_code);
// compute the dynamic gas cost
bn_t dynamic_gas_cost;
// word_size = (length + 31) / 32
cgbn_add_ui32(arith._env, dynamic_gas_cost, length, 31);
cgbn_div_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, 32);
// dynamic_gas_cost = word_size * GAS_KECCAK256_WORD
cgbn_mul_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, GAS_KECCAK256_WORD);
// gas_used += dynamic_gas_cost
cgbn_add(arith._env, gas_used, gas_used, dynamic_gas_cost);
// get the memory expansion gas cost
memory.grow_cost(
offset,
length,
gas_used,
error_code);
if (error_code == ERR_NONE)
{
if (arith.has_gas(gas_limit, gas_used, error_code))
{
uint8_t *data;
data = memory.get(
offset,
length,
error_code);
uint8_t hash[HASH_BYTES];
size_t input_length;
arith.size_t_from_cgbn(input_length, length);
if (error_code == ERR_NONE)
{
keccak.sha3(
data,
input_length,
&(hash[0]),
HASH_BYTES);
bn_t hash_bn;
arith.cgbn_from_memory(
hash_bn,
&(hash[0]));
stack.push(hash_bn, error_code);
pc = pc + 1;
}
}
}
}
__host__ __device__ __forceinline__ static void operation_ADDRESS(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t recipient_address;
message.get_recipient(recipient_address);
stack.push(recipient_address, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_BALANCE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
touch_state_t &touch_state)
{
// cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_ZERO);
bn_t address;
stack.pop(address, error_code);
arith.address_conversion(address);
if (error_code == ERR_NONE)
{
touch_state.charge_gas_access_account(
address,
gas_used);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t balance;
touch_state.get_account_balance(
address,
balance);
stack.push(balance, error_code);
pc = pc + 1;
}
}
}
__host__ __device__ __forceinline__ static void operation_ORIGIN(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
transaction_t &transaction)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t origin;
transaction.get_sender(origin);
stack.push(origin, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CALLER(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t caller;
message.get_sender(caller);
stack.push(caller, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CALLVALUE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t call_value;
message.get_value(call_value);
stack.push(call_value, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CALLDATALOAD(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t index;
stack.pop(index, error_code);
bn_t length;
cgbn_set_ui32(arith._env, length, arith_t::BYTES);
size_t available_data;
uint8_t *data;
data = message.get_data(
index,
length,
available_data);
stack.pushx(arith_t::BYTES, error_code, data, available_data);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CALLDATASIZE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t length;
size_t length_s;
length_s = message.get_data_size();
arith.cgbn_from_size_t(length, length_s);
stack.push(length, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CALLDATACOPY(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message,
memory_t &memory)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
bn_t memory_offset, data_offset, length;
stack.pop(memory_offset, error_code);
stack.pop(data_offset, error_code);
stack.pop(length, error_code);
// compute the dynamic gas cost
bn_t dynamic_gas_cost;
// word_size = (length + 31) / 32
cgbn_add_ui32(arith._env, dynamic_gas_cost, length, 31);
cgbn_div_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, 32);
// dynamic_gas_cost = word_size * 6
cgbn_mul_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, GAS_MEMORY);
// gas_used += dynamic_gas_cost
cgbn_add(arith._env, gas_used, gas_used, dynamic_gas_cost);
// get the memory expansion gas cost
memory.grow_cost(
memory_offset,
length,
gas_used,
error_code);
if (error_code == ERR_NONE)
{
if (arith.has_gas(gas_limit, gas_used, error_code))
{
size_t available_data;
uint8_t *data;
data = message.get_data(
data_offset,
length,
available_data);
memory.set(
data,
memory_offset,
length,
available_data,
error_code);
pc = pc + 1;
}
}
}
__host__ __device__ __forceinline__ static void operation_CODESIZE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
size_t code_size;
code_size = message.get_code_size();
bn_t code_size_bn;
arith.cgbn_from_size_t(code_size_bn, code_size);
stack.push(code_size_bn, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_CODECOPY(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
message_t &message,
memory_t &memory)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
bn_t memory_offset, code_offset, length;
stack.pop(memory_offset, error_code);
stack.pop(code_offset, error_code);
stack.pop(length, error_code);
if (error_code == ERR_NONE)
{
// compute the dynamic gas cost
bn_t dynamic_gas_cost;
// word_size = (length + 31) / 32
cgbn_add_ui32(arith._env, dynamic_gas_cost, length, 31);
cgbn_div_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, 32);
// dynamic_gas_cost = word_size * 6
cgbn_mul_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, GAS_MEMORY);
// gas_used += dynamic_gas_cost
cgbn_add(arith._env, gas_used, gas_used, dynamic_gas_cost);
// get the memory expansion gas cost
memory.grow_cost(
memory_offset,
length,
gas_used,
error_code);
if (error_code == ERR_NONE)
{
if (arith.has_gas(gas_limit, gas_used, error_code))
{
size_t available_data;
uint8_t *data;
data = message.get_byte_code_data(
code_offset,
length,
available_data);
memory.set(
data,
memory_offset,
length,
available_data,
error_code);
pc = pc + 1;
}
}
}
}
__host__ __device__ __forceinline__ static void operation_GASPRICE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
block_t &block,
transaction_t &transaction)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t block_base_fee;
block.get_base_fee(block_base_fee);
bn_t gas_price;
transaction.get_computed_gas_price(
gas_price,
block_base_fee,
error_code);
stack.push(gas_price, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_EXTCODESIZE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
touch_state_t &touch_state)
{
// cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_ZERO);
bn_t address;
stack.pop(address, error_code);
arith.address_conversion(address);
if (error_code == ERR_NONE)
{
touch_state.charge_gas_access_account(
address,
gas_used);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
size_t code_size = touch_state.get_account_code_size(
address);
bn_t code_size_bn;
arith.cgbn_from_size_t(code_size_bn, code_size);
stack.push(code_size_bn, error_code);
pc = pc + 1;
}
}
}
__host__ __device__ __forceinline__ static void operation_EXTCODECOPY(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
touch_state_t &touch_state,
memory_t &memory)
{
// cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_ZERO);
bn_t address, memory_offset, code_offset, length;
stack.pop(address, error_code);
arith.address_conversion(address);
stack.pop(memory_offset, error_code);
stack.pop(code_offset, error_code);
stack.pop(length, error_code);
if (error_code == ERR_NONE)
{
// compute the dynamic gas cost
bn_t dynamic_gas_cost;
// word_size = (length + 31) / 32
cgbn_add_ui32(arith._env, dynamic_gas_cost, length, 31);
cgbn_div_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, 32);
// dynamic_gas_cost = word_size * 6
cgbn_mul_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, GAS_MEMORY);
// gas_used += dynamic_gas_cost
cgbn_add(arith._env, gas_used, gas_used, dynamic_gas_cost);
// get the memory expansion gas cost
memory.grow_cost(
memory_offset,
length,
gas_used,
error_code);
touch_state.charge_gas_access_account(
address,
gas_used);
if (error_code == ERR_NONE)
{
if (arith.has_gas(gas_limit, gas_used, error_code))
{
size_t available_data;
uint8_t *data;
data = touch_state.get_account_code_data(
address,
code_offset,
length,
available_data);
memory.set(
data,
memory_offset,
length,
available_data,
error_code);
pc = pc + 1;
}
}
}
}
__host__ __device__ __forceinline__ static void operation_RETURNDATASIZE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
return_data_t &return_data)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_BASE);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t length;
size_t length_s;
length_s = return_data.size();
arith.cgbn_from_size_t(length, length_s);
stack.push(length, error_code);
pc = pc + 1;
}
}
__host__ __device__ __forceinline__ static void operation_RETURNDATACOPY(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
memory_t &memory,
return_data_t &return_data)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_VERY_LOW);
bn_t memory_offset, data_offset, length;
stack.pop(memory_offset, error_code);
stack.pop(data_offset, error_code);
stack.pop(length, error_code);
if (error_code == ERR_NONE)
{
// compute the dynamic gas cost
bn_t dynamic_gas_cost;
// word_size = (length + 31) / 32
cgbn_add_ui32(arith._env, dynamic_gas_cost, length, 31);
cgbn_div_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, 32);
// dynamic_gas_cost = word_size * 6
cgbn_mul_ui32(arith._env, dynamic_gas_cost, dynamic_gas_cost, GAS_MEMORY);
// gas_used += dynamic_gas_cost
cgbn_add(arith._env, gas_used, gas_used, dynamic_gas_cost);
// get the memory expansion gas cost
memory.grow_cost(
memory_offset,
length,
gas_used,
error_code);
if (error_code == ERR_NONE)
{
if (arith.has_gas(gas_limit, gas_used, error_code))
{
uint8_t *data;
size_t data_offset_s, length_s;
int32_t overflow;
overflow = arith.size_t_from_cgbn(data_offset_s, data_offset);
overflow = overflow || arith.size_t_from_cgbn(length_s, length);
if (overflow)
{
error_code = ERROR_RETURN_DATA_OVERFLOW;
}
else
{
data = return_data.get(
data_offset_s,
length_s,
error_code);
memory.set(
data,
memory_offset,
length,
length_s,
error_code);
pc = pc + 1;
}
}
}
}
}
__host__ __device__ __forceinline__ static void operation_EXTCODEHASH(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
touch_state_t &touch_state,
keccak_t &keccak)
{
// cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_ZERO);
bn_t address;
stack.pop(address, error_code);
arith.address_conversion(address);
if (error_code == ERR_NONE)
{
touch_state.charge_gas_access_account(
address,
gas_used);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t hash_bn;
// TODO: look on the difference between destroyed and empty
if (touch_state.is_empty_account(address))
{
cgbn_set_ui32(arith._env, hash_bn, 0);
}
else if (touch_state.is_delete_account(address))
{
cgbn_set_ui32(arith._env, hash_bn, 0);
}
else
{
uint8_t *bytecode;
size_t code_size;
uint8_t hash[HASH_BYTES];
// TODO: make more efficient
bytecode = touch_state.get_account_code(
address);
code_size = touch_state.get_account_code_size(
address);
keccak.sha3(
bytecode,
code_size,
&(hash[0]),
HASH_BYTES);
arith.cgbn_from_memory(
hash_bn,
&(hash[0]));
}
stack.push(hash_bn, error_code);
pc = pc + 1;
}
}
}
__host__ __device__ __forceinline__ static void operation_SELFBALANCE(
arith_t &arith,
bn_t &gas_limit,
bn_t &gas_used,
uint32_t &error_code,
uint32_t &pc,
stack_t &stack,
touch_state_t &touch_state,
message_t &message)
{
cgbn_add_ui32(arith._env, gas_used, gas_used, GAS_LOW);
bn_t address;
message.get_recipient(address);
if (arith.has_gas(gas_limit, gas_used, error_code))
{
bn_t balance;
touch_state.get_account_balance(
address,
balance);
stack.push(balance, error_code);
pc = pc + 1;
}
}
};
#endif