Git Product home page Git Product logo

status-im / nimbus-eth1 Goto Github PK

View Code? Open in Web Editor NEW
565.0 73.0 110.0 83.96 MB

Nimbus: an Ethereum Execution Client for Resource-Restricted Devices

Home Page: https://status-im.github.io/nimbus-eth1/

License: Apache License 2.0

Nim 97.61% Nix 0.23% Makefile 0.47% Shell 0.64% JavaScript 0.31% HTML 0.14% Dockerfile 0.10% jq 0.04% CodeQL 0.35% C 0.02% CSS 0.01% Python 0.10%
ethereum blockchain mobile android ios nim nim-lang nim-language raspberry-pi nimbus

nimbus-eth1's Introduction

Nimbus: ultra-light Ethereum execution layer client

License: Apache License: MIT GH action-nimbus-eth1 GH action-fluffy

Discord: Nimbus Status: #nimbus-general

Introduction

This repository contains development work on an execution-layer client to pair with our consensus-layer client. This client focuses on efficiency and security and strives to be as light-weight as possible in terms of resources used.

This repository is also home to:

All consensus-layer client development is happening in parallel in the nimbus-eth2 repository.

Development Updates

Monthly development updates are shared here.

Some recent highlights include:

  • Renewed funding from the EF to accelerate development
  • Completed Berlin and London fork compatibility (EIP-1559). It now passes nearly all the EF Hive testsuite, and 100% of contract execution tests (47,951 tests)
  • New GraphQL and WebSocket APIs, complementing JSON-RPC
  • EVMC compatibility, supporting third-party optimised EVM plugins
  • Up to 100x memory saving during contract executions
  • Asynchronous EVM to execute many contracts in parallel, while they wait for data from the network
  • Updated network protocols, to work with the latest eth/66-68 protocols
  • A prototype new mechanism for state sync which combines what have been called Fast sync, Snap sync and Beam sync in a self-tuning way, and allows the user to participate in the network (read accounts, run transactions etc.) while sync is still in progress
  • A significant redesign of the storage database to use less disk space and run faster.

For more detailed write-ups on the development progress, follow the Nimbus blog.

Building & Testing

Prerequisites

  • RocksDB
  • GNU Make, Bash and the usual POSIX utilities. Git 2.9.4 or newer.

On Windows, a precompiled DLL collection download is available through the fetch-dlls Makefile target: (Windows instructions).

# MacOS with Homebrew
brew install rocksdb

# Fedora
dnf install rocksdb-devel

# Debian and Ubuntu
sudo apt-get install librocksdb-dev

# Arch (AUR)
pakku -S rocksdb

rocksdb can also be installed by following their instructions.

Obtaining the prerequisites through the Nix package manager

Experimental

Users of the Nix package manager can install all prerequisites simply by running:

nix-shell default.nix

Build & Develop

POSIX-compatible OS

# The first `make` invocation will update all Git submodules.
# You'll run `make update` after each `git pull`, in the future, to keep those submodules up to date.
# Assuming you have 4 CPU cores available, you can ask Make to run 4 parallel jobs, with "-j4".

make -j4 nimbus

# See available command line options
build/nimbus --help

# Start syncing with mainnet
build/nimbus

# Update to latest version
git pull && make update
# Build the newly downloaded version
make -j4 nimbus

# Run tests
make test

To run a command that might use binaries from the Status Nim fork:

./env.sh bash # start a new interactive shell with the right env vars set
which nim
nim --version

# or without starting a new interactive shell:
./env.sh which nim
./env.sh nim --version

Our Wiki provides additional helpful information for debugging individual test cases and for pairing Nimbus with a locally running copy of Geth.

Windows

(Experimental support!)

Install Mingw-w64 for your architecture using the "MinGW-W64 Online Installer" (first link under the directory listing). Run it and select your architecture in the setup menu ("i686" on 32-bit, "x86_64" on 64-bit), set the threads to "win32" and the exceptions to "dwarf" on 32-bit and "seh" on 64-bit. Change the installation directory to "C:\mingw-w64" and add it to your system PATH in "My Computer"/"This PC" -> Properties -> Advanced system settings -> Environment Variables -> Path -> Edit -> New -> C:\mingw-w64\mingw64\bin (it's "C:\mingw-w64\mingw32\bin" on 32-bit)

Install Git for Windows and use it to clone Nimbus.

Install cmake.

After adding the Git bin directory to your path open a "Git Bash" shell:

bash

After installing Mingw-w64 and adding it to your path you should have the mingw32-make tool available. Next create a link from make to mingw32-make:

ln -s mingw32-make.exe make.exe

You can now follow those instructions in the previous section. For example:

make nimbus # build the Nimbus binary
make test # run the test suite
# etc.

Raspberry PI

Experimental The code can be compiled on a Raspberry PI:

  • Raspberry PI 3b+
  • 64gb SD Card (less might work too, but the default recommended 4-8GB will probably be too small)
  • Rasbian Buster Lite - Lite version is enough to get going and will save some disk space!

Assuming you're working with a freshly written image:

# Start by increasing swap size to 2gb:
sudo vi /etc/dphys-swapfile
# Set CONF_SWAPSIZE=2048
# :wq
sudo reboot

# Install prerequisites
sudo apt-get install git libgflags-dev libsnappy-dev

mkdir status
cd status

# Install rocksdb
git clone https://github.com/facebook/rocksdb.git
cd rocksdb
make shared_lib
sudo make install
cd..

# Raspberry pi doesn't include /usr/local/lib in library search path - need to add
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH

git clone https://github.com/status-im/nimbus.git

cd nimbus

# Follow instructions above!

Android

Experimental Code can be compiled and run on Android devices

Environment setup
  • Install the Termux app from FDroid or the Google Play store
  • Install a PRoot of your choice following the instructions for your preferred distribution. Note, the Ubuntu PRoot is known to contain all Nimbus prerequisites compiled on Arm64 architecture (common architecture for Android devices). Depending on the distribution, it may require effort beyond the scope of this guide to get all prerequisites.

Assuming Ubuntu PRoot is used

# Install prerequisites
apt install git make gcc librocksdb-dev

# Clone repo and build Nimbus just like above
git clone https://github.com/status-im/nimbus.git

cd nimbus

make

make nimbus

build/nimbus

Experimental make variables

Apart from standard Make flags (see link in the next chapter), the following Make variables can be set to control which version of a virtual engine is compiled. The variables are listed with decreasing priority (in case of doubt, the lower prioritised variable is ignored when the higher on is available.)

  • BOEHM_GC=1
    Change garbage collector to boehm. This might help debugging in certain cases when the gc is involved in a memory corruption or corruption camouflage.

  • ENABLE_CHUNKED_RLPX=0
    Disable legacy chunked RLPx messages which are enabled by default for synchronising against Nethermind nodes

  • ENABLE_EVMC=1
    Enable mostly EVMC compliant wrapper around the native Nim VM

  • ENABLE_VMLOWMEM=1
    Enable new re-factored version of the native Nim VM. This version is not optimised and coded in a way so that low memory compilers can handle it (observed on 32 bit windows 7.)

For these variables, using <variable>=0 is ignored and <variable>=2 has the same effect as <variable>=1 (ditto for other numbers.)

Other settings where the non-zero value matters:

  • ENABLE_ETH_VERSION=66
    Enable legacy protocol eth66 (or another available protocol version.)

Development tips

Interesting Make variables and targets are documented in the nimbus-build-system repo.

  • you can switch the DB backend with a Nim compiler define: -d:nimbus_db_backend=... where the (case-insensitive) value is one of "rocksdb" (the default), "sqlite", "lmdb"

  • the Premix debugging tools are documented separately

  • you can control the Makefile's verbosity with the V variable (defaults to 0):

make V=1 # verbose
make V=2 test # even more verbose
make LOG_LEVEL=DEBUG nimbus # this is the default
make LOG_LEVEL=TRACE nimbus # log everything
  • pass arbitrary parameters to the Nim compiler:
make NIMFLAGS="-d:release"
  • if you want to use SSH keys with GitHub (also handles submodules):
make github-ssh
  • force a Nim compiler rebuild:
rm vendor/Nim/bin/nim
make -j8 build-nim
  • some programs in the tests subdirectory do a replay of blockchain database dumps when compiled and run locally. The dumps are found in this module which need to be cloned as nimbus-eth1-blobs parellel to the nimbus-eth1 file system root.

Git submodule workflow

Working on a dependency:

cd vendor/nim-chronicles
git checkout -b mybranch
# make some changes
git status
git commit -a
git push origin mybranch
# create a GitHub PR and wait for it to be approved and merged
git checkout master
git pull
git branch -d mybranch
# realise that the merge was done without "--no-ff"
git branch -D mybranch
# update the submodule's commit in the superproject
cd ../..
git status
git add vendor/nim-chronicles
git commit

It's important that you only update the submodule commit after it's available upstream.

You might want to do this on a new branch of the superproject, so you can make a GitHub PR for it and see the CI test results.

Don't update all Git submodules at once, just because you found the relevant Git command or make target. You risk updating submodules to other people's latest commits when they are not ready to be used in the superproject.

Adding the submodule "https://github.com/status-im/foo" to "vendor/foo":

vendor/nimbus-build-system/scripts/add_submodule.sh status-im/foo
# or
./env.sh add_submodule status-im/foo
# want to place it in "vendor/bar" instead?
./env.sh add_submodule status-im/foo vendor/bar

Removing the submodule "vendor/bar":

git submodule deinit -f -- vendor/bar
git rm -f vendor/bar

Checking out older commits, either to bisect something or to reproduce an older build:

git checkout <commit hash here>
make clean
make -j8 update

Running a dependency's test suite using nim instead of nimble (which cannot be convinced not to run a dependency check, thus clashing with our jury-rigged "vendor/.nimble/pkgs"):

cd vendor/nim-rocksdb
../nimbus-build-system/scripts/nimble.sh test
# or
../../env.sh nimble test

Metric visualisation

Install Prometheus and Grafana. On Gentoo, it's emerge prometheus grafana-bin.

# build Nimbus
make nimbus
# the Prometheus daemon will create its data dir in the current dir, so give it its own directory
mkdir ../my_metrics
# copy the basic config file over there
cp -a examples/prometheus.yml ../my_metrics/
# start Prometheus in a separate terminal
cd ../my_metrics
prometheus --config.file=prometheus.yml # loads ./prometheus.yml, writes metric data to ./data
# start a fresh Nimbus sync and export metrics
rm -rf ~/.cache/nimbus/db; ./build/nimbus --prune:archive --metricsServer

Start the Grafana server. On Gentoo it's /etc/init.d/grafana start. Go to http://localhost:3000, log in with admin:admin and change the password.

Add Prometheus as a data source. The default address of http://localhost:9090 is OK, but Grafana 6.3.5 will not apply that semitransparent default you see in the form field, unless you click on it.

Create a new dashboard. Click on its default title in the upper left corner ("New Dashboard"). In the new page, click "Import dashboard" in the right column and upload "examples/Nimbus-Grafana-dashboard.json".

In the main panel, there's a hidden button used to assign metrics to the left or right Y-axis - it's the coloured line on the left of the metric name, in the graph legend.

To see a single metric, click on its name in the legend. Click it again to go back to the combined view. To edit a panel, click on its title and select "Edit".

Obligatory screenshot.

Troubleshooting

Report any errors you encounter, please, if not already documented!

  • Turn it off and on again:
make clean
make update

License

Licensed and distributed under either of

or

at your option. These files may not be copied, modified, or distributed except according to those terms.

nimbus-eth1's People

Contributors

0xc1c4da avatar acolytec3 avatar adamspitz avatar advaita-saha avatar alehander92 avatar alexm-status avatar arnetheduck avatar cheatfate avatar coffeepots avatar etan-status avatar hmel avatar jakubgs avatar jangko avatar jlokier avatar kdeme avatar konradstaniec avatar markspanbroek avatar mjfh avatar mratsim avatar mynameisdaniil avatar narimiran avatar oskarth avatar stefantalpalaru avatar swader avatar tersec avatar unixpi avatar web3-developer avatar yglukhov avatar zah avatar zedeus 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  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

nimbus-eth1's Issues

VM JSON tests do not run

As mentionned in #45 refactoring, tests_vm_json do not work anymore.

The tests compile but segfaults at the first test:

[Suite] vm json tests
vmTestsboolean.json
Traceback (most recent call last)
test_helpers.nim(48)     test_vm_json
computation.nim(299)     testFixture
computation.nim(293)     :anonymous
SIGSEGV: Illegal storage access. (Attempt to read from nil?)
Error: execution of an external program failed: './build/vm_json '

Note that previously, the test was failing anyway (Exception) but the test suite can recover from exceptions and continue testing on other part. See #32.

vmTestsboolean.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681
  [FAILED] tests/fixtures/VMTests/vmTests/boolean.json
vmTestssuicide.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681
  [FAILED] tests/fixtures/VMTests/vmTests/suicide.json
vmTestsmktx.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681

Test suite: arith.json, boolean.json, and mktx.json seem useless, perhaps worth skipping

- arith.json                                                      Fail
- boolean.json                                                    Fail
- mktx.json                                                       Fail

The failures are due, essentially, to CALL being stubbed enough not to notice that it's supposed to fail.

All lack a post/log/etc section, and as such according to http://ethereum-tests.readthedocs.io/en/latest/test_types/vm_tests.html expect an exception. That'd be fine, but even had CALL not been partly stubbed out, the exception is uninformative in each case, and purely a testing artifact; as:

Since these tests are meant only as a basic test of VM operation, the CALL and CREATE instructions are not actually executed. To provide the possibility of testing to guarantee they were actually run at all, a separate portion callcreates details each CALL or CREATE operation in the order they would have been executed.

They all, further, lack callcreates, yet have a CALL instruction (and no flow control avoiding it), so are expected to experience exceptional halting the exact same way, regardless of nominally testing arithmetic, boolean operations, or transaction creation. They add nothing I can see to Nimbus's development or testing in a meaningful way.

Finally, they were removed from the official test suite 3 months ago: ethereum/tests@4faa8c5.

I don't see why they shouldn't all be skipped.

For reference, mktx.json runs 0x60006000600060006706f05b59d3b200003360c85a03f1, or:

[1] PUSH1 0x00
[3] PUSH1 0x00
[5] PUSH1 0x00
[7] PUSH1 0x00
[16] PUSH8 0x06f05b59d3b20000
[17] CALLER
[19] PUSH1 0xc8
[20] GAS
[21] SUB
[22] CALL

I won't quite the entire disassembly of boolean.json and arith.json's code, but they're 0x600160011615601a57600060006000600060023360c85a03f1505b600060011615603557600060006000600060033360c85a03f1505b600160001615605057600060006000600060043360c85a03f1505b600060001615606b57600060006000600060053360c85a03f1505b6001600117156086576000600060006000600c3360c85a03f1505b60006001171560a1576000600060006000600d3360c85a03f1505b60016000171560bc576000600060006000600e3360c85a03f1505b60006000171560d7576000600060006000600f3360c85a03f1505b and 0x600060006000600060026002600803036002600306600260020460046004600402026002600201010101013360c85a03f1 for https://etherscan.io/opcode-tool use.

Evaluate Microsoft's FASTER key-value store as a potential replacement of RocksDB

Microsoft has recently published a new open-source embeddable key-value store that reports substantial performance improvements over RocksDB on certain workloads:

https://github.com/Microsoft/FASTER
https://www.microsoft.com/en-us/research/uploads/prod/2018/03/faster-sigmod18.pdf

Prior to shipping the server-side version of Nimbus intended for full nodes, we may evaluate FASTER against the current alternatives (RocksDB and SQLite at the moment).

For server-side full nodes, the primary selection criteria would be the database throughput, while on the primary target of Nimbus (mobile devices) it makes more sense to optimize for battery drain, memory and space usage and binary size.

Regression - gas consumption of addmod1_overflow2.json and mulmod1_overflow.json

After #49, both tests are now failing due to the SSTORE which has a consumeGas 20000 and refundGas 15000 part.

Logs before the PR

vmArithmeticTestaddmod1_overflow2.json
1 PUSH1 0x05
3 PUSH1 0x00
5 PUSH1 0x01
7 PUSH1 0x00
8 SUB
9 ADDMOD
11 PUSH1 0x00
12 SSTORE
-1 STOP
#vm.computation.BaseComputation: COMPUTATION STARTING: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n
#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 0
#gas: GAS CONSUMPTION: 10000 - 3 -> 9997 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 2
#gas: GAS CONSUMPTION: 9997 - 3 -> 9994 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 4
#gas: GAS CONSUMPTION: 9994 - 3 -> 9991 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  0
  1


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 6
#gas: GAS CONSUMPTION: 9991 - 3 -> 9988 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  0
  1
  0


#vm.computation.BaseComputation: OPCODE: 0x03 (SUB) | pc: 8
#gas: GAS CONSUMPTION: 9988 - 3 -> 9985 (SUB)
#vm.computation.BaseComputation: Stack:
  5
  0
  115792089237316195423570985008687907853269984665640564039457584007913129639935


#vm.computation.BaseComputation: OPCODE: 0x08 (ADDMOD) | pc: 9
#gas: GAS CONSUMPTION: 9985 - 8 -> 9977 (ADDMOD)
#vm.computation.BaseComputation: Stack:
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 10
#gas: GAS CONSUMPTION: 9977 - 3 -> 9974 (PUSH1)
#vm.computation.BaseComputation: Stack:
  0
  0


#vm.computation.BaseComputation: OPCODE: 0x55 (SSTORE) | pc: 12
#gas: GAS CONSUMPTION: 9974 - 0 -> 9974 (SSTORE)
#gas: GAS CONSUMPTION: 9974 - 5000 -> 4974 (SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0))
#vm.computation.BaseComputation: Stack:



#vm.computation.BaseComputation: COMPUTATION SUCCESS: from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n | gas-used: 5026 | gas-remaining: 4974
  [OK] tests/fixtures/VMTests/vmArithmeticTest/addmod1_overflow2.json

And

vmArithmeticTestmulmod1_overflow.json
1 PUSH1 0x05
3 PUSH1 0x02
5 PUSH1 0x01
7 PUSH1 0x00
8 SUB
9 MULMOD
11 PUSH1 0x00
12 SSTORE
-1 STOP
#vm.computation.BaseComputation: COMPUTATION STARTING: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5e
c6 | value: 1000000000000000000 | depth: 0 | static: n
#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 0
#gas: GAS CONSUMPTION: 10000 - 3 -> 9997 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 2
#gas: GAS CONSUMPTION: 9997 - 3 -> 9994 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  2


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 4
#gas: GAS CONSUMPTION: 9994 - 3 -> 9991 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  2
  1


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 6
#gas: GAS CONSUMPTION: 9991 - 3 -> 9988 (PUSH1)
#vm.computation.BaseComputation: Stack:
  5
  2
  1
  0


#vm.computation.BaseComputation: OPCODE: 0x03 (SUB) | pc: 8
#gas: GAS CONSUMPTION: 9988 - 3 -> 9985 (SUB)
#vm.computation.BaseComputation: Stack:
  5
  2
  115792089237316195423570985008687907853269984665640564039457584007913129639935


#vm.computation.BaseComputation: OPCODE: 0x09 (MULMOD) | pc: 9
#gas: GAS CONSUMPTION: 9985 - 8 -> 9977 (MULMOD)
#vm.computation.BaseComputation: Stack:
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (PUSH1) | pc: 10
#gas: GAS CONSUMPTION: 9977 - 3 -> 9974 (PUSH1)
#vm.computation.BaseComputation: Stack:
  0
  0


#vm.computation.BaseComputation: OPCODE: 0x55 (SSTORE) | pc: 12
#gas: GAS CONSUMPTION: 9974 - 0 -> 9974 (SSTORE)
#gas: GAS CONSUMPTION: 9974 - 5000 -> 4974 (SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0))
#vm.computation.BaseComputation: Stack:



#vm.computation.BaseComputation: COMPUTATION SUCCESS: from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n | gas-used: 5026 | gas-remaining: 4974
  [OK] tests/fixtures/VMTests/vmArithmeticTest/mulmod1_overflow.json

Logs after the PR

vmArithmeticTestaddmod1_overflow2.json
1 Push1 0x05
3 Push1 0x00
5 Push1 0x01
7 Push1 0x00
8 Sub
9 Addmod
11 Push1 0x00
12 Sstore
-1 Stop
#vm.computation.BaseComputation: COMPUTATION STARTING: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n
#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 0
#gas: GAS CONSUMPTION: 10000 - 3 -> 9997 (Push1)
#vm.computation.BaseComputation: Stack:
  5


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 2
#gas: GAS CONSUMPTION: 9997 - 3 -> 9994 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 4
#gas: GAS CONSUMPTION: 9994 - 3 -> 9991 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  0
  1


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 6
#gas: GAS CONSUMPTION: 9991 - 3 -> 9988 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  0
  1
  0


#vm.computation.BaseComputation: OPCODE: 0x03 (Sub) | pc: 8
#gas: GAS CONSUMPTION: 9988 - 3 -> 9985 (Sub)
#vm.computation.BaseComputation: Stack:
  5
  0
  115792089237316195423570985008687907853269984665640564039457584007913129639935


#vm.computation.BaseComputation: OPCODE: 0x08 (Addmod) | pc: 9
#gas: GAS CONSUMPTION: 9985 - 8 -> 9977 (Addmod)
#vm.computation.BaseComputation: Stack:
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 10
#gas: GAS CONSUMPTION: 9977 - 3 -> 9974 (Push1)
#vm.computation.BaseComputation: Stack:
  0
  0


#vm.computation.BaseComputation: OPCODE: 0x55 (Sstore) | pc: 12
#vm.computation.BaseComputation: COMPUTATION ERROR: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n | error: Out of gas: Needed 20000 - Remaining 9974 - Reason: SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0)
    test_vm_json.nim(70, 10): Check failed: not computation.isError
    computation.isError was true
Computation error: Out of gas: Needed 20000 - Remaining 9974 - Reason: SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0)
    Remaining: 9974 - Expected: 4974
    test_vm_json.nim(91, 53): Check failed: actualGasRemaining == expectedGasRemaining or
    computation.code.hasSStore() and
    (actualGasRemaining > expectedGasRemaining and
    (actualGasRemaining - expectedGasRemaining) mod 15000 == 0 or
    expectedGasRemaining > actualGasRemaining and
    (expectedGasRemaining - actualGasRemaining) mod 15000 == 0)
  [FAILED] tests/fixtures/VMTests/vmArithmeticTest/addmod1_overflow2.json

And

vmArithmeticTestmulmod1_overflow.json
1 Push1 0x05
3 Push1 0x02
5 Push1 0x01
7 Push1 0x00
8 Sub
9 Mulmod
11 Push1 0x00
12 Sstore
-1 Stop
#vm.computation.BaseComputation: COMPUTATION STARTING: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 |
depth: 0 | static: n
#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 0
#gas: GAS CONSUMPTION: 10000 - 3 -> 9997 (Push1)
#vm.computation.BaseComputation: Stack:
  5


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 2
#gas: GAS CONSUMPTION: 9997 - 3 -> 9994 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  2


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 4
#gas: GAS CONSUMPTION: 9994 - 3 -> 9991 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  2
  1


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 6
#gas: GAS CONSUMPTION: 9991 - 3 -> 9988 (Push1)
#vm.computation.BaseComputation: Stack:
  5
  2
  1
  0


#vm.computation.BaseComputation: OPCODE: 0x03 (Sub) | pc: 8
#gas: GAS CONSUMPTION: 9988 - 3 -> 9985 (Sub)
#vm.computation.BaseComputation: Stack:
  5
  2
  115792089237316195423570985008687907853269984665640564039457584007913129639935


#vm.computation.BaseComputation: OPCODE: 0x09 (Mulmod) | pc: 9
#gas: GAS CONSUMPTION: 9985 - 8 -> 9977 (Mulmod)
#vm.computation.BaseComputation: Stack:
  0


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 10
#gas: GAS CONSUMPTION: 9977 - 3 -> 9974 (Push1)
#vm.computation.BaseComputation: Stack:
  0
  0


#vm.computation.BaseComputation: OPCODE: 0x55 (Sstore) | pc: 12
#vm.computation.BaseComputation: COMPUTATION ERROR: gas: 10000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n | error: Out of gas: Needed 20000 - Remaining 9974 - Reason: SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0)
    test_vm_json.nim(70, 10): Check failed: not computation.isError
    computation.isError was true
Computation error: Out of gas: Needed 20000 - Remaining 9974 - Reason: SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 0 (0)
    Remaining: 9974 - Expected: 4974
    test_vm_json.nim(91, 53): Check failed: actualGasRemaining == expectedGasRemaining or
    computation.code.hasSStore() and
    (actualGasRemaining > expectedGasRemaining and
    (actualGasRemaining - expectedGasRemaining) mod 15000 == 0 or
    expectedGasRemaining > actualGasRemaining and
    (expectedGasRemaining - actualGasRemaining) mod 15000 == 0)
  [FAILED] tests/fixtures/VMTests/vmArithmeticTest/mulmod1_overflow.json

Opcodes & constant folding: evaluate hash table vs array

In a quest for higher performance, it would be best to avoid indirection at runtime.

Currently opcodes are implemented through a var hash table that is populated at runtime.

https://github.com/status-im/nimbus/blob/8528f1b70474f4fb77c57bdbd63da56828da2b25/nimbus/opcode_table.nim

  1. The hash-table should be populated at compile-time. Will be possible after #36.

  2. It is currently unclear if the C compiler can perform constant folding with Nim hash tables even if result is known at compile-time.

In case constant folding is not done, potentially significant function call/indexing overhead could happen (for each opcode).

In that case solutions could be:

  • Don't rely on the C compiler, force the NimVM to resolve value at compile-time. For example by adding a {.compileTime.} get proc for tables.
  • Use array instead of table. This is a bit less ergonomic as you can't index arrays with enum with holes.

Constant folding can be checked by comparing the ASM for:

import tables

const
  opcodes_table = {
    0x00: "STOP",
    0x01: "ADD",
    0x10: "LT"
  }.toTable()

  opcodes_array = [
    0x00: "STOP",
    0x01: "ADD",
    0x02: "NOP",
    0x03: "NOP",
    0x04: "NOP",
    0x05: "NOP",
    0x06: "NOP",
    0x07: "NOP",
    0x08: "NOP",
    0x09: "NOP",
    0x0A: "NOP",
    0x0B: "NOP",
    0x0C: "NOP",
    0x0D: "NOP",
    0x0E: "NOP",
    0x0F: "NOP",
    0x10: "LT"
  ]

echo opcodes_table[0x10]
echo opcodes_array[0x10]

NOP = No-op to fill holes.

Support Gas costs and balance >= 2^31 on 32-bit

#40 is introduce native integer for gas prices and balances via int64.

Internally it uses toInt proc from stint implemented as follow:

func toInt*(num: Stint or StUint): int {.inline.}=
  # Returns as int. Result is modulo 2^(sizeof(int)
  num.data.least_significant_word.int

On 32-bit, int32 would be returned instead of int64.

TODO: introduce a toInt64 conversion in Stint

Optionally, toInt64 might be wrapped in Nimbus in a toGasInt proc that will add logging and range check for example to make sure a gas value on the stack (uint256) is not over 2^63 - 1 before conversion.

Enforce int size

For int32 there were some issues with overflow when int is used in some cases.
To resolve this, it may be worth introducing a WordInt or Int alias in a similar vein to GasInt.

This allows us to enforce int64 where appropriate and have a central place to change this if required.

For more information, see #101

Message type - match EVMC

It would probably best to match EVMC fields rather than Py-EVM to ease interface with EVM-C and eWASM in the future.

EVMC Message type - https://github.com/ethereum/evmc/blob/master/include/evmc/evmc.h

/**
 * Big-endian 256-bit integer.
 *
 * 32 bytes of data representing big-endian 256-bit integer. I.e. bytes[0] is
 * the most significant byte, bytes[31] is the least significant byte.
 * This type is used to transfer to/from the VM values interpreted by the user
 * as both 256-bit integers and 256-bit hashes.
 */
struct evmc_uint256be
{
    /** The 32 bytes of the big-endian integer or hash. */
    uint8_t bytes[32];
};

/** Big-endian 160-bit hash suitable for keeping an Ethereum address. */
struct evmc_address
{
    /** The 20 bytes of the hash. */
    uint8_t bytes[20];
};

/** The kind of call-like instruction. */
enum evmc_call_kind
{
    EVMC_CALL = 0,         /**< Request CALL. */
    EVMC_DELEGATECALL = 1, /**< Request DELEGATECALL. The value param ignored. */
    EVMC_CALLCODE = 2,     /**< Request CALLCODE. */
    EVMC_CREATE = 3        /**< Request CREATE. Semantic of some params changes. */
};

/** The flags for ::evmc_message. */
enum evmc_flags
{
    EVMC_STATIC = 1 /**< Static call mode. */
};

/**
 * The message describing an EVM call,
 * including a zero-depth calls from a transaction origin.
 */
struct evmc_message
{
    /** The destination of the message. */
    struct evmc_address destination;

    /** The sender of the message. */
    struct evmc_address sender;

    /**
     * The amount of Ether transferred with the message.
     */
    struct evmc_uint256be value;

    /**
     * The message input data.
     *
     *  This MAY be NULL.
     */
    const uint8_t* input_data;

    /**
     * The size of the message input data.
     *
     *  If input_data is NULL this MUST be 0.
     */
    size_t input_size;

    /**
     * The optional hash of the code of the destination account.
     *  The null hash MUST be used when not specified.
     */
    struct evmc_uint256be code_hash;

    /** The amount of gas for message execution. */
    int64_t gas;

    /** The call depth. */
    int32_t depth;

    /** The kind of the call. For zero-depth calls ::EVMC_CALL SHOULD be used. */
    enum evmc_call_kind kind;

    /**
     * Additional flags modifying the call execution behavior.
     *  In the current version the only valid values are ::EVMC_STATIC or 0.
     */
    uint32_t flags;
};

Current Nim type for Messages https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/nimbus/vm_types.nim#L53-L89

Get devopment frameworks like Truffle to work with Nimbus

Now that we have a nearly feature-complete EVM implementation, our next goal is to start executing increasingly more complex code. It should be possible to use Nimbus with some of the existing ethereum development tools and frameworks in order to test and execute arbitrary smart contract code.

As an example of a very popular development framework, see Truffle:
https://medium.com/coinmonks/test-a-smart-contract-with-truffle-3eb8e1929370

Such tools usually interact with a locally running empty development chain. The work-flow is the following:

  1. Smart contract code is written and compiled to a local file (this is taken care by the framework)

  2. The contract is published to a locally running blockchain. Truffle uses JSON-RPC to talk to a locally running Ethereum client. By default, Truffle prefers to use its own Ethereum implementation (see truffle develop, but it should be possible to pair it with a local copy of Nimbus started with an empty custom chain. See this guide for pairing Truffle with Geth as an example).

  3. Transactions are created which execute methods of the contract. Again, this is done over JSON-RPC.

To support this work-flow, Nimbus will have to be enhanced with specific configuration options and the ability to work with multiple blockchains stored on disk. All required JSON-RPC calls employed by the development framework will have to be implemented. This issue is considered an "Epic", it should be broken down in smaller tasks once they are identified.

Remove unnecessary ref object

Py2nim created every types as a ref object because in Python everything is a ref.

As we don't need inheritance or reference semantics (a shared context for example) for several nimbus types, for those types heap allocations are an unnecessary cost.

[Implementation progress] Tracking passed VMtests

Tracking passed VMtests from the JSON fixtures.
https://github.com/status-im/nimbus/tree/master/tests/fixtures/VMTests

VMtests are cloned from official Ethereum VMTests

Working? - Test file
  • tests/fixtures/VMTests/vmTests/boolean.json
  • tests/fixtures/VMTests/vmTests/boolean.json
  • tests/fixtures/VMTests/vmTests/suicide.json
  • tests/fixtures/VMTests/vmTests/mktx.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhash257Block.json
  • tests/fixtures/VMTests/vmBlockInfoTest/gaslimit.json
  • tests/fixtures/VMTests/vmBlockInfoTest/number.json
  • tests/fixtures/VMTests/vmBlockInfoTest/difficulty.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhashInRange.json
  • tests/fixtures/VMTests/vmBlockInfoTest/coinbase.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhashOutOfRange.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhashMyBlock.json
  • tests/fixtures/VMTests/vmBlockInfoTest/timestamp.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhashNotExistingBlock.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhash258Block.json
  • tests/fixtures/VMTests/vmBlockInfoTest/blockhashUnderFlow.json
  • tests/fixtures/VMTests/vmBitwiseLogicOperation/slt1.json
  • tests/fixtures/VMTests/vmBitwiseLogicOperation/or2.json
  • tests/fixtures/VMTests/vmBitwiseLogicOperation/sgt3.json
  • tests/fixtures/VMTests/vmBitwiseLogicOperation/xor2.json

Tests & implementation of CallDataLoad and CodeCopy

Extracting discussion from https://github.com/status-im/nimbus/pull/59/files#r200313762

CallDataLoad

The current master implementation for calldataload is (from #59):

proc callDataLoad*(computation: var BaseComputation) =
  # Load call data into memory
  let origDataPos = computation.stack.popInt
  if origDataPos >= computation.msg.data.len:
    computation.stack.push(0)
    return

  let
    dataPos = origDataPos.toInt
    dataEndPosition = dataPos + 31

  if dataEndPosition < computation.msg.data.len:
    computation.stack.push(computation.msg.data[dataPos .. dataEndPosition])
  else:
    var bytes: array[32, byte]
    var presentBytes = min(computation.msg.data.len - dataPos, 32)

    if presentBytes > 0:
      copyMem(addr bytes[0], addr computation.msg.data[dataPos], presentBytes)
    else:
      presentBytes = 0

    for i in presentBytes ..< 32: bytes[i] = 0
      computation.stack.push(bytes)

With a potential simpler solution (from #65)

op callDataLoad, inline = false, startPos:
  ## 0x35, Get input data of current environment
  let start = startPos.toInt

  # If the data does not take 32 bytes, pad with zeros
  let endRange = min(computation.msg.data.len - 1, start + 31)
  let padding = start + 31 - endRange
  var value: array[32, byte] # We rely on value being initialized with 0 by default
  value[padding ..< 32] = computation.msg.data.toOpenArray(start, endRange)

  push: value # TODO, with the new implementation we can delete push for seq[byte]

CodeCopy

The current master implementation for codecopy is (from #59):

proc writePaddedResult(mem: var Memory,
                       data: openarray[byte],
                       memPos, dataPos, len: Natural,
                       paddingValue = 0.byte) =
  mem.extend(memPos, len)

  let dataEndPosition = dataPos + len - 1
  if dataEndPosition < data.len:
    mem.write(memPos, data[dataPos .. dataEndPosition])
  else:
    var presentElements = data.len - dataPos
    if presentElements > 0:
      mem.write(memPos, data.toOpenArray(dataPos, data.len - 1))
    else:
      presentElements = 0

    mem.writePaddingBytes(memPos + presentElements,
                          len - presentElements,
                          paddingValue)

proc codeCopy*(computation: var BaseComputation) =
  let (memStartPosition,
       codeStartPosition,
       size) = computation.stack.popInt(3)

  computation.gasMeter.consumeGas(
    computation.gasCosts[CodeCopy].d_handler(size),
    reason="CODECOPY: word gas cost")

  let (memPos, codePos, len) = (memStartPosition.toInt, codeStartPosition.toInt, size.toInt)

  computation.memory.writePaddedResult(computation.code.bytes, memPos, codePos, len)

With a simpler solution (from #65):

op codecopy, inline = false, memStartPos, copyStartPos, size:
  ## 0x39, Copy code running in current environment to memory.

  let (memPos, copyPos, len) = (memStartPos.toInt, copyStartPos.toInt, size.toInt)

  computation.gasMeter.consumeGas(
    computation.gasCosts[CodeCopy].m_handler(memPos, copyPos, len),
    reason="CodeCopy fee")

  computation.memory.extend(memPos, len)

  # If the data does not take 32 bytes, pad with zeros
  let lim = min(computation.code.bytes.len, copyPos + len)
  let padding = copyPos + len - lim
  # Note: when extending, extended memory is zero-ed, we only need to offset with padding value
  computation.memory.write(memPos):
    computation.code.bytes.toOpenArray(copyPos+padding, copyPos+lim)

Reference implementation

Here are the implementations in Py-EVM, Geth and Parity:

Py-EVM is doing something very complex, increasing a program counter in the "CodeStream" type. while Geth, Parity and the Yellow paper are just copying bytes?

Concerns

  1. The current implementation is complex, with 2 nested ifs, and the proc "writePaddingBytes" that is redundant with memory.extend (which pads with 0 already)

  2. The simpler codecopy has an off by one error (which see the simpler calldataload which does not suffer for this)

  3. codePos (callDataLoad) and copyPos (codeCopy) can be out of bounds. For copyPos this is handled in memory.write:

proc write*(memory: var Memory, startPos: Natural, value: openarray[byte]) =
  let size = value.len
  if size == 0:
    return
  #echo size
  #echo startPos
  #validateGte(startPos, 0)
  #validateGte(size, 0)
  validateLte(startPos + size, memory.len)
  let index = memory.len
  if memory.len < startPos + size:
    memory.bytes = memory.bytes.concat(repeat(0.byte, memory.len - (startPos + size))) # TODO: better logarithmic scaling?

  for z, b in value:
    memory.bytes[z + startPos] = b

but there is no handling for outofbounds read in codePos.

TODO

  • Add test cases to make sure the implementation chosen cover edge cases.
  • Simplify the current implementations

cc @zah

Post Mortem for {.experimental: "ForLoopMacros".}

Post Mortem for {.experimental: "ForLoopMacros".}

Before I start, this first post will focus on facts. All opinions part will be cleanly separated.

Also the feature is called ForLoopStmt but its flag is called ForLoopMacros.

Context

Starting from Friday August, 17 continuous integration (CI) of Nimbus started to fail.

Subsequently while trying fixes in decreasing order of desiredness, several discussions
were sparked within the team regarding reliance on unstable features and simplification of the code.

History:

  • 2018-04-15: Araq introduces for loop macro (ForLoopStmt) to Nim devel - nim-lang/Nim@c08efb4
  • 2018-06-17: mratsim uses ForLoopStmt in Stint - status-im/nim-stint#54
    • PR allowed compile-time evaluation of all uint
      • This enabled the use of const across all EVM constants
      • As a side benefit, func/noSideEffect to detect reliance on global/shared state is easier as global const
      • As a side benefit, significant code saving were achieved while improving significantly readability and maintainability, PR is +180/-336 on code size
        • Note that we can remove the ForLoopStmt, retain functionality and space-saving at the cost of internal ergonomics/readability
      • As a side-benefit, benchmark were improved by 15% reaching TTMath speed without assembly thanks to full compile-time evaluation and loop unrolling.
  • 2018-08-15: Araq gates all new features including ForLoopStmt behind the new {.experimental: "Flag".} pragma - status-im/nim-stint#54
    • Note that {.experimental: "notnil"} was also introduced
    • Activating the feature can be done with the following 2 ways:
      • Using {. experimental: "Foo" .} where the feature is used
      • Passing --experimental:Foo as a command-line (CLI) flag when compiling
  • 2018-08-17: coffeepots PR has issue on Mac and Windows but succeed on Linux - #108
  • 2018-08-17: coffepots raises issue upstream nim-lang/Nim#8676
    • mratsim detects the regression due to nim-lang/Nim@da41fc1
      • The change was announced on IRC and part of Nim changelog
      • Nimbus issue raised: #109
  • 2018-08-17: mratsim tries to implement a fix following the changelog instruction at the time
  • 2018-08-17: mratsim investigates in PR status-im/nim-stint#62 and finds that
    • the experimental pragma must be added at the call site
      not at the macro definition site - nim-lang/Nim#8676 (comment)
    • In stint all those macros are called via proc, so mratsim adds {.experimental: "ForLoopMacros".} everywhere: status-im/nim-stint@f0efc37
    • Result: still failure
    • Further investigation shows that for generic proc, you need the experimental pragma at call instantiation site
      due to generics early symbol resolution: nim-lang/Nim#8677 (meta issue raised during the investigation)
    • As a "final" solution PR status-im/nim-stint#62
      • only reverts status-im/nim-stint@2152937
      • and add --experimental:ForLoopMacros to the nim.cfg
      • This works locally and on Linux but fails on Mac and 1+ hour later on Appveyor.
  • 2018-08-20: mratsim further investigates in status-im/nim-stint#63 and #110
    • In CI nimbus/nim.cfg config leaks into nimbus/Nim/ compilation options including csources and koch
    • This impacts Mac and Windows but not Linux due to the use of Docker there.
    • Trying to use --skipProjCfg --skipParentCfg create another error - nim-lang/Nim#8691 (comment)
      - cd ..
      - bin\nim --skipProjCfg --skipParentCfg c koch # Don't use stint's nim.cfg to compile koch
      - koch boot --skipProjCfg --skipParentCfg -d:release
      - koch --skipProjCfg --skipParentCfg nimble
    • In the end, mratsim comments out the nim.cfg and add the flag in nimble test task in status-im/nim-stint#63
      • switch("experimental", "ForLoopMacros")
  • 2018-08-21: coffeepots realizes that Appveyor tests are still failing
    • mratsim traces back to the use of nimble install -y instead of nimble install -dy in the Appveyor config
    • This implies that nimble tries to compile the dependencies it just installed
    • Unfortunately it does not use the --experimental flag in that case and Appveyor fails
    • Fix 986cba5

Differences between each "solutions"

In code

Due to nim-lang/Nim#8677, {.experimental: "ForLoopStmt"} is needed everywhere a Stint comparison is used.

This is an artifact of how experimental, macro call and generics early symbol resolution interact. Without any one of those elements,
{.experimental: "ForLoopStmt"} can be contained in Stint and wouldn't contaminate packages that depends on it like Nimbus

In nim.cfg

This means that locally devs can use nim c foo.nim and have the flag automatically turn on.

All nimble tasks would also picked this nim.cfg automatically.

Unfortunately, all libraries that depend on Stint would need to ship a nim.cfg

Since for CI, Nim compiler is bootstrapped as a subfolder of the project, it gets compiled with the project nim.cfg.
The current csources do not support the --experimental switch and CI fails (except Travis + Docker).

Araq mentionned the following solution on 2018-08-21 that mratsim did not see before writing the postmortem - nim-lang/Nim#8691 (comment)

@if nimHasForLoopMacros:
--experimental: forLoopMacros
@end

In nimbus.nimble

There is no contamination to Nim compilation.

All nimble tasks must add the switch.

Locally all devs must have their own nim.cfg, use nimble tasks (or vscode tasks) or nim --experimental:ForLoopStmt c foo.nim

CI must use the hidden undocumented -d flag when installing dependencies.
After installing, nimble tries to compile the code to produce a binary (even for libraries),
since this is done without the --experimental flag either in the code or via a nim.cfg, dependency installation fails.

Implement dispatching to forked ops

1) Modify the current opcode table to include the following information

a) opcode number
b) opcode name
c) forks where the opcode was changed

For example, it can look like this:

opcodeTable:
  0x11 sdiv
  0x12 create [Tangerine, Spurious]

2) Introduce a forkedOp macro

This will be similar to the current op macro, but creating a proc with an additional static enum parameter called fork.

forkedOp create:
  when fork >= Tangerine:
    ...

3) Compile the VM instruction dispatching loop for each fork

This will consult the opcodeTable and it will place calls to the forked procs using the correct static parameters. The information in the table is enough to ensure that only the minimal number of instantiations are created (i.e. only 3 versions of create are needed - "Initial", "After Tangerine" and "After Spurious")

4) Make sure the block validation logic uses the correct dispatching loop depending on the block number

Support the Ultra Light Client (ULC) usage scenario

ULC will be the most lightweight client mode available in the Ethereum world before the introduction of Stateless clients. The premise is that block headers can be obtained from a trusted set of non-colluding servers (M our of N threshold is required) and then the LES protocol can be used to obtain specific state fragments and their corresponding merkle proofs. It is developed by our fellow Status contributors Boris Petrov (@b00ris) and Eugene Danilenko (@JekaMas).

Introduction:
https://www.youtube.com/watch?v=Z6UUT1TqdTs&t=0s&list=PLbrz7IuP1hrgIT5FK2emIMm410oLEFlXO&index=4

Some notes and plans published by Felfรถldi Zsolt:
https://gist.github.com/zsfelfoldi/bd618cbe0b529849abd692694ee11433
https://gist.githubusercontent.com/zsfelfoldi/7da2b802824569d2d159d5d06eeda14c/raw/faa3f18eb2d6f10aed84abbdb4c08a16f1c7a5c3/ulc.txt

An implementation branch based on go-ethereum:
https://github.com/status-im/go-ethereum/commits/enhancement/48/ulc_mode_rebase

Clang: Flexible array member with non-trivial destruction

Currently tests are failing for me on MacOS with Clang:

Error: execution of an external compiler program 'clang++ -c  -w -I/Users/<User>/.nimble/pkgs/ttmath-0.5.0  -I'/Users/<User>/.choosenim/toolchains/nim-#devel/lib' -o /Users/<User>/Programming/Status/nimbus/tests/nimcache/nimbus_stack_test.o /Users/<User>/Programming/Status/nimbus/tests/nimcache/nimbus_stack_test.cpp' failed with exit code: 1

/Users/<User>/Programming/Status/nimbus/tests/nimcache/nimbus_stack_test.cpp:242:29: error: flexible array member 'data' of type 'TY_fSgjjjotfhO8wSAxXj6bfw []' with non-trivial destruction
  TY_fSgjjjotfhO8wSAxXj6bfw data[SEQ_DECL_SIZE];

The offending line is:

struct tySequence_sp47cI5UpQ9bwXDfsT164pg : TGenericSeq {
  TY_fSgjjjotfhO8wSAxXj6bfw data[SEQ_DECL_SIZE];
};

It is used at several places in the generated C++ code, full output available here.

Edit: GCC-7 does not have the same issue

Gas: Disentangle gas computation from operator logic

Followup on #34

Currently operator logic, Ethereum forks specificities and gas costs are interleaved making maintenance, updates and having a global view hard.

Examples

Prerequisite installation with Nix fails on my PC

With a fresh Nix package manager install on Debian Testing, after a fresh git clone of this project.

Running this command:

nix-shell nimbus.nix

Results in this output:

fatal: not a tree object
error: program 'git' failed with exit code 128
(use '--show-trace' to show detailed location information)

Next, using this command:

nix-shell --show-trace nimbus.nix

Results in this output:

fatal: not a tree object
error: while evaluating the attribute 'LD_LIBRARY_PATH' of the derivation 'nimbus-0.0.1' at /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:175:11:
while evaluating 'makeSearchPathOutput' at /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/lib/strings.nix:94:42, called from /tmp/z/nimbus/nimbus.nix:23:24:
while evaluating 'makeSearchPath' at /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/lib/strings.nix:84:28, called from /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/lib/strings.nix:94:48:
while evaluating anonymous function at /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/lib/strings.nix:85:32, called from undefined position:
while evaluating the attribute 'src' of the derivation 'status-nim' at /nix/store/2psxsqmdarbg39783kzq48j0db1f98vl-nixpkgs-18.09pre149172.0ee6d7e3ab7/nixpkgs/pkgs/stdenv/generic/make-derivation.nix:175:11:
program 'git' failed with exit code 128

nix-shell version is 2.0.4

Tests failing with RLP + TTmath + Clang or Mingw64

But passes with GCC: see https://travis-ci.org/status-im/nimbus/builds/365142358

Error: execution of an external compiler program 'clang++ -c  -w -I/Users/travis/.nimble/pkgs/ttmath-#master  -I/Users/travis/build/status-im/nimbus/nim/lib -o /Users/travis/build/status-im/nimbus/nimcache/stdlib_system.o /Users/travis/build/status-im/nimbus/nimcache/stdlib_system.cpp' failed with exit code: 1
/Users/travis/build/status-im/nimbus/nimcache/rlp_writer.cpp:1273:2: error: no matching function for call to 'append_naEuvp5Ccas8eW7spRt2Wg'
        append_naEuvp5Ccas8eW7spRt2Wg(self, (*data).table, 4);
        ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/Users/travis/build/status-im/nimbus/nimcache/rlp_writer.cpp:1238:31: note: candidate function not viable: no known conversion from 'uint [4]' to 'NU *' (aka 'unsigned long long *') for 2nd argument
N_LIB_PRIVATE N_NIMCALL(void, append_naEuvp5Ccas8eW7spRt2Wg)(tyObject_RlpWriter_FzncMjzrGRTHJi822CxGrw& self, NU* list, NI listLen_0) {
                              ^
/Users/travis/build/status-im/nimbus/nim/lib/nimbase.h:243:44: note: expanded from macro 'N_NIMCALL'
#  define N_NIMCALL(rettype, name) rettype name /* no modifier */
                                           ^
1 error generated.

Interesting project

Very interesting development going on here. Kudos.

Does the project have any IRC channel (Slack, Gitter, ...) for development collaboration?

Review implementation of SDIV

Following #71 by @yglukhov, implementation of SDIV follows closely Parity implementation:

Context

instructions::SDIV => {
let (a, sign_a) = get_and_reset_sign(stack.pop_back());
let (b, sign_b) = get_and_reset_sign(stack.pop_back());

// -2^255
let min = (U256::one() << 255) - U256::one();
stack.push(if self.is_zero(&b) {
U256::zero()
} else if a == min && b == !U256::zero() {
min
} else {
let c = a.overflowing_div(b).0;
set_sign(c, sign_a ^ sign_b)
});
},

https://github.com/status-im/nimbus/blob/6c67115ef519a3467ba2aed973e881154d15b2f6/nimbus/vm/interpreter/opcodes_impl.nim#L45-L60

However Parity seems to handle the case of low(int256) incorrectly:

  1. It first get and reset the sign of operand
    low(Int256) = 0b1000_0000
    taking the inverse is flipping bit + 1
  • flipping: 0b0111_1111
  • +1: 0b1000_0000
    i.e. like 0, low(int) inversion in memory gives the same bit pattern (see Wikipedia Two-complement.
  1. Parity defines min = (U256::one() << 255) - U256::one(); but it should be min = (U256::one() << 255) only.

Various bit and hex representation:

import stint, strutils

echo "1 shl 7: " & (1'i8 shl 7).toBin(8)

let x = -128.int8
echo "x: " & x.toBin(8)

let y = (not cast[uint8](x)) + 1
echo "y: " & cast[int8](y).toBin(8)

################################################################
echo "\n 1 shl 255"
let a = one(UInt256) shl 255
echo a.toString(base = 2).align(256, padding = '0')
echo a.dumpHex

echo "\n 1 shl 255 - 1"
let b = a - 1
echo b.toString(base = 2).align(256, padding = '0')
echo b.dumpHex

echo "\n high(Int256)"
echo high(Int256).toString(base = 2).align(256, padding = '0')
echo high(Int256).dumpHex

echo "\n low(Int256)"
# echo low(Int256).toString(base = 2).align(256, padding = '0') # Throws underflow exception
echo low(Int256).dumpHex
1 shl 7: 10000000
x: 10000000
y: 10000000

1 shl 255
1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
8000000000000000000000000000000000000000000000000000000000000000

1 shl 255 - 1
0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

high(Int256)
0111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111
7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff

low(Int256)
8000000000000000000000000000000000000000000000000000000000000000

At minimm tests in test_opcodes are needed to complement EVM tests.

Yellow Paper

2018-07-18!UNITO-UNDERSCORE!11-34-19

Implement a test runner for the standard ETH blockchain/state validation tests

The test cases are located here:
https://github.com/ethereum/tests/tree/develop/BlockchainTests
https://github.com/ethereum/tests/tree/develop/GeneralStateTests

... and described here:
https://ethereum-tests.readthedocs.io/en/latest/test_types/blockchain_tests.html
https://ethereum-tests.readthedocs.io/en/latest/test_types/state_tests.html

We need to implement a simple test runner similar to the existing one in test_vm_json.nim. As these tests are significantly larger in size, it will probably be better if add the test suite repo as a git submodule (the CI scripts may have to be modified to fetch all submodules).

Please consider that in the future, these tests will be executable over the JSON-RPC interface using retesteth. The internal APIs can be structured to simplify the implementation of the JSON-RPC procs described here:

ethereum/interfaces#4
ethereum/retesteth#5

test_opcode: Incorrect results/crash in 32 bit

With GCC 32 and VCC 32 the result of the add test fails.

With GCC, the add gives an incorrect value:

    test_opcode.nim(56, 23): Check failed: c.stack.peek ==
    "115792089237316195423570985008687907853269984665640564039457584007913129639934".u256
    c.stack.peek was 680564733841876926926749214863536422910
    "115792089237316195423570985008687907853269984665640564039457584007913129639934".u256 was 115792089237316195423570985008687907853269984665640564039457584007913129639934
  [FAILED] add

However with VCC the result is a crash here:

Traceback (most recent call last)
test_opcode.nim(54)      test_opcode
computation.nim(278)     testCode
computation.nim(272)     :anonymous
stack_ops.nim(33)        push32
stack.nim(75)            push
utils_numeric.nim(25)    bigEndianToInt
SIGSEGV: Illegal storage access. (Attempt to read from nil?)

Interestingly, placing an echo bytes[24 - z * 8] in bigEndianToInt stops the crash (although the result of the add is still incorrect). The cpp suggests that bytes[24 - z * 8] is treated as an array with the echo and a local stack value without, which makes sense, and might explain the crash upon calling .addr.

I currently suspect there is an overflow issue in ttmath that is causing the incorrect addition when compiled to 32 bit architecture (in Windows), especially as smaller values add correctly in Nimbus and the test values here are a full complement of bits.

Add build and test instructions

Please add basic build/test/install and deploy/usage instructions to README, so people can try out and understand better the implementation.

Multiple RLP/Stint (?) linked issues in test_vm_json.nim

To be investigated, many tests but not all fails at rlp toBytes proc even simple byte operations like equality, xor, and, compare:

  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/or2.json
vmBitwiseLogicOperationsgt3.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/sgt3.json
vmBitwiseLogicOperationxor2.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/xor2.json
vmBitwiseLogicOperationbyte4.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte4.json
vmBitwiseLogicOperationnot5.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/not5.json
vmBitwiseLogicOperationeq2.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/eq2.json
vmBitwiseLogicOperationbyte8.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte8.json
vmBitwiseLogicOperationand0.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/and0.json
vmBitwiseLogicOperationand1.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/and1.json
vmBitwiseLogicOperationbyte9.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte9.json
vmBitwiseLogicOperationnot4.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/not4.json
vmBitwiseLogicOperationbyte5.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte5.json
vmBitwiseLogicOperationxor3.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/xor3.json
vmBitwiseLogicOperationsgt2.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/sgt2.json
vmBitwiseLogicOperationor3.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/or3.json
vmBitwiseLogicOperationslt0.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/slt0.json
vmBitwiseLogicOperationnot3.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/not3.json
vmBitwiseLogicOperationlt0.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/lt0.json
vmBitwiseLogicOperationgt2.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/gt2.json
vmBitwiseLogicOperationor4.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/or4.json
vmBitwiseLogicOperationxor4.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/xor4.json
vmBitwiseLogicOperationbyte2.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte2.json
vmBitwiseLogicOperationbyte3.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte3.json
vmBitwiseLogicOperationxor5.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/xor5.json
vmBitwiseLogicOperationsgt4.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/sgt4.json
vmBitwiseLogicOperationor5.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/or5.json
vmBitwiseLogicOperationgt3.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/gt3.json
vmBitwiseLogicOperationlt1.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/lt1.json
vmBitwiseLogicOperationnot2.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/not2.json
vmBitwiseLogicOperationand4.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/and4.json
vmBitwiseLogicOperationgt0.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/gt0.json
vmBitwiseLogicOperationiszero0.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/iszero0.json
vmBitwiseLogicOperationlt2.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/lt2.json
vmBitwiseLogicOperationnot1.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/not1.json
vmBitwiseLogicOperationbyte0.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte0.json
vmBitwiseLogicOperationslt4.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/slt4.json
vmBitwiseLogicOperationbyte1.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte1.json
vmBitwiseLogicOperationnot0.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/not0.json
vmBitwiseLogicOperationlt3.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/lt3.json
vmBitwiseLogicOperationiszero1.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/iszero1.json
vmBitwiseLogicOperationgt1.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/gt1.json
vmBitwiseLogicOperationand5.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/and5.json
vmBitwiseLogicOperationbyte6.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte6.json
vmBitwiseLogicOperationxor0.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/xor0.json
vmBitwiseLogicOperationsgt1.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/sgt1.json
vmBitwiseLogicOperationor0.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/or0.json
vmBitwiseLogicOperationslt3.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/slt3.json
vmBitwiseLogicOperationand2.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/and2.json
vmBitwiseLogicOperationbyte11.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/byte11.json
vmBitwiseLogicOperationiszeo2.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/iszeo2.json
vmBitwiseLogicOperationeq0.json
  [OK] tests/fixtures/VMTests/vmBitwiseLogicOperation/eq0.json
vmBitwiseLogicOperationeq1.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
storage.nim(40)          sstore
rlp.nim(360)             setStorage
rlp.nim(306)             read
rlp.nim(209)             toBytes

    Unhandled exception:
  [FAILED] tests/fixtures/VMTests/vmBitwiseLogicOperation/eq1.json
vmBitwiseLogicOperationbyte10.json

This used to work in with ttmath so there is probably an encode issue with Stint and RLP: 9fc80cb#diff-7ee8b9b9175bc33fa229c107c363a936 cc @zah

Implement transaction rollback

We require a 'snapshot' ability to rollback the VM state, for instance if there is an error.

Currently this is a commented stub defined in nimbus\db\db_chain.nim.

For example, Py-Evm describes it's snapshot as:

Snapshots are a combination of the state_root at the time of the snapshot and the id of the changeset from the journaled DB.

Each time a recording is started, the underlying journal creates a new changeset and assigns an id to it. The journal then keeps track of all changes that go into this changeset.

Discarding a changeset simply throws it away inculding all subsequent changesets that may have followed. Commiting a changeset merges the given changeset and all subsequent changesets into the previous changeset giving precidence to later changesets in case of conflicting keys.

Nothing is written to the underlying db until persist() is called.

Some example implementation references:

Stack/MessageOptions not converting hex integers to Uint256

The message option type is not converting hex string to Uint256:

https://github.com/status-im/nimbus/blob/43797485e5c50d94fef5a4b9afba858632190896/src/vm/message.nim#L42-L48

This leads to several test failures

vmTestsboolean.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681
  [FAILED] tests/fixtures/VMTests/vmTests/boolean.json
vmTestssuicide.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681
  [FAILED] tests/fixtures/VMTests/vmTests/suicide.json
vmTestsmktx.json
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
context.nim(27)          caller
stack.nim(67)            push
validation.nim(48)       validateStackItem

    Unhandled exception: Invalid stack item: expected 32 bytes, got 42: value is 0xcd1722f3947def4cf144679da39c4c32bdc35681

https://github.com/status-im/nimbus/blob/43797485e5c50d94fef5a4b9afba858632190896/tests/fixtures/VMTests/vmTests/boolean.json#L17-L26
I didn't find any mention of MessageOptions type in Py-EVM, Go-Ethereum, Parity codebases even in past commits besides Go Protobuf.

Implement the EVMC interface in Nimbus

We can consider this complete once we are able to link Nimbus into Aleth (formerly cpp-ethereum) or Geth and libaleth or evmjit into Nimbus.

Potential use cases:

  1. Compare the performance of Nimbus against the other VMs.

  2. Test the blockchain sync algorithm with a VM that's known to work correctly.

  3. Test our VM code with a sync algorithm that is known to be correct.

As an unusual bonus feature, we can add support in Nimbus for running multiple EVMC implementations in parallel and verifying that they arrived at the same result. Something similar is recommended as a "best practice" for running public Ethereum nodes. To guard against potential security exploits, you would run two separate implementations in parallel and verify that they reach the same state after each block.

Boolean opcodes do not compile on latest devel

The current boolean implementation relies on 0 == false in a corner macro case that was fixed in devel.

Consequently, Nimbus does not compile on latest devel

.../nimbus/nimbus/vm/interpreter/opcodes_impl/comparison.nim(11, 13) template/generic instantiation from here
.../nimbus/nimbus/vm/interpreter/opcodes_impl/helpers.nim(33, 12) template/generic instantiation from here
lib/system.nim(414, 10) Error: type mismatch: got <bool, int literal(0)>
but expected one of:
proc `==`[Enum: enum](x, y: Enum): bool
  first type mismatch at position: 1
  required type: Enum: enum
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: pointer): bool
  first type mismatch at position: 1
  required type: pointer
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: string): bool
  first type mismatch at position: 1
  required type: string
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: char): bool
  first type mismatch at position: 1
  required type: char
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: bool): bool
  first type mismatch at position: 2
  required type: bool
  but expression '0' is of type: int literal(0)
proc `==`[T](x, y: set[T]): bool
  first type mismatch at position: 1
  required type: set[T]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T](x, y: ref T): bool
  first type mismatch at position: 1
  required type: ref T
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T](x, y: ptr T): bool
  first type mismatch at position: 1
  required type: ptr T
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T: proc](x, y: T): bool
  first type mismatch at position: 1
  required type: T: proc
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: int16): bool
  first type mismatch at position: 1
  required type: int16
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: int64): bool
  first type mismatch at position: 1
  required type: int64
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a343728: StUint[bits]; b343730: int{lit}): bool
  first type mismatch at position: 1
  required type: StUint[==.bits]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T: SomeUnsignedInt](x, y: T): bool
  first type mismatch at position: 1
  required type: T: SomeUnsignedInt
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: int8): bool
  first type mismatch at position: 1
  required type: int8
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: float32): bool
  first type mismatch at position: 1
  required type: float32
  but expression '<(leftSigned, rightSigned)' is of type: bool
func `==`(x329224, y329226: StUint): bool
  first type mismatch at position: 1
  required type: StUint
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: float): bool
  first type mismatch at position: 1
  required type: float
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: int32): bool
  first type mismatch at position: 1
  required type: int32
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: cstring): bool
  first type mismatch at position: 1
  required type: cstring
  but expression '<(leftSigned, rightSigned)' is of type: bool
func `==`(x, y: IntImpl): bool
  first type mismatch at position: 1
  required type: IntImpl
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a2532798: SomeInteger;
                           b2532800: StInt[bits]): bool
  first type mismatch at position: 1
  required type: SomeInteger
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a2532794: StInt[bits]; b2532796: SomeInteger): bool
  first type mismatch at position: 1
  required type: StInt[==.bits]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T](x, y: seq[T]): bool
  first type mismatch at position: 1
  required type: seq[T]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a343736: StInt[bits]; b343738: int{lit}): bool
  first type mismatch at position: 1
  required type: StInt[==.bits]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(x, y: int): bool
  first type mismatch at position: 1
  required type: int
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[T: tuple |
    object](x, y: T): bool
  first type mismatch at position: 1
  required type: T: tuple or object
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[I, T](x, y: array[I, T]): bool
  first type mismatch at position: 1
  required type: array[I, T]
  but expression '<(leftSigned, rightSigned)' is of type: bool
func `==`(x339224, y339226: StInt): bool
  first type mismatch at position: 1
  required type: StInt
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a2532786: StUint[bits]; b2532788: SomeInteger): bool
  first type mismatch at position: 1
  required type: StUint[==.bits]
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(a, b: NimIdent): bool
  first type mismatch at position: 1
  required type: NimIdent
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(a, b: NimSym): bool
  first type mismatch at position: 1
  required type: NimSym
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a343740: int{lit};
                           b343742: StInt[bits]): bool
  first type mismatch at position: 1
  required type: int
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a2532790: SomeInteger;
                           b2532792: StUint[bits]): bool
  first type mismatch at position: 1
  required type: SomeInteger
  but expression '<(leftSigned, rightSigned)' is of type: bool
func `==`(x, y: UintImpl): bool
  first type mismatch at position: 1
  required type: UintImpl
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`[bits: static[int]](a343732: int{lit};
                           b343734: StUint[bits]): bool
  first type mismatch at position: 1
  required type: int
  but expression '<(leftSigned, rightSigned)' is of type: bool
proc `==`(a, b: NimNode): bool
  first type mismatch at position: 1
  required type: NimNode
  but expression '<(leftSigned, rightSigned)' is of type: bool

expression: <(leftSigned, rightSigned) == 0

https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/nimbus/vm/interpreter/opcodes_impl/helpers.nim#L13-L39

Gas computation: use uint64 instead of uint256

Using uint64 instead of uint256 for gas computation like in Go-Ethereum would be much better for devices from a memory and performance standpoint.

See Go-Ethereum PR - ethereum/go-ethereum#3674.

Furthermore, gas prices can then be stored in a const array and replaced at instantiation site at compile-time instead of being loaded into memory.

This can be tested with

const a = [1111, 2222, 3333, 4444]
echo a[2]

and checking the generated C code.

Test suite: VMTests are only for Homestead, blockNumber-fork mapping should be ignored

Currently we are testing the gas ending balance module 15000 if there was an SSTORE operation involved.

https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/tests/test_vm_json.nim#L89-L92

This was probably a workaround (in Py-EVM or Nimbus) as Py-EVM does not do it currently: https://github.com/ethereum/py-evm/blob/6708b9eedde39b3b8d4c0db57a7bd2b1770c72d9/tests/json-fixtures/test_virtual_machine.py#L236

Example test that should fail: expXY_success.json

https://github.com/status-im/nimbus/blob/6f28d1186675f648e0587f44e730b9dc23291bab/tests/fixtures/VMTests/vmArithmeticTest/expXY_success.json#L1-L54

"currentNumber" : "0x00": this is the block number

"gas" : "0x9bad" = 39853 should be left over after execution but 84853 are left currently and Nimbus passes the test.

Note that the blockNumber for this test is 0x00 but it seems to imply latest block:

  • Sha3 uses the post-EIP150 computation scheme (costs gas 20 instead of 10)
  • SSTORE is supposed to cost 20000 instead of 5000 in this test (== post EIP-150 cost as well)
vmArithmeticTestexpXY_success.json
1 Push1 0x00
2 CallDataLoad
4 Push1 0x00
5 Sstore
7 Push1 0x20
8 CallDataLoad
10 Push1 0x01
11 Sstore
13 Push1 0x01
14 Sload
16 Push1 0x00
17 Sload
18 Exp
20 Push1 0x02
21 Sstore
-1 Stop
#vm.computation.BaseComputation: COMPUTATION STARTING: gas: 100000 | from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000
000000000000 | depth: 0 | static: n
#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 0
#gas: GAS CONSUMPTION: 100000 - 3 -> 99997 (Push1)
#vm.computation.BaseComputation: Stack:
  0


#vm.computation.BaseComputation: OPCODE: 0x35 (CallDataLoad) | pc: 2
#gas: GAS CONSUMPTION: 99997 - 3 -> 99994 (CallDataLoad)
#vm.computation.BaseComputation: Stack:
  21923370962747092222049241656982978670225693601166209560647359695041422438448


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 3
#gas: GAS CONSUMPTION: 99994 - 3 -> 99991 (Push1)
#vm.computation.BaseComputation: Stack:
  21923370962747092222049241656982978670225693601166209560647359695041422438448
  0


#vm.computation.BaseComputation: OPCODE: 0x55 (Sstore) | pc: 5
#gas: GAS CONSUMPTION: 99991 - 5000 -> 94991 (SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 2192337096274709222204924
1656982978670225693601166209560647359695041422438448 (0))
#vm.computation.BaseComputation: Stack:



#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 6
#gas: GAS CONSUMPTION: 94991 - 3 -> 94988 (Push1)
#vm.computation.BaseComputation: Stack:
  32


#vm.computation.BaseComputation: OPCODE: 0x35 (CallDataLoad) | pc: 8
#gas: GAS CONSUMPTION: 94988 - 3 -> 94985 (CallDataLoad)
#vm.computation.BaseComputation: Stack:
  21796157974083048550319244236929488537086114760591164995662604048548353814576


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 9
#gas: GAS CONSUMPTION: 94985 - 3 -> 94982 (Push1)
#vm.computation.BaseComputation: Stack:
  21796157974083048550319244236929488537086114760591164995662604048548353814576
  1


#vm.computation.BaseComputation: OPCODE: 0x55 (Sstore) | pc: 11
#gas: GAS CONSUMPTION: 94982 - 5000 -> 89982 (SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 21796157974083048550319244236929488537086114760591164995662604048548353814576 (0))
#vm.computation.BaseComputation: Stack:



#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 12
#gas: GAS CONSUMPTION: 89982 - 3 -> 89979 (Push1)
#vm.computation.BaseComputation: Stack:
  1


#vm.computation.BaseComputation: OPCODE: 0x54 (Sload) | pc: 14
#gas: GAS CONSUMPTION: 89979 - 50 -> 89929 (Sload)
#vm.computation.BaseComputation: Stack:
  2


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 15
#gas: GAS CONSUMPTION: 89929 - 3 -> 89926 (Push1)
#vm.computation.BaseComputation: Stack:
  2
  0


#vm.computation.BaseComputation: OPCODE: 0x54 (Sload) | pc: 17
#gas: GAS CONSUMPTION: 89926 - 50 -> 89876 (Sload)
#vm.computation.BaseComputation: Stack:
  2
  2


#vm.computation.BaseComputation: OPCODE: 0x0A (Exp) | pc: 18
#gas: GAS CONSUMPTION: 89876 - 20 -> 89856 (EXP: exponent bytes)
#vm.computation.BaseComputation: Stack:
  4


#vm.computation.BaseComputation: OPCODE: 0x60 (Push1) | pc: 19
#gas: GAS CONSUMPTION: 89856 - 3 -> 89853 (Push1)
#vm.computation.BaseComputation: Stack:
  4
  2


#vm.computation.BaseComputation: OPCODE: 0x55 (Sstore) | pc: 21
#gas: GAS CONSUMPTION: 89853 - 5000 -> 84853 (SSTORE: [15, 87, 46, 82, 149, 197, 127, 21, 136, 111, 155, 38, 62, 47, 109, 45, 108, 123, 94, 198][slot] -> 4 (0))
#vm.computation.BaseComputation: Stack:



#vm.computation.BaseComputation: COMPUTATION SUCCESS: from: cd1722f2947def4cf144679da39c4c32bdc35681 | to: 0f572e5295c57f15886f9b263e2f6d2d6c7b5ec6 | value: 1000000000000000000 | depth: 0 | static: n | gas-used: 15147 | gas-remaining: 84853
  [OK] tests/fixtures/VMTests/vmArithmeticTest/expXY_success.json

Stack popping issue: Index out of bounds

In some 2 specific cases:

  • fixture tests/fixtures/VMTests/vmArithmeticTest/mulUnderFlow.json
  • fixture tests/fixtures/VMTests/vmArithmeticTest/sdiv_dejavu.json

popping the stack eads to an out-of-bounds exception.

Traceback

test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
stack.nim(135)           mul
stack.nim(116)           internalPopTuple2
system.nim(2445)         pop
system.nim(2843)         sysFatal

    Unhandled exception: index out of bounds
test_helpers.nim(48)     test_vm_json
computation.nim(278)     testFixture
computation.nim(272)     :anonymous
stack.nim(135)           sdiv
stack.nim(116)           internalPopTuple2
system.nim(2445)         pop
system.nim(2843)         sysFatal

    Unhandled exception: index out of bounds

Support gas prices pre-EIP 150

Some tests are failing because the environment is pre-EIP 150 which increased a lot of gas prices.

For example SLOAD opcode changed from 50 to 200 and impacts this test:

Decompiling the code yields

import ../src/vm/code_stream, ../src/opcode_values, strformat

var c = newCodeStreamFromUnescaped("0x6000356000556020356001556001546000540a600255")

let opcodes = c.decompile()
for op in opcodes:
  echo &"[{op[0]}]\t{op[1]}\t{op[2]}"

# [1]     PUSH1   0x00
# [2]     CALLDATALOAD
# [4]     PUSH1   0x00
# [5]     SSTORE
# [7]     PUSH1   0x20
# [8]     CALLDATALOAD
# [10]    PUSH1   0x01
# [11]    SSTORE
# [13]    PUSH1   0x01
# [14]    SLOAD
# [16]    PUSH1   0x00
# [17]    SLOAD
# [18]    EXP
# [20]    PUSH1   0x02
# [21]    SSTORE
# [-1]    STOP

Expected gas balance is 39853, current resulting gas balance was 39553

Other almost certainly impacted (gas not matching in logs) includes:

  • expXY
  • blockhash257Block
  • blockhashInRange
  • coinbase
  • blockhashOutOfRange
  • blockhashMyBlock
  • blockhashNotExistingBlock
  • blockhash258Block
  • smod5 (out of gas error)
  • smod7 (out of gas error)
  • signextend_Overflow_dj42

Implement getBlockBody

Implementation required for getBlockBody.
This routine fetches the BlockBody from the Chain by it's hash.

There are currently two version to satisfy (there is also an AbstractChainDb version that should remain as raising an error):

  • method getBlockBody*(c: Chain, blockHash: KeccakHash): BlockBodyRef in nimbus\p2p\chain.nim
  • proc getBlockBody*(self: BaseChainDB, h: Hash256, output: var BlockBody): bool in nimbus\db\db_chain

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.