Code Monkey home page Code Monkey logo

kakarot-ssj's Introduction


GitHub Workflow Status GitHub GitHub contributors GitHub top language Telegram Contributions welcome Read FAQ GitHub Repo stars Twitter Follow

Table of Contents

About

Kakarot is an (zk)-Ethereum Virtual Machine implementation written in Cairo. Kakarot is Ethereum compatible, i.e. all existing smart contracts, developer tools and wallets work out-of-the-box on Kakarot. It's been open source from day one. Soon available on Starknet L2 and L3.

🚧 It is a work in progress, and it is not ready for production.

Getting Started

This repository is a rewrite of the first version of Kakarot zkEVM.

Installation

  • Install Scarb. To make sure your version always matches the one used by Kakarot, you can install Scarb via asdf.

  • Install Bun to run the JavaScript scripts to compute the Starknet address.

  • Fork the repository and clone your fork (git clone https://github.com/<YOUR_USERNAME>/kakarot-ssj)

  • Run make install to install the git hooks.

  • Add your environment variables to .env (see .env.example for an example).

    • Get your Github token here

Usage

Build

scarb build

Test

scarb test

Format

The project uses trunk for everything apart cairo files. If you don't have it installed already, you can do:

curl https://get.trunk.io -fsSL | bash

then

trunk check --fix

VS Code users, don't miss the VS Code trunk plugin.

For cairo files, run:

scarb fmt

Roadmap

See the open issues for a list of proposed features (and known issues).

Support

Reach out to the maintainer at one of the following places:

Project assistance

If you want to say thank you or/and support active development of Kakarot:

Together, we can make Kakarot better!

Contributing

First off, thanks for taking the time to contribute! Contributions are what make the open-source community such an amazing place to learn, inspire, and create. Any contribution you make will benefit everybody else and is greatly appreciated.

Please read our contribution guidelines, and thank you for being involved!

Authors & contributors

For a full list of all authors and contributors, see the contributors page.

Security

Kakarot follows good practices of security, but 100% security cannot be assured. Kakarot is provided "as is" without any warranty. Use at your own risk.

For more information and to report security issues, please refer to our security documentation.

License

This project is licensed under the MIT license.

See LICENSE for more information.

Acknowledgements

Contributors ✨

Thanks goes to these wonderful people (emoji key):

Abdel @ StarkWare
Abdel @ StarkWare

πŸ’»
johann bestowrous
johann bestowrous

πŸ’»
Elias Tazartes
Elias Tazartes

πŸ‘€ βœ… πŸ“’
Mathieu
Mathieu

πŸ’» ⚠️ πŸ“–
khaeljy
khaeljy

πŸ’»
ClΓ©ment Walter
ClΓ©ment Walter

πŸ’»
Lucas @ StarkWare
Lucas @ StarkWare

πŸ’»
lambda-0x
lambda-0x

πŸ’»
danilowhk
danilowhk

πŸ’»
Tristan
Tristan

πŸ’»
Quentash
Quentash

πŸ’»
ftupas
ftupas

πŸ’»
Aniket Prajapati
Aniket Prajapati

πŸ’»
Daniel Bejarano
Daniel Bejarano

πŸ’»
Noeljarillo
Noeljarillo

πŸ’»
Thomas Butler
Thomas Butler

πŸ’»
Ammar Arif
Ammar Arif

πŸ“–
greged93
greged93

πŸ’»
Charlotte
Charlotte

πŸ’»
akhercha
akhercha

πŸ’»
Alexandro T. Netto
Alexandro T. Netto

πŸ’»
tedison
tedison

πŸ’»
Pia
Pia

πŸ’»
glihm
glihm

πŸ’»
Add your contributions

This project follows the all-contributors specification. Contributions of any kind welcome!

kakarot-ssj's People

Contributors

abdelstark avatar akhercha avatar alextnetto avatar aniketpr01 avatar bajpai244 avatar chachaleo avatar clementwalter avatar d-roak avatar danilowhk avatar dbejarano820 avatar edisontim avatar eikix avatar enitrat avatar ftupas avatar glihm avatar greged93 avatar jobez avatar kariy avatar khaeljy avatar lambda-0x avatar lucaslvy avatar mohiiit avatar noeljarillo avatar quentash avatar remybar avatar rkdud007 avatar tadev0 avatar trbutler4 avatar ulerdogan avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

kakarot-ssj's Issues

feat: implement 0x17 - OR opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first binary value.
  2. b: second binary value.

Stack output

  1. a | b: the bitwise OR result.

Examples

* Input Output * * Input Output
1 0xF0 0xFF * 1 0xFF 0xFF
2 0xF * 2 0xFF

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

dev: optimize memory by using 32-byte words

Description

Memory is one of the core elements of the EVM. Having it as optimized as possible would reduce the overall costs of all transactions ran through the EVM.

Here is my proposal: We can refactor the memory to be 32-bytes words based instead of the current 16-bytes words based implementation.

I started to implement the new version to run some benchmarks. Below are the related code snippets.

16-bytes words memory (current implementation)

    fn store(ref self: Memory, element: u256, offset: usize) {
        let x = testing::get_available_gas();
        gas::withdraw_gas().unwrap();
        let new_min_bytes_len = helpers::ceil_bytes_len_to_next_32_bytes_word(offset + 32);

        let new_bytes_len = if new_min_bytes_len > self.bytes_len {
            new_min_bytes_len
        } else {
            self.bytes_len
        };
        self.bytes_len = new_bytes_len;

        // Check alignment of offset to bytes16 chunks
        let (chunk_index, offset_in_chunk) = u32_safe_divmod(offset, u32_as_non_zero(16));

        if offset_in_chunk == 0 {
            // Offset is aligned. This is the simplest and most efficient case,
            // so we optimize for it.
            self.items.store_u256(element, chunk_index);
            return ();
        }

        // Offset is misaligned.
        // |   W0   |   W1   |   w2   |
        //     |  EL_H  |  EL_L  |
        // ^---^
        //   |-- mask = 256 ** offset_in_chunk

        let mask: u256 = helpers::pow256_rev(offset_in_chunk);
        let mask_c: u256 = utils::pow(256, 16).into() / mask;

        // Split the 2 input bytes16 chunks at offset_in_chunk.

        let (el_hh, el_hl) = u256_safe_div_rem(
            u256 { low: element.high, high: 0 }, u256_as_non_zero(mask_c)
        );

        let (el_lh, el_ll) = u256_safe_div_rem(
            u256 { low: element.low, high: 0 }, u256_as_non_zero(mask_c)
        );

        // Read the words at chunk_index, chunk_index + 2.
        let w0: u128 = self.items.get(chunk_index.into());
        let w2: u128 = self.items.get(chunk_index.into() + 2);

        // Compute the new words
        let w0_h: u256 = (w0.into() / mask);
        let w2_l: u256 = (w2.into() / mask);

        // We can convert them back to felt252 as we know they fit in one word.
        let new_w0: u128 = (w0_h.into() * mask + el_hh).try_into().unwrap();
        let new_w1: u128 = (el_hl.into() * mask + el_lh).try_into().unwrap();
        let new_w2: u128 = (el_ll.into() * mask + w2_l).try_into().unwrap();

        // Write the new words
        self.items.insert(chunk_index.into(), new_w0);
        self.items.insert(chunk_index.into() + 1, new_w1);
        self.items.insert(chunk_index.into() + 2, new_w2);
        (x - testing::get_available_gas()).print();
    }

32-bytes words memory (optimisation proposal)

    fn store(ref self: Memory, element: u256, offset: usize) {
        let x = testing::get_available_gas();
        gas::withdraw_gas().unwrap();
        let new_min_bytes_len = helpers::ceil_bytes_len_to_next_32_bytes_word(offset + 32);

        let new_bytes_len = if new_min_bytes_len > self.bytes_len {
            new_min_bytes_len
        } else {
            self.bytes_len
        };
        self.bytes_len = new_bytes_len;

        // Check alignment of offset to 32-bytes chunks
        let (chunk_index, offset_in_chunk) = u32_safe_divmod(offset, u32_as_non_zero(32));

        if offset_in_chunk == 0 {
            // Offset is aligned. This is the simplest and most efficient case,
            // so we optimize for it.
            self.items.insert(chunk_index.into(), NullableExtensionTrait::new(element));
            return ();
        }

        // Offset is misaligned.
        // |   W0     |   W1   |
        //     |     EL     |
        // ^---^
        //   |-- mask = 256 ** offset_in_chunk

        let mask: u256 = helpers::pow256_rev(offset_in_chunk);
        let mask_c: u256 = utils::pow(256, 32).into() / mask;

        // Split the input two chunks at offset_in_chunk.

        let (el_h, el_l) = u256_safe_div_rem(element, u256_as_non_zero(mask_c));

        // Read the words at chunk_index, chunk_index + 1.
        let w0: u256 = self.items.get(chunk_index.into()).deref_or_0();
        let w1: u256 = self.items.get(chunk_index.into() + 1).deref_or_0();

        // Compute the new words
        let w0: u256 = (w0.into() / mask);
        let w1: u256 = (w1.into() / mask);

        // We can convert them back to felt252 as we know they fit in one word.
        let new_w0: u256 = (w0.into() * mask + el_h);
        let new_w1: u256 = (el_l.into() * mask + w1);

        // Write the new words

        self.items.insert(chunk_index.into(), NullableExtensionTrait::new(new_w0));
        self.items.insert(chunk_index.into() + 1, NullableExtensionTrait::new(new_w1));
        (x - testing::get_available_gas()).print();
    }
}

Benchmarks

I ran tests on both implementations and computed the actual gas usage of both functions. Here is the output

❯ scarb test -f test_store_should_add_an_element_to_the_memory_with_offset
testing kakarot ...
running 2 tests
[DEBUG(memory_32_bytes_chunks)]	                               	(raw: 0x36cfe

[DEBUG(memory_16_bytes_chunks)]	                               	(raw: 0x38e78

This leads to a ~4% gas decrease for each store operation

Proposal

Replace the 16-bytes words implementation with the 32-bytes words one.

dev: investigate costs of using methods on structs rather than pure functions

Does having EVMInterpreter as an empty struct better than having, run, decode_and_execute and other functions inside a mod? Or maybe if they are a trait for ExecutionContext itself? Curious if there is any performance difference

e.g.

#[derive(Drop, Copy)]
struct EVMInterpreter {}

fn foo(){
    let x = EVMInterpreter{};
    x.run()
}

vs

```rust
mod EVMInterpreter {}

fn foo(){
    EVMInterpreter::run()
}

feat: implement 0x1D - SAR opcode


fork: Constantinople
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Notes

Shift the bits towards the least significant one. The bits moved before the first one are discarded, the new bits are set to 0 if the previous most significant bit was 0, otherwise the new bits are set to 1.

Stack input

  1. shift: number of bits to shift to the right.
  2. value: integer to shift.

Stack output

  1. value >> shift: the shifted value.

Examples

* Input Output
1 1 1
2 2
* Input Output
1 4 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
2 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

dev: Explore how we architect "Contract Account" to store bytecode as 32 or 16 bytes instead of 1-byte / storage

Describe the Feature Request

On Cairo 0, we are currently storing all bytecodes as 1-byte data. So for a 10Kb contract, we are triggering 10.000 stores(when deploying the contract) and also 10.000 reads(on any contract call), which is expensive as for every storage or read a hash is calculated.

With storage improvement to 32 or 16 bytes, we decrease the amount of store and reads.

Describe Preferred/Possible Solution

  1. Use storage index: Besides storing 32/16 bytes for each storage, once the hash for the storage location is calculated we can access up to 256 more slots by adding to the index.

  2. Use dw(currently not possible). We could use dw to store the code in the contract and just access them as needed.

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: implement: 0X0A - EXP

SINCE GROUP
Frontier Stop and Arithmetic Operations

Reproduce in playground.

Error cases

The state changes done by the current context areΒ revertedΒ in those cases:

  • Not enough gas.
  • Not enough values on the stack.

Gas

static_gas = 10
dynamic_gas = 50 * exponent_byte_size

The dynamic gas is simply a function of how many bytes we need to represent the exponent inΒ binary.

SINCE GROUP Frontier Stop and Arithmetic Operations Index 1 is top of the stack. See [PUSH](https://www.evm.codes/#60).

Index 1 is top of the stack. SeeΒ PUSH.

Stack input

  1. a: integer base.
  2. exponent: integer exponent.

Stack output

  1. a ** exponent: integer result of the exponential operation modulo 2256.

Examples

Β  Input Output Β  Β  Input Output
1 10 100 Β  1 2 4
2 2 Β  Β  2 2 Β 

Reproduce in playground.


Index 1 is top of the stack. See [PUSH](https://www.evm.codes/#60).

feat: implement 0x18 - XOR opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first binary value.
  2. b: second binary value.

Stack output

  1. a ^ b: the bitwise XOR result.

Examples

* Input Output * * Input Output
1 0xF0 0xFF * 1 0xFF 0
2 0xF * 2 0xFF

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

meta-epic: plan the migration and create corresponding issues + spec the issues

Feature Request

I have looked a bit into ssj, there still needs to be a lot (almost all) of issues created and spec’d
Namely:

Tasks

feat: implement 0x15 - ISZERO opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: integer.

Stack output

  1. a == 0: 1 if a is 0, 0 otherwise.

Examples

* Input Output * * Input Output
1 10 0 * 1 0 1

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x13 - SGT opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Notes

All values are treated as two's complement signed 256-bit integers.

Stack input

  1. a: left side signed integer.
  2. b: right side signed integer.

Stack output

  1. a > b: 1 if the left side is bigger, 0 otherwise.

Examples

* Input Output * * Input Output
1 0 1 * 1 10 0
2 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF * 2 10

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: u256 mulmod math function

Feature Request

The core library doesn't expose a u256 mulmod libfunc, nor does it expose a normal function that performs (a*b) mod n.
We need to implement one for the #4 opcode
Describe the Feature Request

Describe Preferred Solution

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

bug: ADDMOD opcode intermediate calculations should not be subject to the 2256 modulo.

let (add_res, _) = u256_overflowing_add(*popped[0], *popped[1]);

Using overflowing_add here is equivalent to performing an addition modulo 2^256. In the ADDMOD case, we don't want this intermediate calculation to be modulo 2^256.

Instead, what we can do is either:

  • Add the u256s and return a u512. apply u512_safe_div_rem_by_u256 and keep the remainder
  • compute (a mod N) + (b mod N) mod N, since (a + b) mod n = [(a mod n) + (b mod n)] mod n.

The most gas-efficient solution of these two will be implemented.

feat: u256 exponentiation math function

Feature Request

Implement a u256 exponentiation math function.
You will compare the costs of using a fast-exponentiation algorithm vs. using a simple exponentiation algorithm, and submit the most adapted one
Describe the Feature Request

Describe Preferred Solution

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

bug: `mod` opcodes should return 0 if modulo is zero

Bug Report

Kakarot version:

Current implementation of modular opcodes operations should specifically handle the case where the divider is 0 - like it's done for div.
Current behavior:

    fn exec_mod(ref self: ExecutionContext) {
        // Stack input:
        // 0 = a: number
        // 1 = b: modulo
        let popped = self.stack.pop_n(2);

        // Compute the result of a mod b
        let result = *popped[0] % *popped[1];

        // Stack output:
        // a%b: unsigned integer result of a mod b
        self.stack.push(result);
    }

Expected behavior:

 fn exec_mod(ref self: ExecutionContext) {
        // Stack input:
        // 0 = a: number
        // 1 = b: modulo
        let popped = self.stack.pop_n(2);

        let mut result = 0;
        let b = *popped[1];
        if b != 0 {
            // Compute the result of a mod b
            result = *popped[0] % *popped[1];
        }

        // Stack output:
        // a % b: integer result of the integer modulo. If the denominator is 0, the result will be 0.
        self.stack.push(result);
    }

Steps to reproduce:

Related code:

Other information:

feat: implement: 0X09 - MULMOD

SINCE GROUP
Frontier Stop and Arithmetic Operations

Notes

All intermediate calculations of this operation are not subject to the 2^256 modulo.

Stack input

  1. a: first integer value to multiply.
  2. b: second integer value to multiply.
  3. N: integer denominator.

Stack output

(a * b) % c: integer result of the multiplication followed by a modulo. If the denominator is 0, the result will be 0.

Examples

Reproduce in playground

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x01 - ADD opcode


fork: Frontier
group: Stop and Arithmetic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first integer value to add.
  2. b: second integer value to add.

Stack output

  1. a + b: integer result of the addition modulo 2256.

Examples

* Input Output * * Input Output
1 10 20 * 1 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0
2 10 * 2 1

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

dev: clean up comments as discussed with the team

From @enitrat
imo we go from

/// ADDMOD operation.
/// Addition modulo operation
/// # Additional informations:
/// - Since: Frontier
/// - Group: Stop and Arithmetic Operations
/// - Gas: 8
/// - Stack consumed elements: 2
/// - Stack produced elements: 1
/// # Arguments
/// * self - the execution context
fn exec_addmod(ref self: ExecutionContext) {

to

/// MULMOD operation.
/// (a * b) % N: integer result of the multiplication followed by a modulo.
/// All intermediate calculations of this operation are not subject to the 2^256 modulo.
/// If the denominator is 0, the result will be 0.
fn exec_mulmod(ref self: ExecutionContext) {

dev: refactor Memory to use `Nullable<u256>`

After further investigatoin, this issue should not be implemented until we can properly benchmark the benefits of doing such an optimization. Relying on Sierra gas and testing::get_available_gas doest not seem to be a good way to benchmark for now.

feat: implement 0x04 - DIV


fork: Frontier
group: Stop and Arithmetic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: integer numerator
  2. b: integer denominator.

Stack output

  1. a // b: integer result of the integer division. If the denominator is 0, the result will be 0.

Examples

* Input Output * * Input Output
1 10 1 * 1 1 0
2 10 * 2 2

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x14 - EQ opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: left side integer.
  2. b: right side integer.

Stack output

  1. a == b: 1 if the left side is equal to the right side, 0 otherwise.

Examples

* Input Output * * Input Output
1 10 1 * 1 10 0
2 10 * 2 5

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x16 - AND opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first binary value.
  2. b: second binary value.

Stack output

  1. a & b: the bitwise AND result.

Examples

* Input Output * * Input Output
1 0xF 0xF * 1 0xFF 0
2 0xF * 2 0

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x11 - GT opcode


since: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: left side integer.
  2. b: right side integer.

Stack output

  1. a > b: 1 if the left side is bigger, 0 otherwise.

Examples

* Input Output * * Input Output
1 10 1 * 1 10 0
2 9 * 2 10

Related Kakarot 0.10 Issue

kkrt-labs/kakarot#36

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x05 - SDIV opcode


fork: Frontier
group: Stop and Arithmetic Operations

Index 1 is top of the stack. See PUSH.

Notes

All values are treated as two's complement signed 256-bit integers. Note the overflow semantic when 2255 is negated.

Stack input

  1. a: integer numerator.
  2. b: integer denominator.

Stack output

  1. a // b: integer result of the signed integer division. If the denominator is 0, the result will be 0.

Examples

* Input Output * * Input Output
1 10 1 * 1 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE 2
2 10 * 2 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: compute intrisic gas costs

Feature Request

Describe the Feature Request
Implement the compute intrinsic gas costs as a method of ExecutionModel.

This method will compute the gas cost of the current transaction, based on per transaction constant and cost of input data (16 gas per non-zero byte and 4 gas per zero byte), and return it.

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: explore how to run a test using blockifier instead of cairo-test

Feature Request

Describe the Feature Request

Tasks

feat: create a folder architecture comparable to kakarot 0

Feature Request

Describe the Feature Request

Describe Preferred Solution

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: create a ExecutionContext struct that is adapted to the migration path

Feature Request

Describe the Feature Request

We need to describe the new ExecutionContext in kakarot-ssj

Kakarot Cairo 0:

  struct ExecutionContext {
        call_context: CallContext*,
        program_counter: felt,
        stopped: felt,
        return_data: felt*,
        return_data_len: felt,
        stack: Stack*,
        memory: Memory*,
        gas_used: felt,
        gas_limit: felt,
        gas_price: felt,
        starknet_contract_address: felt,
        evm_contract_address: felt,
        calling_context: ExecutionContext*,
        sub_context: ExecutionContext*,
        destroy_contracts_len: felt,
        destroy_contracts: felt*,
        events_len: felt,
        events: Event*,
        create_addresses_len: felt,
        create_addresses: felt*,
        revert_contract_state: RevertContractState*,
        reverted: felt,
        read_only: felt,
    }

Describe Preferred Solution

We could split the ExecutionContext well in themes:
dynamic, static data

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: Memory

Feature Request

Describe the Feature Request

For the purpose of Kakarot, we need a Memory library that follows the EVM specification: a byte-size memory, accessed by offset.

Describe Preferred Solution

The Memory has 3 entry points:

  • store_n: stores n bytes into memory at offset offset. If offset > memory.size(), expand memory.
  • store (for optimisation purposes): stores 32 bytes into memory at offset offset, if offset > memory.size(), expand memory
  • load_n: loads n bytes accessed at offset offset into a variable. If offset > memory.size() - 32, expand memory
  • load: loads 32 bytes accessed at offset offset into a variable. If offset > memory.size() - 32, expand memory
  • expand(n): padds memory with n zeroes at offset offset

Describe Alternatives

Related Code

https://github.com/sayajin-labs/kakarot/blob/main/src/kakarot/memory.cairo

Additional Context

This feature needs EXTENSIVE testing.

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: implement 0x1C - SHR opcode


fork: Constantinople
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Notes

Shift the bits towards the least significant one. The bits moved before the first one are discarded, the new bits are set to 0.

Stack input

  1. shift: number of bits to shift to the right.
  2. value: 32 bytes to shift.

Stack output

  1. value >> shift: the shifted value. If shift is bigger than 255, returns 0.

Examples

* Input Output * * Input Output
1 1 1 * 1 4 0xF
2 2 * 2 0xFF

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x1B - SHL opcode


fork: Constantinople
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Notes

Shift the bits towards the most significant one. The bits moved after the 256th one are discarded, the new bits are set to 0.

Stack input

  1. shift: number of bits to shift to the left.
  2. value: 32 bytes to shift.

Stack output

  1. value << shift: the shifted value. If shift is bigger than 255, returns 0.

Examples

* Input Output
1 1 2
2 1
* Input Output
1 4 0xF000000000000000000000000000000000000000000000000000000000000000
2 0xFF00000000000000000000000000000000000000000000000000000000000000

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement: 0X08 - ADDMOD

SINCE GROUP
Frontier Stop and Arithmetic Operations
Reproduce in playground.

Error cases

The state changes done by the current context areΒ revertedΒ in those cases:

  • Not enough gas.
  • Not enough values on the stack.

SINCE GROUP Frontier Stop and Arithmetic Operations Index 1 is top of the stack. See PUSH.

Notes

All values are treated as two’s complement signed 256-bit integers. Note the overflow semantic when βˆ’2255 is negated.

Stack input

a: integer. b: integer. c: integer.

Stack output

a + b % c: integer result of the addition integer modulo.

Examples

Reproduce in playground.

Related Kakarot 0.10 Issue

kkrt-labs/kakarot#25

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x03 - SUB opcode


fork: Frontier
group: Stop and Arithmetic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first integer value.
  2. b: integer value to subtract to the first.

Stack output

  1. a - b: integer result of the subtraction modulo 2256.

Examples

* Input Output * * Input Output
1 10 0 * 1 0 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
2 10 * 2 1

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: two's complement signed integer division

Feature Request

The SDIV Opcode performs signed division on u256 values.
There is no such thing as signed division on unsigned integers implemented yet in the core library, and the signed integer types do not go up to 256 bits yet.
Describe the Feature Request
Write a math library for signed integer division, using 256-bits values using two's complement
Describe Preferred Solution

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

feat: implement 0x19 - NOT opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: binary value.

Stack output

  1. ~a: the bitwise NOT result.

Example

* Input Output
1 0 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

epic: test evm conformance with ethereum general state transition tests

Feature Request

Describe the Feature Request

General State Tests are tests created by the ethereum foundation to test transaction handling.

The general structure is:

We should focus on using the structure of these tests, specifically for the Merge fork, to establish adequate transaction handling of our evm implementation.

Describe Preferred Solution

There are two distinct formats of GeneralStateTests that can be somewhat confusing:

General State Tests Format: This format is well-documented here and primarily focuses on the specifics of the state transition for Ethereum transactions.

Blockchain Tests Format: Within the blockchain tests folder structure, there's a sub-folder named /BlockchainTests/GeneralStateTests. This essentially contains a copy of the General State Tests. However, instead of just focusing on the state transition, it runs the tests within the broader context of blockchain logic. The same tests in a different format is explicitly mentioned here, but here in the latest docs.

For our purposes, we'll be utilizing both formats of GeneralStateTests. Why? The Blockchain Test format offers a more detailed post-state. By combining this with the GeneralStateTest format, we can concentrate on the state transition of transactions without the overhead of mimicking how ethereum manages state (via a merkle tree). This approach provides a streamlined method to assert post-state transitions.

In rust-like pseudocode, the general shape of test running would look like

let general_state_tests = directory("tests/GeneralStateTests/").filterJSONFiles();

for (general_state_test, general_state_test_filename) in general_state_tests {
  // focus on the Merge fork
    let sub_tests = general_state_test.post["Merge"];
    for (sub_test, sub_test_idx) in sub_tests {
        // fresh kakarot state is fresh starknet state, with the kakarot system deployed on top
        let prepared_kakarot_state = fresh_kakarot_state();
        // the transaction in a general state test has arrays for data, value, and gas for each transaction. the post state defines concrete indexes for the expected results 
        let concrete_transaction = general_state_test.transaction(sub_test.indexes.data, sub_test.indexes.value, sub_test.indexes.gas);
        // eth->starknet types here
        let kakarot_env = general_state_test.env.into();
        let kakarot_transaction = concrete_transaction.into();
        // initialize test's desired pre state
        for (address, state) in general_state_test.pre {
            prepared_kakarot_state.write(address, state);
            
            }

        // assuming we define a unique entry point for the general state tests, like we have for the `test_cases` for bytecode strings                  
        let (return_data: felt*, updated_kakarot_state, events) = await kakarot_system.general_state_test_entry_point(prepared_kakarot_state, kakarot_env, kakarot_transaction);

   // here load a unique file generated from a conformant evm (i.e. geth) to get the post state we check against
        
   let post_state_check = subtest.post;
   // we assert that the geth-evaluated trace matches our post state     
   assert_conformance!(updated_kakarot_state, post_state_check);

    let eth_logs = event.into();
    // need to convert starknet log data to ethereum logs and rlp encode and keccak hash the logs to assert equality
    // see: https://github.com/ethereum/go-ethereum/blob/master/tests/state_test_util.go#L207
    let hashed_rlp_logs = encode_and_hash_log(eth_logs);
    assert_eq!(hashed_rlp_logs, sub_test.logs);
        

  
    }
}

Tasks

  1. low-priority

Related Code

Additional Context

If the feature request is approved, would you be willing to submit a PR?
(Help can be provided if you need assistance submitting a PR)

  • Yes
  • No

derive necessary features from starknet foundry to run general state transition tests

The goal of this issue is to discuss/determine what we need to run the general state tests to help guide potential contributions to starknet foundry.

I am assuming that it would be simpler to have a pre-running script that would take the ethereum test data and express it in terms of starknet types, so sn-foundry would just need to load the json.

Possible shape of running the GeneralStateTests:

feat: implement 0x12 - SLT opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Notes

All values are treated as two's complement signed 256-bit integers.

Stack input

  1. a: left side signed integer.
  2. b: right side signed integer.

Stack output

  1. a < b: 1 if the left side is smaller, 0 otherwise.

Examples

* Input Output * * Input Output
1 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 1 * 1 10 0
2 0 * 2 10

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x1A - BYTE opcode


fork: Frontier
group: Comparison & Bitwise Logic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. i: byte offset starting from the most significant byte.
  2. x: 32-byte value.

Stack output

  1. y: the indicated byte at the least significant position. If the byte offset is out of range, the result is 0.

Examples

* Input Output * * Input Output
1 31 0xFF * 1 30 0xFF
2 0xFF * 2 0xFF00

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

feat: implement 0x00 - STOP opcode


fork: Frontier
group: Stop and Arithmetic Operations

Notes

Exits the current context successfully.

When a call is executed on an address with no code and the EVM tries to read the code data, the default value is returned, 0, which corresponds to this instruction and halts the execution.

feat: implement 0x02 - MUL


fork: Frontier
group: Stop and Arithmetic Operations

Index 1 is top of the stack. See PUSH.

Stack input

  1. a: first integer value to multiply.
  2. b: second integer value to multiply.

Stack output

  1. a * b: integer result of the multiplication modulo 2256.

Examples

* Input Output
1 10 100
2 10
* Input Output
1 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE
2 2

Reproduce in playground.

Error cases

The state changes done by the current context are reverted in those cases:

  • Not enough values on the stack.

dev: migrate to starknet-foundry

We should use starknet-foundry to test our code rather than the native cairo-test runner. It will have future feature that we will need.

We need #[should_panic] to be available before that.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.