Program Listing for File arith.cuh#

Return to documentation for file (src/arith.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 _ARITH_H_
#define _ARITH_H_

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <cuda.h>
#include <gmp.h>
#ifndef __CGBN_H__
#define __CGBN_H__
#include <cgbn/cgbn.h>
#endif
#include "data_content.h"

template <class params>
class arith_env_t
{
public:
  typedef cgbn_context_t<params::TPI, params> context_t;
  typedef cgbn_env_t<context_t, params::BITS> env_t;
  typedef typename env_t::cgbn_t bn_t;
  typedef typename env_t::cgbn_wide_t bn_wide_t;
  typedef cgbn_mem_t<params::BITS> evm_word_t;
  static const uint32_t BITS = params::BITS;
  static const uint32_t BYTES = params::BITS / 8;
  static const uint32_t LIMBS = params::BITS / 32;
  static const uint32_t ADDRESS_BYTES = 20;
  context_t _context;
  env_t _env;
  uint32_t _instance;
  __device__ __forceinline__ arith_env_t(
    cgbn_monitor_t monitor,
    cgbn_error_report_t *report,
    uint32_t instance
  ) : _context(monitor, report, instance),
      _env(_context),
      _instance(instance)
  {
  }

  __device__ __forceinline__ arith_env_t(
    cgbn_monitor_t monitor
  ) : _context(monitor),
      _env(_context),
      _instance(0)
  {
  }

  __host__ __forceinline__ arith_env_t(
    cgbn_monitor_t monitor,
    uint32_t instance
  ) : _context(monitor),
      _env(_context),
      _instance(instance)
  {
  }

  __host__ __device__ __forceinline__ arith_env_t(
    const arith_env_t &env
  ) : _context(env._context),
      _env(_context),
      _instance(env._instance)
  {
  }

  __host__ __device__ __forceinline__ void address_conversion(
    bn_t &address
  )
  {
    int32_t address_bits = int32_t(ADDRESS_BYTES) * 8;
    cgbn_bitwise_mask_and(_env, address, address, address_bits);
  }
  __host__ __device__ __forceinline__ size_t memory_from_cgbn(
    uint8_t *dst,
    bn_t &src
  )
  {
    for (uint32_t idx = 0; idx < BYTES; idx++)
    {
      dst[idx] = cgbn_extract_bits_ui32(_env, src, BITS - (idx + 1) * 8, 8);
    }
    return BYTES;
  }

  __host__ __device__ __forceinline__ void cgbn_from_memory(
    bn_t &dst,
    uint8_t *src
  )
  {
    for (uint32_t idx = 0; idx < BYTES; idx++)
    {
      cgbn_insert_bits_ui32(_env, dst, dst, BITS - (idx + 1) * 8, 8, src[idx]);
    }
  }

  __host__ __device__ __forceinline__ void cgbn_from_size_t(
    bn_t &dst,
    size_t src
  )
  {
    cgbn_set_ui32(_env, dst, 0);
    cgbn_insert_bits_ui32(_env, dst, dst, 32, 32, (src >> 32));
    cgbn_insert_bits_ui32(_env, dst, dst, 0, 32, src);
  }

  __host__ __device__ __forceinline__ int32_t size_t_from_cgbn(
    size_t &dst,
    bn_t &src
  )
  {
    bn_t MAX_SIZE_T;
    cgbn_set_ui32(_env, MAX_SIZE_T, 1);
    cgbn_shift_left(_env, MAX_SIZE_T, MAX_SIZE_T, 64);
    dst = 0;
    dst = cgbn_extract_bits_ui32(_env, src, 0, 32);
    dst |= ((size_t)cgbn_extract_bits_ui32(_env, src, 32, 32)) << 32;
    if (cgbn_compare(_env, src, MAX_SIZE_T) >= 0)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }

  __host__ __device__ __forceinline__ int32_t uint64_t_from_cgbn(
    uint64_t &dst,
    bn_t &src
  )
  {
    bn_t MAX_uint64_t;
    cgbn_set_ui32(_env, MAX_uint64_t, 1);
    cgbn_shift_left(_env, MAX_uint64_t, MAX_uint64_t, 64);
    dst = 0;
    dst = cgbn_extract_bits_ui32(_env, src, 0, 32);
    dst |= ((uint64_t)cgbn_extract_bits_ui32(_env, src, 32, 32)) << 32;
    if (cgbn_compare(_env, src, MAX_uint64_t) >= 0)
    {
      return 1;
    }
    else
    {
      return 0;
    }
  }
  __host__ void hex_string_from_cgbn_memory(
    char *dst_hex_string,
    evm_word_t &src_cgbn_mem,
    uint32_t count = LIMBS
  )
  {
    dst_hex_string[0] = '0';
    dst_hex_string[1] = 'x';
    for (uint32_t idx = 0; idx < count; idx++)
    {
      sprintf(
        dst_hex_string + 2 + idx * 8,
        "%08x",
        src_cgbn_mem._limbs[count - 1 - idx]
      );
    }
    dst_hex_string[count * 8 + 2] = '\0';
  }

  __host__ int32_t cgbn_memory_from_hex_string(
    evm_word_t &dst_cgbn_memory,
    char *src_hex_string
  )
  {
    mpz_t value;
    size_t written;
    mpz_init(value);
    if (
      (src_hex_string[0] == '0') &&
      ((src_hex_string[1] == 'x') || (src_hex_string[1] == 'X'))
    )
    {
      mpz_set_str(value, src_hex_string + 2, 16);
    }
    else
    {
      mpz_set_str(value, src_hex_string, 16);
    }
    if (mpz_sizeinbase(value, 2) > BITS)
    {
      return 1;
    }
    mpz_export(
      dst_cgbn_memory._limbs,
      &written,
      -1,
      sizeof(uint32_t),
      0,
      0,
      value
    );
    while (written < LIMBS)
    {
      dst_cgbn_memory._limbs[written++] = 0;
    }
    mpz_clear(value);
    return 0;
  }

  __host__ __device__ __forceinline__ void cgbn_memory_from_size_t(
    evm_word_t &dst_cgbn_memory,
    size_t src
  )
  {
    bn_t src_cgbn;
    cgbn_from_size_t(src_cgbn, src);
    cgbn_store(_env, &dst_cgbn_memory, src_cgbn);
  }

  __host__ __device__ __forceinline__ void print_cgbn_memory(
    evm_word_t &src_cgbn_memory
  )
  {
    for (uint32_t idx = 0; idx < LIMBS; idx++)
      printf("%08x ", src_cgbn_memory._limbs[LIMBS - 1 - idx]);
    printf("\n");
  }

  __host__ __device__ __forceinline__ int32_t has_gas(
    bn_t &gas_limit,
    bn_t &gas_used,
    uint32_t &error_code
  )
  {
    int32_t gas_sign = cgbn_compare(_env, gas_limit, gas_used);
    error_code = (gas_sign < 0) ? ERROR_GAS_LIMIT_EXCEEDED : error_code;
    return (gas_sign >= 0) && (error_code == ERR_NONE);
  }

  __host__ __device__ __forceinline__ void max_gas_call(
    bn_t &gas_capped,
    bn_t &gas_limit,
    bn_t &gas_used
  )
  {
      // compute the remaining gas
      bn_t gas_left;
      cgbn_sub(_env, gas_left, gas_limit, gas_used);

      // gas capped = (63/64) * gas_left
      cgbn_div_ui32(_env, gas_capped, gas_left, 64);
      cgbn_sub(_env, gas_capped, gas_left, gas_capped);
  }

  __host__ __device__ __forceinline__ uint8_t *get_data(
    data_content_t &data_content,
    bn_t &index,
    bn_t &length,
    size_t &available_size
  )
  {
    available_size = 0;
    size_t index_s;
    int32_t overflow = size_t_from_cgbn(index_s, index);
    if (
        (overflow != 0) ||
        (index_s >= data_content.size))
    {
        return NULL;
    }
    else
    {
        size_t length_s;
        overflow = size_t_from_cgbn(length_s, length);
        if (
            (overflow != 0) ||
            (length_s > data_content.size - index_s))
        {
            available_size = data_content.size - index_s;
            return data_content.data + index_s;
        }
        else
        {
            available_size = length_s;
            return data_content.data + index_s;
        }
    }
  }

  __host__ __device__ __forceinline__ void evm_words_gas_cost
  (
    bn_t &gas_used,
    bn_t &length,
    uint32_t gas_per_word
  )
  {
    // gas_used += gas_per_word * emv word count of length
    // length = (length + 31) / 32
    bn_t evm_words_gas;
    cgbn_add_ui32(_env, evm_words_gas, length, BYTES -1);
    cgbn_div_ui32(_env, evm_words_gas, evm_words_gas, BYTES);
    cgbn_mul_ui32(_env, evm_words_gas, evm_words_gas, gas_per_word);
    cgbn_add(_env, gas_used, gas_used, evm_words_gas);
  }

  __host__ __device__ __forceinline__ void initcode_cost(
    bn_t &gas_used,
    bn_t &initcode_length
  )
  {
    // gas_used += GAS_INITCODE_WORD_COST * emv word count of initcode
    // length = (initcode_length + 31) / 32
    evm_words_gas_cost(gas_used, initcode_length, GAS_INITCODE_WORD_COST);
  }

  __host__ __device__ __forceinline__ void keccak_cost(
    bn_t &gas_used,
    bn_t &length
  )
  {
    evm_words_gas_cost(gas_used, length, GAS_KECCAK256_WORD);
  }



};

#endif