Coder Social home page Coder Social logo

ethereum / evmone Goto Github PK

View Code? Open in Web Editor NEW
808.0 34.0 268.0 5.24 MB

Fast Ethereum Virtual Machine implementation

License: Apache License 2.0

CMake 5.64% Shell 0.01% C++ 94.19% C 0.02% Python 0.10% Dockerfile 0.03%
ethereum ethereum-virtual-machine evmc evm blockchain hacktoberfest

evmone's Introduction

evmone

ethereum badge readme style standard badge codecov badge circleci badge appveyor badge license badge

Fast Ethereum Virtual Machine implementation

evmone is a C++ implementation of the Ethereum Virtual Machine (EVM). Created by members of the Ipsilon (ex-Ewasm) team, the project aims for clean, standalone EVM implementation that can be imported as an execution module by Ethereum Client projects. The codebase of evmone is optimized to provide fast and efficient execution of EVM smart contracts.

Characteristic of evmone

  1. Exposes the EVMC API.
  2. Requires C++20 standard.
  3. The intx library is used to provide 256-bit integer precision.
  4. The ethash library is used to provide Keccak hash function implementation needed for the special KECCAK256 instruction.
  5. Contains two interpreters:
    • Baseline (default)
    • Advanced (select with the advanced option)

Baseline Interpreter

  1. Provides relatively straight-forward but efficient EVM implementation.
  2. Performs only minimalistic JUMPDEST analysis.

Advanced Interpreter

  1. The indirect call threading is the dispatch method used - a loaded EVM program is a table with pointers to functions implementing virtual instructions.
  2. The gas cost and stack requirements of block of instructions is precomputed and applied once per block during execution.
  3. Performs extensive and expensive bytecode analysis before execution.

Usage

As geth plugin

evmone implements the EVMC API for Ethereum Virtual Machines. It can be used as a plugin replacing geth's internal EVM. But for that a modified version of geth is needed. The Ewasm's fork of go-ethereum provides binary releases of geth with EVMC support.

Next, download evmone from Releases.

Start the downloaded geth with --vm.evm option pointing to the evmone shared library.

geth --vm.evm=./libevmone.so

Building from source

To build the evmone EVMC module (shared library), test, and benchmark:

  1. Fetch the source code:

    git clone --recursive https://github.com/ethereum/evmone
    cd evmone
    
  2. Configure the project build and dependencies:

    Linux / OSX
    cmake -S . -B build -DEVMONE_TESTING=ON
    
    Windows
    cmake -S . -B build -DEVMONE_TESTING=ON -G "Visual Studio 16 2019" -A x64
    
  3. Build:

    cmake --build build --parallel
    
  4. Run the unit tests or benchmarking tool:

    build/bin/evmone-unittests
    build/bin/evmone-bench test/evm-benchmarks/benchmarks
    

Precompiles

Ethereum Precompiled Contracts (precompiles for short) are only partly supported by evmone.

However, there are options to enable limited precompiles support for testing.

  1. For precompiles with missing implementation stubs are enabled by default. They will correctly respond to known inputs.
  2. The CMake option EVMONE_PRECOMPILES_SILKPRE=1 enables building of the silkpre third party library with the implementation of the precompiles. This library also requires GMP (e.g. libgmp-dev) library for building and execution.

Tools

evm-test

The evm-test executes a collection of unit tests on any EVMC-compatible Ethereum Virtual Machine implementation. The collection of tests comes from the evmone project.

evm-test ./evmone.so

Docker

Docker images with evmone are available on Docker Hub: https://hub.docker.com/r/ethereum/evmone.

Having the evmone shared library inside a docker is not very useful on its own, but the image can be used as the base of another one or you can run benchmarks with it.

docker run --entrypoint evmone-bench ethereum/evmone /src/test/benchmarks

EVM Object Format (EOF) support

evmone supports EOFv1. Since EOF validation is done once during deploy-time, evmone does not revalidate during execution of bytecode. To force EOF revalidation, you can use the validate_eof option, example:

evmc run --vm libevmone.so,validate_eof --rev 13 "EF00"

References

  1. Efficient gas calculation algorithm for EVM

Maintainer

Paweł Bylica @chfast

License

license badge

Licensed under the Apache License, Version 2.0.

evmone's People

Contributors

andrealanfranchi avatar axic avatar battlmonstr avatar cdetrio avatar chfast avatar gumb0 avatar gzanitti avatar halfalicious avatar hrishikeshsuresh avatar hugo-dc avatar jszymanskijs avatar miles170 avatar pdobacz avatar radrow avatar rex4539 avatar rodiazet avatar sandakersmann avatar vorot93 avatar yperbasis 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

evmone's Issues

Non-deterministically get EVMC_STATIC_MODE_VIOLATION

Hello,

I'm using evmone commit hash beginning with f25d9c7

My test code is the following

	message.kind = EVMC_CALL;
	// FIXME: We call a function in one contract that calls a function in another.
	// So, call depth = 2?
	message.depth = 2;
	// Call value is zero
	message.value = {0};
	message.gas = std::numeric_limits<int64_t>::max();
	message.input_data = input.data();
	message.input_size = input.size();

        auto result = evmone.execute(
		hostContext,
		hostContext.getRevision(),
		message,
		byteCode.data(),
		byteCode.size()
	);

input is:

input.data(): [f8,a8,fd,6d]
input.size(): 4

The host context implementation is here: https://github.com/ethereum/solidity/blob/1e106a8bdc0b7a47438275ef25205f1cf9578808/test/EVMHost.cpp

byteCode is:

608060405234801561001057600080fd5b506004361061002b5760003560e01c8063f8a8fd6d14610030575b600080fd5b61003861004e565b604051610045919061011b565b60405180910390f35b60008060405161005d906100f5565b604051809103906000f080158015610079573d6000803e3d6000fd5b509050806001600160a01b031663f8a8fd6d6040518163ffffffff1660e01b8152600401602060405180830381600087803b1580156100b757600080fd5b505af11580156100cb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506100ef9190810190610102565b91505090565b61021f8061012583390190565b60006020828403121561011457600080fd5b5051919050565b9081526020019056fe608060405234801561001057600080fd5b506101ff806100206000396000f3fe608060405234801561001057600080fd5b50600436106100415760003560e01c806384575abd14610046578063eae8b1a114610046578063f8a8fd6d14610064575b600080fd5b61004e61006c565b60405161005b9190610189565b60405180910390f35b61004e610072565b60005b90565b600080306001600160a01b03166384575abd6040518163ffffffff1660e01b815260040160206040518083038186803b1580156100ae57600080fd5b505afa1580156100c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506100e69190810190610170565b905080156100f557905061006f565b306001600160a01b031663eae8b1a16040518163ffffffff1660e01b815260040160206040518083038186803b15801561012e57600080fd5b505afa158015610142573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052506101669190810190610170565b6103e80191505090565b60006020828403121561018257600080fd5b5051919050565b9081526020019056fea365627a7a7231582048720e2880554d9b8bf6a3525e19a4a440330322422d8133169c336ad826ecd96c6578706572696d656e74616cf564736f6c63782c302e352e31312d646576656c6f702e323031392e372e31302b636f6d6d69742e63323731343334632e6d6f64006aa365627a7a723158206363888328953de10fa5f2b7086b7b95e523a69ca7542779188ef005b0f6e94a6c6578706572696d656e74616cf564736f6c63782c302e352e31312d646576656c6f702e323031392e372e31302b636f6d6d69742e63323731343334632e6d6f64006a

The issue is not reproducible on every run as the title suggests, but definitely one in five calls results in the following return code EVMC_STATIC_MODE_VIOLATION (11). Otherwise I get a success return code EVMC_SUCCESS.

Please let me know if you need more info to triage this. Thank you.

Error when building Ethash during generation of Visual Studio 2019 solution ("warning C4211: nonstandard extension used: redefined extern to static")

I'm trying to build EVMOne on my Windows machine (running Windows 10.0.18362.418 and Visual Studio 2019 - compiler version Microsoft (R) Build Engine version 16.3.0-preview-19377-01+dd8019d9e) and I'm hitting a build error when trying to generate the VS 2019 solution via the following command:

cmake -DEVMONE_TESTING=ON .. -G "Visual Studio 16 2019" -A "x64"

Relevant build output:

C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(25,1): error C2220: the following warning is treated as an error [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]
C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(25,1): warning C4211: nonstandard extension used: redefined extern to static [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]
    managed.cpp
    progpow.cpp
C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(35,1): warning C4211: nonstandard extension used: redefined extern to static [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]
C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(25,1): error C2220: the following warning is treated as an error [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]
C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(25,1): warning C4211: nonstandard extension used: redefined extern to static [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]
    Generating Code...
C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Source\lib\ethash\builtins.h(35,1): warning C4211: nonstandard extension used: redefined extern to static [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release-prefix\src\ethash-Release-build\lib\ethash\ethash.vcxproj] [C:\.hunter\_Base\bc7da32\e71809d\792dc56\Build\ethash\Build\ethash-Release.vcxproj]

[hunter ** FATAL ERROR **] Build step failed (dir: C:/.hunter/_Base/bc7da32/e71809d/792dc56/Build/ethash
[hunter ** FATAL ERROR **] [Directory:C:/.hunter/_Base/Download/Hunter/0.23.190/bc7da32/Unpacked/cmake/projects/ethash]

It looks like the ETHash library redefines an extern to a static which is not allowed in ANSI compatibility mode (/Za) but allowed with the default extensions (/Ze) - Microsoft Documentation

I used to be able to build EVMOne on Windows so I suspect that either the inclusion of EThash in EVMOne is new, ETHash was recently updated, or my version of VS was updated with a new compiler which broke building ETHash.

I took a quick look at the EThash GitHub repo and it hasn't been updated in years so I'm assuming that either this issue has always existed in the EThash source and EThash was only recently included in EVMone, or my compiler was updated and contains a bug.

op_exp needs to have a shortcut for 2^e

There are a lot of contracts on the Ethereum mainnet that were compiled with Solidity prior to the shifting operations were available. For those, exponentiation 2^160 is used to emulate 1<<160. Measurements performed on turbo-geth confirm this. Since we are planning to integrate evmone into turbo-geth, and we have already implemented this shortcut (ledgerwatch/erigon#505), it would be good to maintain that in evmone.

Segment fault running contracts generated by ZoKrates

Hi there,

So I have the following Solidity code:

// This file is LGPL3 Licensed

/**
 * @title Elliptic curve operations on twist points for alt_bn128
 * @author Mustafa Al-Bassam ([email protected])
 * @dev Homepage: https://github.com/musalbas/solidity-BN256G2
 */

library BN256G2 {
    uint256 internal constant FIELD_MODULUS = 0x30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47;
    uint256 internal constant TWISTBX = 0x2b149d40ceb8aaae81be18991be06ac3b5b4c5e559dbefa33267e6dc24a138e5;
    uint256 internal constant TWISTBY = 0x9713b03af0fed4cd2cafadeed8fdf4a74fa084e52d1852e4a2bd0685c315d2;
    uint internal constant PTXX = 0;
    uint internal constant PTXY = 1;
    uint internal constant PTYX = 2;
    uint internal constant PTYY = 3;
    uint internal constant PTZX = 4;
    uint internal constant PTZY = 5;

    /**
     * @notice Add two twist points
     * @param pt1xx Coefficient 1 of x on point 1
     * @param pt1xy Coefficient 2 of x on point 1
     * @param pt1yx Coefficient 1 of y on point 1
     * @param pt1yy Coefficient 2 of y on point 1
     * @param pt2xx Coefficient 1 of x on point 2
     * @param pt2xy Coefficient 2 of x on point 2
     * @param pt2yx Coefficient 1 of y on point 2
     * @param pt2yy Coefficient 2 of y on point 2
     * @return (pt3xx, pt3xy, pt3yx, pt3yy)
     */
    function ECTwistAdd(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy
    ) public view returns (
        uint256, uint256,
        uint256, uint256
    ) {
        if (
            pt1xx == 0 && pt1xy == 0 &&
            pt1yx == 0 && pt1yy == 0
        ) {
            if (!(
                pt2xx == 0 && pt2xy == 0 &&
                pt2yx == 0 && pt2yy == 0
            )) {
                assert(_isOnCurve(
                    pt2xx, pt2xy,
                    pt2yx, pt2yy
                ));
            }
            return (
                pt2xx, pt2xy,
                pt2yx, pt2yy
            );
        } else if (
            pt2xx == 0 && pt2xy == 0 &&
            pt2yx == 0 && pt2yy == 0
        ) {
            assert(_isOnCurve(
                pt1xx, pt1xy,
                pt1yx, pt1yy
            ));
            return (
                pt1xx, pt1xy,
                pt1yx, pt1yy
            );
        }

        assert(_isOnCurve(
            pt1xx, pt1xy,
            pt1yx, pt1yy
        ));
        assert(_isOnCurve(
            pt2xx, pt2xy,
            pt2yx, pt2yy
        ));

        uint256[6] memory pt3 = _ECTwistAddJacobian(
            pt1xx, pt1xy,
            pt1yx, pt1yy,
            1,     0,
            pt2xx, pt2xy,
            pt2yx, pt2yy,
            1,     0
        );

        return _fromJacobian(
            pt3[PTXX], pt3[PTXY],
            pt3[PTYX], pt3[PTYY],
            pt3[PTZX], pt3[PTZY]
        );
    }

    /**
     * @notice Multiply a twist point by a scalar
     * @param s     Scalar to multiply by
     * @param pt1xx Coefficient 1 of x
     * @param pt1xy Coefficient 2 of x
     * @param pt1yx Coefficient 1 of y
     * @param pt1yy Coefficient 2 of y
     * @return (pt2xx, pt2xy, pt2yx, pt2yy)
     */
    function ECTwistMul(
        uint256 s,
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy
    ) public view returns (
        uint256, uint256,
        uint256, uint256
    ) {
        uint256 pt1zx = 1;
        if (
            pt1xx == 0 && pt1xy == 0 &&
            pt1yx == 0 && pt1yy == 0
        ) {
            pt1xx = 1;
            pt1yx = 1;
            pt1zx = 0;
        } else {
            assert(_isOnCurve(
                pt1xx, pt1xy,
                pt1yx, pt1yy
            ));
        }

        uint256[6] memory pt2 = _ECTwistMulJacobian(
            s,
            pt1xx, pt1xy,
            pt1yx, pt1yy,
            pt1zx, 0
        );

        return _fromJacobian(
            pt2[PTXX], pt2[PTXY],
            pt2[PTYX], pt2[PTYY],
            pt2[PTZX], pt2[PTZY]
        );
    }

    /**
     * @notice Get the field modulus
     * @return The field modulus
     */
    function GetFieldModulus() public pure returns (uint256) {
        return FIELD_MODULUS;
    }

    function submod(uint256 a, uint256 b, uint256 n) internal pure returns (uint256) {
        return addmod(a, n - b, n);
    }

    function _FQ2Mul(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256, uint256) {
        return (
            submod(mulmod(xx, yx, FIELD_MODULUS), mulmod(xy, yy, FIELD_MODULUS), FIELD_MODULUS),
            addmod(mulmod(xx, yy, FIELD_MODULUS), mulmod(xy, yx, FIELD_MODULUS), FIELD_MODULUS)
        );
    }

    function _FQ2Muc(
        uint256 xx, uint256 xy,
        uint256 c
    ) internal pure returns (uint256, uint256) {
        return (
            mulmod(xx, c, FIELD_MODULUS),
            mulmod(xy, c, FIELD_MODULUS)
        );
    }

    function _FQ2Add(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256, uint256) {
        return (
            addmod(xx, yx, FIELD_MODULUS),
            addmod(xy, yy, FIELD_MODULUS)
        );
    }

    function _FQ2Sub(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (uint256 rx, uint256 ry) {
        return (
            submod(xx, yx, FIELD_MODULUS),
            submod(xy, yy, FIELD_MODULUS)
        );
    }

    function _FQ2Div(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal view returns (uint256, uint256) {
        (yx, yy) = _FQ2Inv(yx, yy);
        return _FQ2Mul(xx, xy, yx, yy);
    }

    function _FQ2Inv(uint256 x, uint256 y) internal view returns (uint256, uint256) {
        uint256 inv = _modInv(addmod(mulmod(y, y, FIELD_MODULUS), mulmod(x, x, FIELD_MODULUS), FIELD_MODULUS), FIELD_MODULUS);
        return (
            mulmod(x, inv, FIELD_MODULUS),
            FIELD_MODULUS - mulmod(y, inv, FIELD_MODULUS)
        );
    }

    function _isOnCurve(
        uint256 xx, uint256 xy,
        uint256 yx, uint256 yy
    ) internal pure returns (bool) {
        uint256 yyx;
        uint256 yyy;
        uint256 xxxx;
        uint256 xxxy;
        (yyx, yyy) = _FQ2Mul(yx, yy, yx, yy);
        (xxxx, xxxy) = _FQ2Mul(xx, xy, xx, xy);
        (xxxx, xxxy) = _FQ2Mul(xxxx, xxxy, xx, xy);
        (yyx, yyy) = _FQ2Sub(yyx, yyy, xxxx, xxxy);
        (yyx, yyy) = _FQ2Sub(yyx, yyy, TWISTBX, TWISTBY);
        return yyx == 0 && yyy == 0;
    }

    function _modInv(uint256 a, uint256 n) internal view returns (uint256 result) {
        bool success;
        assembly {
            let freemem := mload(0x40)
            mstore(freemem, 0x20)
            mstore(add(freemem,0x20), 0x20)
            mstore(add(freemem,0x40), 0x20)
            mstore(add(freemem,0x60), a)
            mstore(add(freemem,0x80), sub(n, 2))
            mstore(add(freemem,0xA0), n)
            success := staticcall(sub(gas, 2000), 5, freemem, 0xC0, freemem, 0x20)
            result := mload(freemem)
        }
        require(success);
    }

    function _fromJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal view returns (
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy
    ) {
        uint256 invzx;
        uint256 invzy;
        (invzx, invzy) = _FQ2Inv(pt1zx, pt1zy);
        (pt2xx, pt2xy) = _FQ2Mul(pt1xx, pt1xy, invzx, invzy);
        (pt2yx, pt2yy) = _FQ2Mul(pt1yx, pt1yy, invzx, invzy);
    }

    function _ECTwistAddJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy,
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy,
        uint256 pt2zx, uint256 pt2zy) internal pure returns (uint256[6] memory pt3) {
            if (pt1zx == 0 && pt1zy == 0) {
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    pt2xx, pt2xy,
                    pt2yx, pt2yy,
                    pt2zx, pt2zy
                );
                return pt3;
            } else if (pt2zx == 0 && pt2zy == 0) {
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    pt1xx, pt1xy,
                    pt1yx, pt1yy,
                    pt1zx, pt1zy
                );
                return pt3;
            }

            (pt2yx,     pt2yy)     = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // U1 = y2 * z1
            (pt3[PTYX], pt3[PTYY]) = _FQ2Mul(pt1yx, pt1yy, pt2zx, pt2zy); // U2 = y1 * z2
            (pt2xx,     pt2xy)     = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // V1 = x2 * z1
            (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1xx, pt1xy, pt2zx, pt2zy); // V2 = x1 * z2

            if (pt2xx == pt3[PTZX] && pt2xy == pt3[PTZY]) {
                if (pt2yx == pt3[PTYX] && pt2yy == pt3[PTYY]) {
                    (
                        pt3[PTXX], pt3[PTXY],
                        pt3[PTYX], pt3[PTYY],
                        pt3[PTZX], pt3[PTZY]
                    ) = _ECTwistDoubleJacobian(pt1xx, pt1xy, pt1yx, pt1yy, pt1zx, pt1zy);
                    return pt3;
                }
                (
                    pt3[PTXX], pt3[PTXY],
                    pt3[PTYX], pt3[PTYY],
                    pt3[PTZX], pt3[PTZY]
                ) = (
                    1, 0,
                    1, 0,
                    0, 0
                );
                return pt3;
            }

            (pt2zx,     pt2zy)     = _FQ2Mul(pt1zx, pt1zy, pt2zx,     pt2zy);     // W = z1 * z2
            (pt1xx,     pt1xy)     = _FQ2Sub(pt2yx, pt2yy, pt3[PTYX], pt3[PTYY]); // U = U1 - U2
            (pt1yx,     pt1yy)     = _FQ2Sub(pt2xx, pt2xy, pt3[PTZX], pt3[PTZY]); // V = V1 - V2
            (pt1zx,     pt1zy)     = _FQ2Mul(pt1yx, pt1yy, pt1yx,     pt1yy);     // V_squared = V * V
            (pt2yx,     pt2yy)     = _FQ2Mul(pt1zx, pt1zy, pt3[PTZX], pt3[PTZY]); // V_squared_times_V2 = V_squared * V2
            (pt1zx,     pt1zy)     = _FQ2Mul(pt1zx, pt1zy, pt1yx,     pt1yy);     // V_cubed = V * V_squared
            (pt3[PTZX], pt3[PTZY]) = _FQ2Mul(pt1zx, pt1zy, pt2zx,     pt2zy);     // newz = V_cubed * W
            (pt2xx,     pt2xy)     = _FQ2Mul(pt1xx, pt1xy, pt1xx,     pt1xy);     // U * U
            (pt2xx,     pt2xy)     = _FQ2Mul(pt2xx, pt2xy, pt2zx,     pt2zy);     // U * U * W
            (pt2xx,     pt2xy)     = _FQ2Sub(pt2xx, pt2xy, pt1zx,     pt1zy);     // U * U * W - V_cubed
            (pt2zx,     pt2zy)     = _FQ2Muc(pt2yx, pt2yy, 2);                    // 2 * V_squared_times_V2
            (pt2xx,     pt2xy)     = _FQ2Sub(pt2xx, pt2xy, pt2zx,     pt2zy);     // A = U * U * W - V_cubed - 2 * V_squared_times_V2
            (pt3[PTXX], pt3[PTXY]) = _FQ2Mul(pt1yx, pt1yy, pt2xx,     pt2xy);     // newx = V * A
            (pt1yx,     pt1yy)     = _FQ2Sub(pt2yx, pt2yy, pt2xx,     pt2xy);     // V_squared_times_V2 - A
            (pt1yx,     pt1yy)     = _FQ2Mul(pt1xx, pt1xy, pt1yx,     pt1yy);     // U * (V_squared_times_V2 - A)
            (pt1xx,     pt1xy)     = _FQ2Mul(pt1zx, pt1zy, pt3[PTYX], pt3[PTYY]); // V_cubed * U2
            (pt3[PTYX], pt3[PTYY]) = _FQ2Sub(pt1yx, pt1yy, pt1xx,     pt1xy);     // newy = U * (V_squared_times_V2 - A) - V_cubed * U2
    }

    function _ECTwistDoubleJacobian(
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal pure returns (
        uint256 pt2xx, uint256 pt2xy,
        uint256 pt2yx, uint256 pt2yy,
        uint256 pt2zx, uint256 pt2zy
    ) {
        (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 3);            // 3 * x
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1xx, pt1xy); // W = 3 * x * x
        (pt1zx, pt1zy) = _FQ2Mul(pt1yx, pt1yy, pt1zx, pt1zy); // S = y * z
        (pt2yx, pt2yy) = _FQ2Mul(pt1xx, pt1xy, pt1yx, pt1yy); // x * y
        (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt1zx, pt1zy); // B = x * y * S
        (pt1xx, pt1xy) = _FQ2Mul(pt2xx, pt2xy, pt2xx, pt2xy); // W * W
        (pt2zx, pt2zy) = _FQ2Muc(pt2yx, pt2yy, 8);            // 8 * B
        (pt1xx, pt1xy) = _FQ2Sub(pt1xx, pt1xy, pt2zx, pt2zy); // H = W * W - 8 * B
        (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt1zx, pt1zy); // S_squared = S * S
        (pt2yx, pt2yy) = _FQ2Muc(pt2yx, pt2yy, 4);            // 4 * B
        (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt1xx, pt1xy); // 4 * B - H
        (pt2yx, pt2yy) = _FQ2Mul(pt2yx, pt2yy, pt2xx, pt2xy); // W * (4 * B - H)
        (pt2xx, pt2xy) = _FQ2Muc(pt1yx, pt1yy, 8);            // 8 * y
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1yx, pt1yy); // 8 * y * y
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt2zx, pt2zy); // 8 * y * y * S_squared
        (pt2yx, pt2yy) = _FQ2Sub(pt2yx, pt2yy, pt2xx, pt2xy); // newy = W * (4 * B - H) - 8 * y * y * S_squared
        (pt2xx, pt2xy) = _FQ2Muc(pt1xx, pt1xy, 2);            // 2 * H
        (pt2xx, pt2xy) = _FQ2Mul(pt2xx, pt2xy, pt1zx, pt1zy); // newx = 2 * H * S
        (pt2zx, pt2zy) = _FQ2Mul(pt1zx, pt1zy, pt2zx, pt2zy); // S * S_squared
        (pt2zx, pt2zy) = _FQ2Muc(pt2zx, pt2zy, 8);            // newz = 8 * S * S_squared
    }

    function _ECTwistMulJacobian(
        uint256 d,
        uint256 pt1xx, uint256 pt1xy,
        uint256 pt1yx, uint256 pt1yy,
        uint256 pt1zx, uint256 pt1zy
    ) internal pure returns (uint256[6] memory pt2) {
        while (d != 0) {
            if ((d & 1) != 0) {
                pt2 = _ECTwistAddJacobian(
                    pt2[PTXX], pt2[PTXY],
                    pt2[PTYX], pt2[PTYY],
                    pt2[PTZX], pt2[PTZY],
                    pt1xx, pt1xy,
                    pt1yx, pt1yy,
                    pt1zx, pt1zy);
            }
            (
                pt1xx, pt1xy,
                pt1yx, pt1yy,
                pt1zx, pt1zy
            ) = _ECTwistDoubleJacobian(
                pt1xx, pt1xy,
                pt1yx, pt1yy,
                pt1zx, pt1zy
            );

            d = d / 2;
        }
    }
}
// This file is MIT Licensed.
//
// Copyright 2017 Christian Reitwiessner
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
pragma solidity ^0.5.0;
library Pairing {
    struct G1Point {
        uint X;
        uint Y;
    }
    // Encoding of field elements is: X[0] * z + X[1]
    struct G2Point {
        uint[2] X;
        uint[2] Y;
    }
    /// @return the generator of G1
    function P1() pure internal returns (G1Point memory) {
        return G1Point(1, 2);
    }
    /// @return the generator of G2
    function P2() pure internal returns (G2Point memory) {
        return G2Point(
            [11559732032986387107991004021392285783925812861821192530917403151452391805634,
             10857046999023057135944570762232829481370756359578518086990519993285655852781],
            [4082367875863433681332203403145435568316851327593401208105741076214120093531,
             8495653923123431417604973247489272438418190587263600148770280649306958101930]
        );
    }
    /// @return the negation of p, i.e. p.addition(p.negate()) should be zero.
    function negate(G1Point memory p) pure internal returns (G1Point memory) {
        // The prime q in the base field F_q for G1
        uint q = 21888242871839275222246405745257275088696311157297823662689037894645226208583;
        if (p.X == 0 && p.Y == 0)
            return G1Point(0, 0);
        return G1Point(p.X, q - (p.Y % q));
    }
    /// @return the sum of two points of G1
    function addition(G1Point memory p1, G1Point memory p2) internal returns (G1Point memory r) {
        uint[4] memory input;
        input[0] = p1.X;
        input[1] = p1.Y;
        input[2] = p2.X;
        input[3] = p2.Y;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 6, 0, input, 0xc0, r, 0x60)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require(success);
    }
    /// @return the sum of two points of G2
    function addition(G2Point memory p1, G2Point memory p2) internal returns (G2Point memory r) {
        (r.X[1], r.X[0], r.Y[1], r.Y[0]) = BN256G2.ECTwistAdd(p1.X[1],p1.X[0],p1.Y[1],p1.Y[0],p2.X[1],p2.X[0],p2.Y[1],p2.Y[0]);
    }
    /// @return the product of a point on G1 and a scalar, i.e.
    /// p == p.scalar_mul(1) and p.addition(p) == p.scalar_mul(2) for all points p.
    function scalar_mul(G1Point memory p, uint s) internal returns (G1Point memory r) {
        uint[3] memory input;
        input[0] = p.X;
        input[1] = p.Y;
        input[2] = s;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 7, 0, input, 0x80, r, 0x60)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require (success);
    }
    /// @return the result of computing the pairing check
    /// e(p1[0], p2[0]) *  .... * e(p1[n], p2[n]) == 1
    /// For example pairing([P1(), P1().negate()], [P2(), P2()]) should
    /// return true.
    function pairing(G1Point[] memory p1, G2Point[] memory p2) internal returns (bool) {
        require(p1.length == p2.length);
        uint elements = p1.length;
        uint inputSize = elements * 6;
        uint[] memory input = new uint[](inputSize);
        for (uint i = 0; i < elements; i++)
        {
            input[i * 6 + 0] = p1[i].X;
            input[i * 6 + 1] = p1[i].Y;
            input[i * 6 + 2] = p2[i].X[0];
            input[i * 6 + 3] = p2[i].X[1];
            input[i * 6 + 4] = p2[i].Y[0];
            input[i * 6 + 5] = p2[i].Y[1];
        }
        uint[1] memory out;
        bool success;
        assembly {
            success := call(sub(gas, 2000), 8, 0, add(input, 0x20), mul(inputSize, 0x20), out, 0x20)
            // Use "invalid" to make gas estimation work
            switch success case 0 { invalid() }
        }
        require(success);
        return out[0] != 0;
    }
    /// Convenience method for a pairing check for two pairs.
    function pairingProd2(G1Point memory a1, G2Point memory a2, G1Point memory b1, G2Point memory b2) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](2);
        G2Point[] memory p2 = new G2Point[](2);
        p1[0] = a1;
        p1[1] = b1;
        p2[0] = a2;
        p2[1] = b2;
        return pairing(p1, p2);
    }
    /// Convenience method for a pairing check for three pairs.
    function pairingProd3(
            G1Point memory a1, G2Point memory a2,
            G1Point memory b1, G2Point memory b2,
            G1Point memory c1, G2Point memory c2
    ) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](3);
        G2Point[] memory p2 = new G2Point[](3);
        p1[0] = a1;
        p1[1] = b1;
        p1[2] = c1;
        p2[0] = a2;
        p2[1] = b2;
        p2[2] = c2;
        return pairing(p1, p2);
    }
    /// Convenience method for a pairing check for four pairs.
    function pairingProd4(
            G1Point memory a1, G2Point memory a2,
            G1Point memory b1, G2Point memory b2,
            G1Point memory c1, G2Point memory c2,
            G1Point memory d1, G2Point memory d2
    ) internal returns (bool) {
        G1Point[] memory p1 = new G1Point[](4);
        G2Point[] memory p2 = new G2Point[](4);
        p1[0] = a1;
        p1[1] = b1;
        p1[2] = c1;
        p1[3] = d1;
        p2[0] = a2;
        p2[1] = b2;
        p2[2] = c2;
        p2[3] = d2;
        return pairing(p1, p2);
    }
}

contract Verifier {
    using Pairing for *;
    struct VerifyingKey {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G2Point gamma;
        Pairing.G2Point delta;
        Pairing.G1Point[] gamma_abc;
    }
    struct Proof {
        Pairing.G1Point a;
        Pairing.G2Point b;
        Pairing.G1Point c;
    }
    function verifyingKey() pure internal returns (VerifyingKey memory vk) {
        vk.a = Pairing.G1Point(uint256(0x2884cf5ac168644f625a2251f3a6bee393f48ddce96f937bd66c60bb151c5779), uint256(0x24820d9f36b7c5ef677c55d54c8d2a17cccf8e6dc12ab6bc2da85585fc362fb3));
        vk.b = Pairing.G2Point([uint256(0x0ac43048fcd228472a94dcaca13621780c063efad2031a23d08901f4b605b61e), uint256(0x09393a931f98fd2e705d9ef779b993e79d5fb2eaef71e92288a6e054e976f9b0)], [uint256(0x004cccd4aa1662303777e618f47102a61a75422029cd2118a0c2ee71c40a3bca), uint256(0x0c3ffb3c0177cafb2ce6668807215e6290029ef55c76905578434fe9ddb67bcd)]);
        vk.gamma = Pairing.G2Point([uint256(0x20cce92a28ed8dcdc6a9c14e5c194764f7ec43317949919b9ab5c07a1fe72026), uint256(0x05c1b85af730d859bbacaedf3a9921b998c9d5f403f077012b417730d08dac8c)], [uint256(0x0542b91ae9e164e7dfbd015b17026ba77c2518bfe5374c5ac425f1b2d2ad980f), uint256(0x2abeb94fe7bf700cbfbf57a4f06ab7e71ae95319f85ba05f8522734b24a48ff2)]);
        vk.delta = Pairing.G2Point([uint256(0x285a5ec2d3e962ba79018950b0690ee9821e657c439c079784dbeb327505b4b7), uint256(0x2db08e06a3dfc37149d0df801a40ab702d5711831e232e00f42ade9abf0aa733)], [uint256(0x23da10160f5e6c9397f876dadb0b0029418c39bb5374c5d7e896866e0a0ff9a3), uint256(0x2c744e48d51d76fd748e0e31ceb1491f4d64f448ab18cf1dd0031de7d713ee66)]);
        vk.gamma_abc = new Pairing.G1Point[](3);
        vk.gamma_abc[0] = Pairing.G1Point(uint256(0x0da280e90bcc174d1dc70ee666488e949ec3ee28e3865675c54e0bc1c311434a), uint256(0x22d399c4dfc5470ea18340d68eb589044ad6c5f74d63da1720639c109add5f62));
        vk.gamma_abc[1] = Pairing.G1Point(uint256(0x1307ff3e97233192f569740f2c75675697e64f794eddfb69bc31a4b3e5f41576), uint256(0x2f832dfa9dfdbc7d8833459e653994b89c4544120808ae22f41391889fa5167c));
        vk.gamma_abc[2] = Pairing.G1Point(uint256(0x2a40be867dd7796b0cdd92d20980f8466221390febae5dbea101c1dda0672858), uint256(0x06c0360cc3cae1d2b38925b2e85f9bf4ef20c2d5148dc56906407552f23a43f8));
    }
    function verify(uint[] memory input, Proof memory proof) internal returns (uint) {
        uint256 snark_scalar_field = 21888242871839275222246405745257275088548364400416034343698204186575808495617;
        VerifyingKey memory vk = verifyingKey();
        require(input.length + 1 == vk.gamma_abc.length);
        // Compute the linear combination vk_x
        Pairing.G1Point memory vk_x = Pairing.G1Point(0, 0);
        for (uint i = 0; i < input.length; i++) {
            require(input[i] < snark_scalar_field);
            vk_x = Pairing.addition(vk_x, Pairing.scalar_mul(vk.gamma_abc[i + 1], input[i]));
        }
        vk_x = Pairing.addition(vk_x, vk.gamma_abc[0]);
        if(!Pairing.pairingProd4(
             proof.a, proof.b,
             Pairing.negate(vk_x), vk.gamma,
             Pairing.negate(proof.c), vk.delta,
             Pairing.negate(vk.a), vk.b)) return 1;
        return 0;
    }
    event Verified(string s);
    function verifyTx(
            uint[2] memory a,
            uint[2][2] memory b,
            uint[2] memory c,
            uint[2] memory input
        ) public returns (bool r) {
        Proof memory proof;
        proof.a = Pairing.G1Point(a[0], a[1]);
        proof.b = Pairing.G2Point([b[0][0], b[0][1]], [b[1][0], b[1][1]]);
        proof.c = Pairing.G1Point(c[0], c[1]);
        uint[] memory inputValues = new uint[](input.length);
        for(uint i = 0; i < input.length; i++){
            inputValues[i] = input[i];
        }
        if (verify(inputValues, proof) == 0) {
            emit Verified("Transaction successfully verified.");
            return true;
        } else {
            return false;
        }
    }
}

This is in fact just contract generated when we run ZoKrates' getting-started guide: https://zokrates.github.io/gettingstarted.html

After deploying the smart contract on Ethereum, we can extract the following contract code in hex:

608060405234801561001057600080fd5b506004361061002b5760003560e01c8063621e9ec014610030575b600080fd5b61018f600480360361014081101561004757600080fd5b8101908080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f82011690508083019250505050505091929192908060800190600280602002604051908101604052809291906000905b828210156100fc578382604002016002806020026040519081016040528092919082600260200280828437600081840152601f19601f820116905080830192505050505050815260200190600101906100a8565b50505050919291929080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f820116905080830192505050505050919291929080604001906002806020026040519081016040528092919082600260200280828437600081840152601f19601f82011690508083019250505050505091929192905050506101a9565b604051808215151515815260200191505060405180910390f35b60006101b3610f49565b6040518060400160405280876000600281106101cb57fe5b60200201518152602001876001600281106101e257fe5b60200201518152508160000181905250604051806040016040528060405180604001604052808860006002811061021557fe5b602002015160006002811061022657fe5b602002015181526020018860006002811061023d57fe5b602002015160016002811061024e57fe5b6020020151815250815260200160405180604001604052808860016002811061027357fe5b602002015160006002811061028457fe5b602002015181526020018860016002811061029b57fe5b60200201516001600281106102ac57fe5b602002015181525081525081602001819052506040518060400160405280856000600281106102d757fe5b60200201518152602001856001600281106102ee57fe5b60200201518152508160400181905250606060026040519080825280602002602001820160405280156103305781602001602082028038833980820191505090505b50905060008090505b60028110156103785784816002811061034e57fe5b602002015182828151811061035f57fe5b6020026020010181815250508080600101915050610339565b50600061038582846103f0565b14156103e1577f3f3cfdb26fb5f9f1786ab4f1a1f9cd4c0b5e726cbdfc26e495261731aad44e396040518080602001828103825260228152602001806110a66022913960400191505060405180910390a16001925050506103e8565b6000925050505b949350505050565b6000807f30644e72e131a029b85045b68181585d2833e84879b9709143e1f593f0000001905061041e610f7c565b610426610562565b905080608001515160018651011461043d57600080fd5b610445610fc3565b6040518060400160405280600081526020016000815250905060008090505b86518110156104db578387828151811061047a57fe5b60200260200101511061048c57600080fd5b6104cc826104c7856080015160018501815181106104a657fe5b60200260200101518a85815181106104ba57fe5b60200260200101516109af565b610a43565b91508080600101915050610464565b506104fe8183608001516000815181106104f157fe5b6020026020010151610a43565b90506105448560000151866020015161051684610af6565b85604001516105288a60400151610af6565b876060015161053a8960000151610af6565b8960200151610b90565b610554576001935050505061055c565b600093505050505b92915050565b61056a610f7c565b60405180604001604052807f2884cf5ac168644f625a2251f3a6bee393f48ddce96f937bd66c60bb151c577981526020017f24820d9f36b7c5ef677c55d54c8d2a17cccf8e6dc12ab6bc2da85585fc362fb38152508160000181905250604051806040016040528060405180604001604052807f0ac43048fcd228472a94dcaca13621780c063efad2031a23d08901f4b605b61e81526020017f09393a931f98fd2e705d9ef779b993e79d5fb2eaef71e92288a6e054e976f9b0815250815260200160405180604001604052807e4cccd4aa1662303777e618f47102a61a75422029cd2118a0c2ee71c40a3bca81526020017f0c3ffb3c0177cafb2ce6668807215e6290029ef55c76905578434fe9ddb67bcd8152508152508160200181905250604051806040016040528060405180604001604052807f20cce92a28ed8dcdc6a9c14e5c194764f7ec43317949919b9ab5c07a1fe7202681526020017f05c1b85af730d859bbacaedf3a9921b998c9d5f403f077012b417730d08dac8c815250815260200160405180604001604052807f0542b91ae9e164e7dfbd015b17026ba77c2518bfe5374c5ac425f1b2d2ad980f81526020017f2abeb94fe7bf700cbfbf57a4f06ab7e71ae95319f85ba05f8522734b24a48ff28152508152508160400181905250604051806040016040528060405180604001604052807f285a5ec2d3e962ba79018950b0690ee9821e657c439c079784dbeb327505b4b781526020017f2db08e06a3dfc37149d0df801a40ab702d5711831e232e00f42ade9abf0aa733815250815260200160405180604001604052807f23da10160f5e6c9397f876dadb0b0029418c39bb5374c5d7e896866e0a0ff9a381526020017f2c744e48d51d76fd748e0e31ceb1491f4d64f448ab18cf1dd0031de7d713ee668152508152508160600181905250600360405190808252806020026020018201604052801561085057816020015b61083d610fdd565b8152602001906001900390816108355790505b50816080018190525060405180604001604052807f0da280e90bcc174d1dc70ee666488e949ec3ee28e3865675c54e0bc1c311434a81526020017f22d399c4dfc5470ea18340d68eb589044ad6c5f74d63da1720639c109add5f6281525081608001516000815181106108bf57fe5b602002602001018190525060405180604001604052807f1307ff3e97233192f569740f2c75675697e64f794eddfb69bc31a4b3e5f4157681526020017f2f832dfa9dfdbc7d8833459e653994b89c4544120808ae22f41391889fa5167c815250816080015160018151811061093057fe5b602002602001018190525060405180604001604052807f2a40be867dd7796b0cdd92d20980f8466221390febae5dbea101c1dda067285881526020017f06c0360cc3cae1d2b38925b2e85f9bf4ef20c2d5148dc56906407552f23a43f881525081608001516002815181106109a157fe5b602002602001018190525090565b6109b7610fc3565b6109bf610ff7565b8360000151816000600381106109d157fe5b6020020181815250508360200151816001600381106109ec57fe5b6020020181815250508281600260038110610a0357fe5b6020020181815250506000606083608084600060076107d05a03f190508060008114610a2e57610a30565bfe5b5080610a3b57600080fd5b505092915050565b610a4b610fc3565b610a53611019565b836000015181600060048110610a6557fe5b602002018181525050836020015181600160048110610a8057fe5b602002018181525050826000015181600260048110610a9b57fe5b602002018181525050826020015181600360048110610ab657fe5b602002018181525050600060608360c084600060066107d05a03f190508060008114610ae157610ae3565bfe5b5080610aee57600080fd5b505092915050565b610afe610fc3565b60007f30644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd47905060008360000151148015610b3b575060008360200151145b15610b5f576040518060400160405280600081526020016000815250915050610b8b565b60405180604001604052808460000151815260200182856020015181610b8157fe5b0683038152509150505b919050565b600060606004604051908082528060200260200182016040528015610bcf57816020015b610bbc610fdd565b815260200190600190039081610bb45790505b50905060606004604051908082528060200260200182016040528015610c0f57816020015b610bfc61103b565b815260200190600190039081610bf45790505b5090508a82600081518110610c2057fe5b60200260200101819052508882600181518110610c3957fe5b60200260200101819052508682600281518110610c5257fe5b60200260200101819052508482600381518110610c6b57fe5b60200260200101819052508981600081518110610c8457fe5b60200260200101819052508781600181518110610c9d57fe5b60200260200101819052508581600281518110610cb657fe5b60200260200101819052508381600381518110610ccf57fe5b6020026020010181905250610ce48282610cf4565b9250505098975050505050505050565b60008151835114610d0457600080fd5b6000835190506000600682029050606081604051908082528060200260200182016040528015610d435781602001602082028038833980820191505090505b50905060008090505b83811015610ee957868181518110610d6057fe5b602002602001015160000151826000600684020181518110610d7e57fe5b602002602001018181525050868181518110610d9657fe5b602002602001015160200151826001600684020181518110610db457fe5b602002602001018181525050858181518110610dcc57fe5b602002602001015160000151600060028110610de457fe5b6020020151826002600684020181518110610dfb57fe5b602002602001018181525050858181518110610e1357fe5b602002602001015160000151600160028110610e2b57fe5b6020020151826003600684020181518110610e4257fe5b602002602001018181525050858181518110610e5a57fe5b602002602001015160200151600060028110610e7257fe5b6020020151826004600684020181518110610e8957fe5b602002602001018181525050858181518110610ea157fe5b602002602001015160200151600160028110610eb957fe5b6020020151826005600684020181518110610ed057fe5b6020026020010181815250508080600101915050610d4c565b50610ef2611061565b60006020826020860260208601600060086107d05a03f190508060008114610f1957610f1b565bfe5b5080610f2657600080fd5b600082600060018110610f3557fe5b602002015114159550505050505092915050565b6040518060600160405280610f5c610fdd565b8152602001610f6961103b565b8152602001610f76610fdd565b81525090565b6040518060a00160405280610f8f610fdd565b8152602001610f9c61103b565b8152602001610fa961103b565b8152602001610fb661103b565b8152602001606081525090565b604051806040016040528060008152602001600081525090565b604051806040016040528060008152602001600081525090565b6040518060600160405280600390602082028038833980820191505090505090565b6040518060800160405280600490602082028038833980820191505090505090565b604051806040016040528061104e611083565b815260200161105b611083565b81525090565b6040518060200160405280600190602082028038833980820191505090505090565b604051806040016040528060029060208202803883398082019150509050509056fe5472616e73616374696f6e207375636365737366756c6c792076657269666965642ea265627a7a7231582091214c18c2db32d6ee1d6cee87a914a4ab17e56e1a484579e8c4427e8296ecf064736f6c634300050c0032

If we try to run this with correct input parameters, evmone would generate segment fault:

./bin/evmone-bench verifier_out.hex 621e9ec023087b24becc8b6ce3352d3ecda8c3bb4d966e2d316fb32df69411f52b57bcef09794a9d582525b0554bd44aaf358c038215e4469dd8e12c9292ece84f49ba0e2f727062b860a2207fbd2534e06e0569ad273fe21cce33d5ed0e4263cc8a62d62f79f4c92b082b0c51524e1495492ad0f1e6b6e0fc6946becf137c5212814b082bbaeccaa1fdaddfb6b4bc30c3f8941a69d9a6d315001d5056ec4f7202a0a68e0f8e9f2253d63058b19425ffdd68a9781e240d6bab14cd63737e0fbda452e4af02a6c5c81a8818c89e751793220224ef43bbfe5015fda95b3e3b907a0836c31b2b98987be08c79a6adca5fff6d88a39b45b1a3eb5a33027598da0f4f750761a7000000000000000000000000000000000000000000000000000000000001bba10000000000000000000000000000000000000000000000000000000000000001 0000000000000000000000000000000000000000000000000000000000000001                            
2019-11-15 00:11:37
Running ./bin/evmone-bench
Run on (8 X 2500 MHz CPU s)
CPU Caches:
  L1 Data 32K (x4)
  L1 Instruction 32K (x4)
  L2 Unified 1024K (x4)
  L3 Unified 36608K (x1)
zsh: segmentation fault  ./bin/evmone-bench verifier_out.hex

Note that the same parameters could run on parity evm just fine.

Any idea what might be causing this? Many thanks!

drag races between geth, aleth and evmone

I've been cleaning up my old https://github.com/gcolvin/evm-drag-race repo, and have the first set of results ready, which are performance measurements for the arithmetic operations, constrained so that operands are 64, 128, or 256 bits long. From some simple analysis of these measurements it's clear that evmone is a large improvement over its predecessors, that geth has improved over the last year and and a half, and that some opcodes are still mispriced.

The tests (except exp) do a million loops over blocks of about three hundred opcodes, mostly consisting of dup2 <op> pairs for each tested op, with occasional breaks to reset the stack. They are sensitive to the quality of the bigint libraries, and with very long basic blocks they benefit from pulling the gas calculations to block boundaries.

Here are the raw numbers for gas and time in seconds, parsed from the VM output.

(sec/test) gas geth aleth evmone
nop 361000061 3.021555105 2.129643 0.746905
pop 745000061 4.457840038 2.311975 0.632604
add64 873000061 8.044817514 2.911097 0.921461
add128 873000061 8.623307368 3.592689 0.984146
add256 873000061 8.300209998 5.320576 0.931194
sub64 873000061 7.849262534 3.009673 1.171843
sub128 873000061 8.815820885 3.855291 1.171966
sub256 873000061 8.591228095 5.137147 1.171641
mul64 1129000061 8.209472574 2.612447 1.501970
mul128 1129000061 8.38368307 2.818472 1.500458
mul256 1129000061 20.599212858 7.086396 1.502394
div64 1129000061 9.658561249 5.953036 6.957576
div128 1129000061 12.370122234 10.857481 6.652817
div256 1129000061 36.699296987 18.487051 7.299538
exp 1281870061 130.965289511 45.846095 8.183632

Attempting to correct interpreter overhead proves to be fraught with peril. So below I report the total time to execute a single operation including the interpreter overhead, as reported by the VMs themselves. The nop test uses blocks of jumpdest jumpdest, and the pop test uses blocks of dup2 pop. They can be helpful in estimating interpreter overhead, being very little but overhead, but understanding the interpreter code helps.

Also for comparison, mul64c.c does the same calculation with blocks of x *= y. Unoptimized (gcc -O0) it runs at 0.27 ns/op, and fully optimized (gcc -O3) at 0.0016 ns/OP. That sort of sets a bound on how fast a VM could be.

(ns/OP) geth aleth evmone C — C opt
nop 9.33 6.57 2.31
pop 13.76 7.14 1.95
add64 24.83 8.98 2.84
add128 26.62 11.09 3.04
add256 25.62 16.42 2.87
sub64 24.23 9.29 3.62
sub128 27.21 11.90 3.62
sub256 26.52 15.86 3.62
mul64 25.34 8.06 4.64 0.27 — 0.0016
mul128 25.88 8.70 4.63
mul256 63.58 21.87 4.64
div64 29.81 18.37 21.47
div128 38.18 33.51 20.53
div256 113.27 57.06 22.53
exp 13473.80 4716.68 841.94

Finally we have the time for each arithmetic operation normalized to nanoseconds per unit of gas. If every opcode were perfectly priced the values for each column of tests for a VM would be the same. This isn't the case, especially for division and exponentiation.

(ns/gas) gas geth aleth evmone
add64 128000000 28.02 4.68 2.26
add128 128000000 32.54 10.01 2.75
add256 128000000 30.02 23.50 2.33
sub64 128000000 26.50 5.45 4.21
sub128 128000000 34.05 12.06 4.21
sub256 128000000 32.29 22.07 4.21
mul64 384000000 9.77 0.78 2.26
mul128 384000000 10.22 1.32 2.26
mul256 384000000 42.03 12.43 2.27
div64 384000000 13.54 9.48 16.47
div128 384000000 20.60 22.25 15.68
div256 384000000 83.96 42.12 17.36
exp 536870000 235.64 81.09 14.06

I still have several small Solidity programs to test. The programs wouldn't compile with version 5, and I broke some of them getting them to compile.

EVM test "empty" triggers debug assert on Windows: "string_view subscript out of range"

The EVM test "empty" triggers the following debug assertion when run on Windows:

---------------------------
Microsoft Visual C++ Runtime Library
---------------------------
Debug Assertion Failed!

Program: ...se\Documents\Code\evmone\build\bin\Debug\evmone-unittests.exe
File: C:\Program Files (x86)\Microsoft Visual Studio\2019\Preview\VC\Tools\MSVC\14.23.28008\include\xstring
Line: 1268

Expression: string_view subscript out of range

For information on how your program can cause an assertion
failure, see the Visual C++ documentation on asserts.

(Press Retry to debug the application)

---------------------------
Abort   Retry   Ignore   
---------------------------

Test is here:

TEST_F(evm, empty)
{
execute(0, "");
EXPECT_GAS_USED(EVMC_SUCCESS, 0);
}

The problem is that execute wraps the supplied string "" with a string_view but string_views don't include null terminators, meaning that accessing this string_view has undefined behavior since it doesn't contain any characters. We then try to access the first char in the string view here:

/// Wrapper for evmone::execute. The result will be in the .result field.
void execute(const evmc_message& m, bytes_view code) noexcept
{
result = vm.execute(host, rev, m, &code[0], code.size());

AdjustTime support

Hi There,

Does EVM One support the geth equivalent of AdjustTime(duration time.Duration) at this point? Conscious it's not part of the RPC spec and probably not the client-evm spec so answer may be no, but useful feature nonetheless!

Create SGX build

The SDK can be found here: https://software.intel.com/content/www/us/en/develop/topics/software-guard-extensions/sdk.html

The SDK contains installers for various distros, including recent Ubuntu versions, so should we do this task it would make sense installing it on one of the ethereum/cpp-build-env images.

A basic guide is here, and the official one here.

I think the work done in #293 would be a good stepping stone as it requires very similar changes/improvements (no pthreads/dlopen/etc).

Do static call to evmone got `EVMC_REVERT`

The demo repo (Rust binding): https://github.com/TheWaWaR/play-evmone-lite

The way I reproduce this:

  1. Create an account with a SimpleStorage contract
  2. Call SimpleStorage::get() method

This will return a EVMC_REVERT failure result:

ExecutionResult {
    status_code: EVMC_REVERT,
    gas_left: 4466543,
    output_data: "",
    release: None,
    create_address: 0x0000000000000000000000000000000000000000,
    padding: [0, 0, 0, 0],
}

The SimpleStorage.sol contract:

pragma solidity >=0.4.0 <0.7.0;

contract SimpleStorage {
  uint storedData;

  constructor() public payable {
    storedData = 123;
  }

  function set(uint x) public payable {
    storedData = x;
  }

  function get() public view returns (uint) {
    return storedData;
  }
}

NOTE: There is no problem to call SimpleStorage::set() method.

Refactor call instruction implementation

The abstract goal is to de-duplicate the logic of call instructions. Currently, almost all calls (except CALL and CALLCODE) are implemented independently - it was done to get good code coverage in unit tests.

This can be achieved in two ways:

  1. By using a template by evmc_call_kind. This might be faster (less checks in runtime, code for legacy opcodes like CALLCODE stay cold) but result in larger code size (bad for wasm?).
  2. By having single runtime procedure for all calls. See #159 (comment).

There can be also a variant that selects 1 or 2 at build time - by using [[gcc::always_inline]] in variant 2.

threading model

README.md describes the current model, with a table of function pointers, as direct threading, but it is indirect. Direct threading requires placing the address of the next opcode inline and jumping to it with no function call, typically using either assembly or GCC's address-of-label extension. Pull request #11 from @zac-williamson implements direct threading.

Optimize jumps

Notice that the jump instructions (JUMP, JUMPI) always land on OPX_BEGINBLOCK. Therefor we can execute the "beginblock" from within the jump instruction and then target the instruction after OPX_BEGINBLOCK.

The OPX_BEGINBLOCK instructions must stay at least for JUMPDEST case.
For other cases (new block after terminating instruction, the first instruction in the code) we could remove it, but it might be hard to properly specify check in the analysis for this. However, that would be beneficial, because then the size of the instruction table will be not greater than the original code size (when ignoring the additional terminating STOP). Better estimation gives us ability to pre-allocate the instruction table with guarantees that the iterators are not going to be invalidated.

Create WebAssembly build

For quite a long time we considered creating a WebAssembly build, going back as far as when runevm was created. We did try, but since evmone was using exceptions at the time it was not straightforward -- they are not yet supported natively by wasm, so the only option was commercial compilers or emscripten, both create additional scaffolding. Today however evmone is exception-free.

Probably there would not be many immediate use cases/users, but the following situations may change it:
a) Should computationally heavy opcodes be added to the EVM (akin to evm384), then a Wasm build would likely be faster than ethereumjs + BigInt and could speed up browser tools (e.g. remix, metamask)
b) If Silkworm gets used in dfinity
c) Widespread use of browser-based light clients

Since Silkworm already compiles to Wasm that means evmone compiles already too. The only thing left here is adding some CI tasks for creating builds here.

Baseline Interpreter Optimizations

List of optimizations to investigate

1. Extend bytecode with 33 zero bytes

This requires copying the original code and adding additional 33 zero bytes in the end. The memcpy is ~20x faster than the current JUMPDEST analysis, but this increases the memory footprint.

The number 33 is because:

  • 32 bytes are needed in case of incomplete PUSH32 at the end of the bytecode,
  • additional one zero byte is needed to work as STOP instruction.

This allows implementing 2 optimizations straight away:

  • remove the "end of code" check from the main loop (replace with "infinite" loop),
  • remove the "end of code" check from PUSH instructions implementations.

It would be nice for clients to load code this way.

2. Optimize PUSH with out-of-bound loads

Requires: 1.

Instead of loading of exact number of bytes, load more and mask them. E.g. for PUSH7 load 8 bytes and mask with 0xffffffffffffff.

3. Implement JUMPDEST analysis with AVX instructions.

Analyze more than single byte at a time.

4. Implement interpreter dispatch with "computed goto".

5. Create custom instruction table.

Currently Baseline uses instruction tables from EVMC. This is not efficient it has to lookup information at least in two tables. Custom table can be created using information from evmone::instr.

Draft: https://github.com/ethereum/evmone/tree/baseline_instruction_table.

6. Combine instruction requirements checks

Combine gas cost check and stack overflow/underflow checks so that we have single if jumping to [[unlikely]] error handling section (where you can figure out exact error code).

7. Inline stack requirements checks

There are 4 groups of instructions:

  • no stack violation possible
  • can cause stack underflow
  • can cause stack overflow
  • DUPs

Most of the time you don't need all checks. And the check latency can be hidden in the instruction implementation.

8. Inline gas cost check

Requires: 7.

Same as 7 plus also inline gas cost check. For some instructions this cost is constant, for some it depends on EVM revision. Also some instructions have "variadic" cost so they are check gas cost condition anyway.

Improve from_hex()

  • Make it case insensitive (support A-F chars).
  • Throw exception on invalid input
  • Add unit tests

Bechmark suite changes

  1. Fix the extension of files with runtime code, e.g. .code or .evm. Currently all files expect *.inputs are assumed to contain code. This makes it inconvenient to have other files like solidity sources, README, etc.
  2. Add new file extension for initcode. The benchmark tools should first create a contract with this initcode and benchmark the deployed code. This should work the same as evmc run --create.
  3. Split inputs into individual files *.NAME.input and *.NAME.expected. With this change it is easier to use inputs in other tools, e.g.
    evmc run sha1.code --input sha1.empty.input | grep  "Output:.*$(cat sha1.empty.expected)"
  4. An input file should be required for execution benchmark.

Bump CMake version required

We are using a feature that allows to install() targets from subdirs. Find in which version it was added and bump the CMake version required accordingly.

Create instruction tables for evmone

Create modified version of evmc::instructions:

  • no -1 cost for undefined instructions,
  • stack_change instead of returned_items,
  • flags for:
    • small push,
    • large push,
    • terminators,
    • access to gas counter.

Add homebrew package

This could be useful as a preparation for geth or other use cases to make it simple for users' to acquire evmone.

@chfast what do you think?

Re-evaluate the block_analysis struct

The #144 introduces the block_analysis which keeps the basic block data as ints later compressed to smaller block_info. Therefore, the value clapping happens only once per block. However, the commit where it was introduced causes a slowdown (there might be other reasons for it, like the block vector growing).

Try using smaller types in block_analysis or inherit from block_info.

Comparing bin/evmone-bench-master to bin/evmone-bench
Benchmark                                          Time             CPU      Time Old      Time New       CPU Old       CPU New
-------------------------------------------------------------------------------------------------------------------------------
blake2b_huff/analysis                           +0.0165         +0.0165            35            35            35            35
blake2b_shifts/analysis                         +0.0153         +0.0153            19            19            19            19
sha1_divs/analysis                              +0.0470         +0.0470             4             4             4             4
sha1_shifts/analysis                            +0.0084         +0.0084             3             3             3             3
weierstrudel/analysis                           +0.0662         +0.0662            42            45            42            45
micro/loop_with_many_jumpdests/analysis         +0.0502         +0.0502           323           339           323           339

evmone-unittests.exe silently stops executing when exception is thrown

When an EVMOne test triggers an exception, evmone-unittests.exe will stop executing without logging an error or exception message so the user has no indication as to what caused the failure or that a test even failed. Note that this is also true if one runs evmone-unittests.exe in a debugger - the process will suddenly exit. It would be nice for both an exception message and call stack to be logged when an exception is detected during test execution.

Optimize the "dispatch table"

Currently, in the analysis phase we prepare a table where for each instruction in the code we store:

  • pointer to function implementing the instruction (8 bytes)
  • optional imminent value (4 bytes)
  • block index (4 bytes)

Issues:

  1. The imminent value slot is only used by some instructions.
  2. Moreover, 4 bytes is not enough for some instructions as imminent value, then it is an index in an additional memory where the bigger value is stored.
  3. Block index is only used for the first instructions in blocks, so usually not used.

Proposed changes:

  1. Place the optional imminent values after the pointer to the instruction function, as many of 8 byte multiplies as required. The instruction implementation is responsible for moving the next instruction pointer (PC) after the imminent value.
  2. Instead of string block index for every instruction, insert "BLOCK_START" virtual instruction that will check block before processing with the execution.

Merge instruction metrics and function pointers tables

The #158 proposes new better instruction metrics table. But evmone also uses a table with function pointers to instruction implementations that is identical in structure. If both are combined, we will get better cache locality - single 16 bytes fetch for each instruction during analysis.

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.