mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-07-22 12:46:44 +00:00
feat: port cosmos patch to 1.16 (#7)
* Eric/add workflows (#5) * Add workflows * Don't change this * Merge patches onto upstream geth 1.15 (#4) * Merge patches onto upstream geth 1.15 * Fix test failures * Tests pass * Update go version in CI * Update go version in CI in all places * Problem: precompile address don't match (#6) Solution: - fix addresses and add validation rule * fix build * fix test --------- Co-authored-by: Eric Warehime <eric.warehime@gmail.com>
This commit is contained in:
parent
dd1ebac117
commit
2fc7571efa
25 changed files with 1167 additions and 120 deletions
33
.github/workflows/build.yml
vendored
Normal file
33
.github/workflows/build.yml
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
name: Build
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
cleanup-runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rokroskar/workflow-run-cleanup-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/main'"
|
||||
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.23
|
||||
check-latest: true
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- run: |
|
||||
make build
|
||||
if: env.GIT_DIFF
|
||||
28
.github/workflows/dependencies.yml
vendored
Normal file
28
.github/workflows/dependencies.yml
vendored
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
name: "Dependency Review"
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.23
|
||||
check-latest: true
|
||||
- name: "Checkout Repository"
|
||||
uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: "Dependency Review"
|
||||
uses: actions/dependency-review-action@v3
|
||||
if: env.GIT_DIFF
|
||||
- name: "Go vulnerability check"
|
||||
run: make vulncheck
|
||||
if: env.GIT_DIFF
|
||||
72
.github/workflows/lint.yml
vendored
Normal file
72
.github/workflows/lint.yml
vendored
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
name: Lint
|
||||
# Lint runs golangci-lint over the entire ethermint repository This workflow is
|
||||
# run on every pull request and push to main The `golangci` will pass without
|
||||
# running if no *.{go, mod, sum} files have been changed.
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
jobs:
|
||||
golangci:
|
||||
name: Run golangci-lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
# Required: setup-go, for all versions v3.0.0+ of golangci-lint
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.23
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- uses: golangci/golangci-lint-action@v3.3.1
|
||||
with:
|
||||
# Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version.
|
||||
version: latest
|
||||
args: --timeout 10m
|
||||
github-token: ${{ secrets.github_token }}
|
||||
# Check only if there are differences in the source code
|
||||
if: env.GIT_DIFF
|
||||
markdown-lint:
|
||||
name: Run markdown-lint
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
docs/**/*.md
|
||||
x/**/*.md
|
||||
README.md
|
||||
- uses: nosborn/github-action-markdown-cli@v3.2.0
|
||||
with:
|
||||
files: .
|
||||
config_file: .markdownlint.yml
|
||||
ignore_path: .markdownlintignore
|
||||
# Check only if there are differences in the source code
|
||||
if: env.GIT_DIFF
|
||||
gomod2nix:
|
||||
name: Check gomod2nix.toml file is up to date
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2.3.4
|
||||
- uses: cachix/install-nix-action@v18
|
||||
- uses: cachix/cachix-action@v12
|
||||
with:
|
||||
name: ethermint
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.py
|
||||
- name: run gomod2nix
|
||||
run: |
|
||||
nix run -f ./nix gomod2nix
|
||||
git diff --no-ext-diff --exit-code
|
||||
if: env.GIT_DIFF
|
||||
29
.github/workflows/markdown-links.yml
vendored
Normal file
29
.github/workflows/markdown-links.yml
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
name: Check Markdown links
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.md'
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
markdown-link-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
id: git_diff
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.md
|
||||
- uses: gaurav-nelson/github-action-markdown-link-check@master
|
||||
with:
|
||||
folder-path: "docs"
|
||||
check-modified-files-only: "yes"
|
||||
use-quiet-mode: "yes"
|
||||
base-branch: "main"
|
||||
config-file: "mlc_config.json"
|
||||
if: env.GIT_DIFF
|
||||
37
.github/workflows/security.yml
vendored
Normal file
37
.github/workflows/security.yml
vendored
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
name: Run Gosec
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
jobs:
|
||||
Gosec:
|
||||
permissions:
|
||||
security-events: write
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GO111MODULE: on
|
||||
steps:
|
||||
- name: Checkout Source
|
||||
uses: actions/checkout@v3
|
||||
- name: Get Diff
|
||||
uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/*.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Run Gosec Security Scanner
|
||||
uses: cosmos/gosec@master
|
||||
with:
|
||||
# we let the report trigger content trigger a failure using the GitHub Security features.
|
||||
args: "-no-fail -fmt sarif -out results.sarif ./..."
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
||||
- name: Upload SARIF file
|
||||
uses: github/codeql-action/upload-sarif@v2
|
||||
with:
|
||||
# Path to SARIF file relative to the root of the repository
|
||||
sarif_file: results.sarif
|
||||
if: "env.GIT_DIFF_FILTERED != ''"
|
||||
38
.github/workflows/super-linter.yml
vendored
Normal file
38
.github/workflows/super-linter.yml
vendored
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
# This workflow executes several linters on changed files based on languages used in your code base whenever
|
||||
# you push a code or open a pull request.
|
||||
#
|
||||
# You can adjust the behavior by modifying this file.
|
||||
# For more information, see:
|
||||
# https://github.com/github/super-linter
|
||||
---
|
||||
name: Lint Code Base
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["master"]
|
||||
pull_request:
|
||||
branches: ["master"]
|
||||
jobs:
|
||||
run-lint:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
# Full git history is needed to get a proper list of changed files within `super-linter`
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Lint Code Base
|
||||
uses: github/super-linter@v4
|
||||
env:
|
||||
LINTER_RULES_PATH: /
|
||||
YAML_CONFIG_FILE: .yamllint
|
||||
VALIDATE_ALL_CODEBASE: false
|
||||
MARKDOWN_CONFIG_FILE: .markdownlint.yml
|
||||
PROTOBUF_CONFIG_FILE: .protolint.yml
|
||||
VALIDATE_NATURAL_LANGUAGE: false
|
||||
VALIDATE_OPENAPI: false
|
||||
VALIDATE_JSCPD: false
|
||||
VALIDATE_GO: false
|
||||
DEFAULT_BRANCH: "master"
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
35
.github/workflows/test.yml
vendored
Normal file
35
.github/workflows/test.yml
vendored
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
name: Tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release/**
|
||||
|
||||
jobs:
|
||||
cleanup-runs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: rokroskar/workflow-run-cleanup-action@master
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
if: "!startsWith(github.ref, 'refs/tags/') && github.ref != 'refs/heads/master'"
|
||||
|
||||
test-all:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v3
|
||||
with:
|
||||
go-version: 1.23
|
||||
check-latest: true
|
||||
- uses: actions/checkout@v3
|
||||
- uses: technote-space/get-diff-action@v6.1.2
|
||||
with:
|
||||
PATTERNS: |
|
||||
**/**.go
|
||||
go.mod
|
||||
go.sum
|
||||
- name: Test and Create Coverage Report
|
||||
run: |
|
||||
make test all
|
||||
if: env.GIT_DIFF
|
||||
|
|
@ -486,7 +486,7 @@ func (st *stateTransition) execute() (*ExecutionResult, error) {
|
|||
// Execute the preparatory steps for state transition which includes:
|
||||
// - prepare accessList(post-berlin)
|
||||
// - reset transient storage(eip 1153)
|
||||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, vm.ActivePrecompiles(rules), msg.AccessList)
|
||||
st.state.Prepare(rules, msg.From, st.evm.Context.Coinbase, msg.To, st.evm.ActivePrecompiles(), msg.AccessList)
|
||||
|
||||
var (
|
||||
ret []byte
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ type Contract struct {
|
|||
|
||||
Gas uint64
|
||||
value *uint256.Int
|
||||
isPrecompile bool
|
||||
}
|
||||
|
||||
// NewContract returns a new contract environment for the execution of EVM.
|
||||
|
|
@ -62,6 +63,10 @@ func NewContract(caller common.Address, address common.Address, value *uint256.I
|
|||
}
|
||||
|
||||
func (c *Contract) validJumpdest(dest *uint256.Int) bool {
|
||||
if c.isPrecompile {
|
||||
return false
|
||||
}
|
||||
|
||||
udest, overflow := dest.Uint64WithOverflow()
|
||||
// PC cannot go beyond len(code) and certainly can't be bigger than 63bits.
|
||||
// Don't bother checking for JUMPDEST in that case.
|
||||
|
|
@ -78,6 +83,10 @@ func (c *Contract) validJumpdest(dest *uint256.Int) bool {
|
|||
// isCode returns true if the provided PC location is an actual opcode, as
|
||||
// opposed to a data-segment following a PUSHN operation.
|
||||
func (c *Contract) isCode(udest uint64) bool {
|
||||
if c.isPrecompile {
|
||||
return false
|
||||
}
|
||||
|
||||
// Do we already have an analysis laying around?
|
||||
if c.analysis != nil {
|
||||
return c.analysis.codeSegment(udest)
|
||||
|
|
@ -160,6 +169,9 @@ func (c *Contract) Value() *uint256.Int {
|
|||
|
||||
// SetCallCode sets the code of the contract,
|
||||
func (c *Contract) SetCallCode(hash common.Hash, code []byte) {
|
||||
if c.isPrecompile {
|
||||
return
|
||||
}
|
||||
c.Code = code
|
||||
c.CodeHash = hash
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ import (
|
|||
"github.com/ethereum/go-ethereum/crypto/kzg4844"
|
||||
"github.com/ethereum/go-ethereum/crypto/secp256r1"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/holiman/uint256"
|
||||
"golang.org/x/crypto/ripemd160"
|
||||
)
|
||||
|
||||
|
|
@ -44,8 +45,9 @@ import (
|
|||
// requires a deterministic gas count based on the input size of the Run method of the
|
||||
// contract.
|
||||
type PrecompiledContract interface {
|
||||
Address() common.Address
|
||||
RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
|
||||
Run(input []byte) ([]byte, error) // Run runs the precompiled contract
|
||||
Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) // Run runs the precompiled contract
|
||||
}
|
||||
|
||||
// PrecompiledContracts contains the precompiled contracts supported at the given fork.
|
||||
|
|
@ -257,48 +259,71 @@ func ActivePrecompiles(rules params.Rules) []common.Address {
|
|||
// - the returned bytes,
|
||||
// - the _remaining_ gas,
|
||||
// - any error that occurred
|
||||
func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
||||
func (evm *EVM) RunPrecompiledContract(
|
||||
p PrecompiledContract,
|
||||
caller common.Address,
|
||||
input []byte,
|
||||
suppliedGas uint64,
|
||||
value *uint256.Int,
|
||||
readOnly bool,
|
||||
logger *tracing.Hooks,
|
||||
) (ret []byte, remainingGas uint64, err error) {
|
||||
return runPrecompiledContract(evm, p, caller, input, suppliedGas, value, readOnly, logger)
|
||||
}
|
||||
|
||||
func runPrecompiledContract(evm *EVM, p PrecompiledContract, caller common.Address, input []byte, suppliedGas uint64,
|
||||
value *uint256.Int, readOnly bool, logger *tracing.Hooks) (ret []byte, remainingGas uint64, err error) {
|
||||
addrCopy := p.Address()
|
||||
inputCopy := make([]byte, len(input))
|
||||
copy(inputCopy, input)
|
||||
|
||||
contract := NewPrecompile(caller, addrCopy, value, suppliedGas)
|
||||
contract.Input = inputCopy
|
||||
|
||||
gasCost := p.RequiredGas(input)
|
||||
if suppliedGas < gasCost {
|
||||
if !contract.UseGas(gasCost, logger, tracing.GasChangeCallPrecompiledContract) {
|
||||
return nil, 0, ErrOutOfGas
|
||||
}
|
||||
if logger != nil && logger.OnGasChange != nil {
|
||||
logger.OnGasChange(suppliedGas, suppliedGas-gasCost, tracing.GasChangeCallPrecompiledContract)
|
||||
}
|
||||
suppliedGas -= gasCost
|
||||
output, err := p.Run(input)
|
||||
return output, suppliedGas, err
|
||||
|
||||
output, err := p.Run(evm, contract, readOnly)
|
||||
return output, contract.Gas, err
|
||||
}
|
||||
|
||||
// ecrecover implemented as a native contract.
|
||||
type ecrecover struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (ecrecover) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{1})
|
||||
}
|
||||
|
||||
func (c *ecrecover) RequiredGas(input []byte) uint64 {
|
||||
return params.EcrecoverGas
|
||||
}
|
||||
|
||||
func (c *ecrecover) Run(input []byte) ([]byte, error) {
|
||||
func (c *ecrecover) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
const ecRecoverInputLength = 128
|
||||
|
||||
input = common.RightPadBytes(input, ecRecoverInputLength)
|
||||
contract.Input = common.RightPadBytes(contract.Input, ecRecoverInputLength)
|
||||
// "input" is (hash, v, r, s), each 32 bytes
|
||||
// but for ecrecover we want (r, s, v)
|
||||
|
||||
r := new(big.Int).SetBytes(input[64:96])
|
||||
s := new(big.Int).SetBytes(input[96:128])
|
||||
v := input[63] - 27
|
||||
r := new(big.Int).SetBytes(contract.Input[64:96])
|
||||
s := new(big.Int).SetBytes(contract.Input[96:128])
|
||||
v := contract.Input[63] - 27
|
||||
|
||||
// tighter sig s values input homestead only apply to tx sigs
|
||||
if !allZero(input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
|
||||
if !allZero(contract.Input[32:63]) || !crypto.ValidateSignatureValues(v, r, s, false) {
|
||||
return nil, nil
|
||||
}
|
||||
// We must make sure not to modify the 'input', so placing the 'v' along with
|
||||
// the signature needs to be done on a new allocation
|
||||
sig := make([]byte, 65)
|
||||
copy(sig, input[64:128])
|
||||
copy(sig, contract.Input[64:128])
|
||||
sig[64] = v
|
||||
// v needs to be at the end for libsecp256k1
|
||||
pubKey, err := crypto.Ecrecover(input[:32], sig)
|
||||
pubKey, err := crypto.Ecrecover(contract.Input[:32], sig)
|
||||
// make sure the public key is a valid one
|
||||
if err != nil {
|
||||
return nil, nil
|
||||
|
|
@ -311,6 +336,12 @@ func (c *ecrecover) Run(input []byte) ([]byte, error) {
|
|||
// SHA256 implemented as a native contract.
|
||||
type sha256hash struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (sha256hash) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{2})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
|
|
@ -318,14 +349,21 @@ type sha256hash struct{}
|
|||
func (c *sha256hash) RequiredGas(input []byte) uint64 {
|
||||
return uint64(len(input)+31)/32*params.Sha256PerWordGas + params.Sha256BaseGas
|
||||
}
|
||||
func (c *sha256hash) Run(input []byte) ([]byte, error) {
|
||||
h := sha256.Sum256(input)
|
||||
|
||||
func (c *sha256hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
h := sha256.Sum256(contract.Input)
|
||||
return h[:], nil
|
||||
}
|
||||
|
||||
// RIPEMD160 implemented as a native contract.
|
||||
type ripemd160hash struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (ripemd160hash) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{3})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
|
|
@ -333,15 +371,22 @@ type ripemd160hash struct{}
|
|||
func (c *ripemd160hash) RequiredGas(input []byte) uint64 {
|
||||
return uint64(len(input)+31)/32*params.Ripemd160PerWordGas + params.Ripemd160BaseGas
|
||||
}
|
||||
func (c *ripemd160hash) Run(input []byte) ([]byte, error) {
|
||||
|
||||
func (c *ripemd160hash) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
ripemd := ripemd160.New()
|
||||
ripemd.Write(input)
|
||||
ripemd.Write(contract.Input)
|
||||
return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
|
||||
}
|
||||
|
||||
// data copy implemented as a native contract.
|
||||
type dataCopy struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (dataCopy) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{4})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
//
|
||||
// This method does not require any overflow checking as the input size gas costs
|
||||
|
|
@ -349,8 +394,9 @@ type dataCopy struct{}
|
|||
func (c *dataCopy) RequiredGas(input []byte) uint64 {
|
||||
return uint64(len(input)+31)/32*params.IdentityPerWordGas + params.IdentityBaseGas
|
||||
}
|
||||
func (c *dataCopy) Run(in []byte) ([]byte, error) {
|
||||
return common.CopyBytes(in), nil
|
||||
|
||||
func (c *dataCopy) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return common.CopyBytes(contract.Input), nil
|
||||
}
|
||||
|
||||
// bigModExp implements a native big integer exponential modular operation.
|
||||
|
|
@ -402,6 +448,12 @@ func modexpMultComplexity(x *big.Int) *big.Int {
|
|||
return x
|
||||
}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bigModExp) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{5})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
||||
var (
|
||||
|
|
@ -498,16 +550,16 @@ func (c *bigModExp) RequiredGas(input []byte) uint64 {
|
|||
return gas.Uint64()
|
||||
}
|
||||
|
||||
func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
||||
func (c *bigModExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
var (
|
||||
baseLen = new(big.Int).SetBytes(getData(input, 0, 32)).Uint64()
|
||||
expLen = new(big.Int).SetBytes(getData(input, 32, 32)).Uint64()
|
||||
modLen = new(big.Int).SetBytes(getData(input, 64, 32)).Uint64()
|
||||
baseLen = new(big.Int).SetBytes(getData(contract.Input, 0, 32)).Uint64()
|
||||
expLen = new(big.Int).SetBytes(getData(contract.Input, 32, 32)).Uint64()
|
||||
modLen = new(big.Int).SetBytes(getData(contract.Input, 64, 32)).Uint64()
|
||||
)
|
||||
if len(input) > 96 {
|
||||
input = input[96:]
|
||||
if len(contract.Input) > 96 {
|
||||
contract.Input = contract.Input[96:]
|
||||
} else {
|
||||
input = input[:0]
|
||||
contract.Input = contract.Input[:0]
|
||||
}
|
||||
// Handle a special case when both the base and mod length is zero
|
||||
if baseLen == 0 && modLen == 0 {
|
||||
|
|
@ -519,9 +571,9 @@ func (c *bigModExp) Run(input []byte) ([]byte, error) {
|
|||
}
|
||||
// Retrieve the operands and execute the exponentiation
|
||||
var (
|
||||
base = new(big.Int).SetBytes(getData(input, 0, baseLen))
|
||||
exp = new(big.Int).SetBytes(getData(input, baseLen, expLen))
|
||||
mod = new(big.Int).SetBytes(getData(input, baseLen+expLen, modLen))
|
||||
base = new(big.Int).SetBytes(getData(contract.Input, 0, baseLen))
|
||||
exp = new(big.Int).SetBytes(getData(contract.Input, baseLen, expLen))
|
||||
mod = new(big.Int).SetBytes(getData(contract.Input, baseLen+expLen, modLen))
|
||||
v []byte
|
||||
)
|
||||
switch {
|
||||
|
|
@ -577,26 +629,38 @@ func runBn256Add(input []byte) ([]byte, error) {
|
|||
// Istanbul consensus rules.
|
||||
type bn256AddIstanbul struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256AddIstanbul) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{6})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256AddIstanbul) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256AddGasIstanbul
|
||||
}
|
||||
|
||||
func (c *bn256AddIstanbul) Run(input []byte) ([]byte, error) {
|
||||
return runBn256Add(input)
|
||||
func (c *bn256AddIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256Add(contract.Input)
|
||||
}
|
||||
|
||||
// bn256AddByzantium implements a native elliptic curve point addition
|
||||
// conforming to Byzantium consensus rules.
|
||||
type bn256AddByzantium struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256AddByzantium) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{6})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256AddByzantium) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256AddGasByzantium
|
||||
}
|
||||
|
||||
func (c *bn256AddByzantium) Run(input []byte) ([]byte, error) {
|
||||
return runBn256Add(input)
|
||||
func (c *bn256AddByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256Add(contract.Input)
|
||||
}
|
||||
|
||||
// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
|
||||
|
|
@ -615,26 +679,38 @@ func runBn256ScalarMul(input []byte) ([]byte, error) {
|
|||
// multiplication conforming to Istanbul consensus rules.
|
||||
type bn256ScalarMulIstanbul struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256ScalarMulIstanbul) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{7})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256ScalarMulIstanbul) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256ScalarMulGasIstanbul
|
||||
}
|
||||
|
||||
func (c *bn256ScalarMulIstanbul) Run(input []byte) ([]byte, error) {
|
||||
return runBn256ScalarMul(input)
|
||||
func (c *bn256ScalarMulIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256ScalarMul(contract.Input)
|
||||
}
|
||||
|
||||
// bn256ScalarMulByzantium implements a native elliptic curve scalar
|
||||
// multiplication conforming to Byzantium consensus rules.
|
||||
type bn256ScalarMulByzantium struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256ScalarMulByzantium) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{7})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256ScalarMulByzantium) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256ScalarMulGasByzantium
|
||||
}
|
||||
|
||||
func (c *bn256ScalarMulByzantium) Run(input []byte) ([]byte, error) {
|
||||
return runBn256ScalarMul(input)
|
||||
func (c *bn256ScalarMulByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256ScalarMul(contract.Input)
|
||||
}
|
||||
|
||||
var (
|
||||
|
|
@ -683,30 +759,48 @@ func runBn256Pairing(input []byte) ([]byte, error) {
|
|||
// conforming to Istanbul consensus rules.
|
||||
type bn256PairingIstanbul struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256PairingIstanbul) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{8})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256PairingIstanbul) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256PairingBaseGasIstanbul + uint64(len(input)/192)*params.Bn256PairingPerPointGasIstanbul
|
||||
}
|
||||
|
||||
func (c *bn256PairingIstanbul) Run(input []byte) ([]byte, error) {
|
||||
return runBn256Pairing(input)
|
||||
func (c *bn256PairingIstanbul) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256Pairing(contract.Input)
|
||||
}
|
||||
|
||||
// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
|
||||
// conforming to Byzantium consensus rules.
|
||||
type bn256PairingByzantium struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bn256PairingByzantium) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{8})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bn256PairingByzantium) RequiredGas(input []byte) uint64 {
|
||||
return params.Bn256PairingBaseGasByzantium + uint64(len(input)/192)*params.Bn256PairingPerPointGasByzantium
|
||||
}
|
||||
|
||||
func (c *bn256PairingByzantium) Run(input []byte) ([]byte, error) {
|
||||
return runBn256Pairing(input)
|
||||
func (c *bn256PairingByzantium) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
return runBn256Pairing(contract.Input)
|
||||
}
|
||||
|
||||
type blake2F struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (blake2F) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{9})
|
||||
}
|
||||
|
||||
func (c *blake2F) RequiredGas(input []byte) uint64 {
|
||||
// If the input is malformed, we can't calculate the gas, return 0 and let the
|
||||
// actual call choke and fault.
|
||||
|
|
@ -727,18 +821,18 @@ var (
|
|||
errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
|
||||
)
|
||||
|
||||
func (c *blake2F) Run(input []byte) ([]byte, error) {
|
||||
func (c *blake2F) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Make sure the input is valid (correct length and final flag)
|
||||
if len(input) != blake2FInputLength {
|
||||
if len(contract.Input) != blake2FInputLength {
|
||||
return nil, errBlake2FInvalidInputLength
|
||||
}
|
||||
if input[212] != blake2FNonFinalBlockBytes && input[212] != blake2FFinalBlockBytes {
|
||||
if contract.Input[212] != blake2FNonFinalBlockBytes && contract.Input[212] != blake2FFinalBlockBytes {
|
||||
return nil, errBlake2FInvalidFinalFlag
|
||||
}
|
||||
// Parse the input into the Blake2b call parameters
|
||||
var (
|
||||
rounds = binary.BigEndian.Uint32(input[0:4])
|
||||
final = input[212] == blake2FFinalBlockBytes
|
||||
rounds = binary.BigEndian.Uint32(contract.Input[0:4])
|
||||
final = contract.Input[212] == blake2FFinalBlockBytes
|
||||
|
||||
h [8]uint64
|
||||
m [16]uint64
|
||||
|
|
@ -746,14 +840,14 @@ func (c *blake2F) Run(input []byte) ([]byte, error) {
|
|||
)
|
||||
for i := 0; i < 8; i++ {
|
||||
offset := 4 + i*8
|
||||
h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
|
||||
h[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8])
|
||||
}
|
||||
for i := 0; i < 16; i++ {
|
||||
offset := 68 + i*8
|
||||
m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
|
||||
m[i] = binary.LittleEndian.Uint64(contract.Input[offset : offset+8])
|
||||
}
|
||||
t[0] = binary.LittleEndian.Uint64(input[196:204])
|
||||
t[1] = binary.LittleEndian.Uint64(input[204:212])
|
||||
t[0] = binary.LittleEndian.Uint64(contract.Input[196:204])
|
||||
t[1] = binary.LittleEndian.Uint64(contract.Input[204:212])
|
||||
|
||||
// Execute the compression function, extract and return the result
|
||||
blake2b.F(&h, m, t, final, rounds)
|
||||
|
|
@ -776,27 +870,33 @@ var (
|
|||
// bls12381G1Add implements EIP-2537 G1Add precompile.
|
||||
type bls12381G1Add struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381G1Add) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0b})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381G1Add) RequiredGas(input []byte) uint64 {
|
||||
return params.Bls12381G1AddGas
|
||||
}
|
||||
|
||||
func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381G1Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 G1Add precompile.
|
||||
// > G1 addition call expects `256` bytes as an input that is interpreted as byte concatenation of two G1 points (`128` bytes each).
|
||||
// > Output is an encoding of addition operation result - single G1 point (`128` bytes).
|
||||
if len(input) != 256 {
|
||||
if len(contract.Input) != 256 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
var err error
|
||||
var p0, p1 *bls12381.G1Affine
|
||||
|
||||
// Decode G1 point p_0
|
||||
if p0, err = decodePointG1(input[:128]); err != nil {
|
||||
if p0, err = decodePointG1(contract.Input[:128]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Decode G1 point p_1
|
||||
if p1, err = decodePointG1(input[128:]); err != nil {
|
||||
if p1, err = decodePointG1(contract.Input[128:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -812,6 +912,12 @@ func (c *bls12381G1Add) Run(input []byte) ([]byte, error) {
|
|||
// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
|
||||
type bls12381G1MultiExp struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381G1MultiExp) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0c})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
|
||||
// Calculate G1 point, scalar value pair length
|
||||
|
|
@ -831,12 +937,12 @@ func (c *bls12381G1MultiExp) RequiredGas(input []byte) uint64 {
|
|||
return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
|
||||
}
|
||||
|
||||
func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381G1MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 G1MultiExp precompile.
|
||||
// G1 multiplication call expects `160*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
|
||||
// Output is an encoding of multiexponentiation operation result - single G1 point (`128` bytes).
|
||||
k := len(input) / 160
|
||||
if len(input) == 0 || len(input)%160 != 0 {
|
||||
k := len(contract.Input) / 160
|
||||
if len(contract.Input) == 0 || len(contract.Input)%160 != 0 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
points := make([]bls12381.G1Affine, k)
|
||||
|
|
@ -847,7 +953,7 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
|||
off := 160 * i
|
||||
t0, t1, t2 := off, off+128, off+160
|
||||
// Decode G1 point
|
||||
p, err := decodePointG1(input[t0:t1])
|
||||
p, err := decodePointG1(contract.Input[t0:t1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -858,7 +964,7 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
|||
}
|
||||
points[i] = *p
|
||||
// Decode scalar value
|
||||
scalars[i] = *new(fr.Element).SetBytes(input[t1:t2])
|
||||
scalars[i] = *new(fr.Element).SetBytes(contract.Input[t1:t2])
|
||||
}
|
||||
|
||||
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
|
||||
|
|
@ -872,27 +978,33 @@ func (c *bls12381G1MultiExp) Run(input []byte) ([]byte, error) {
|
|||
// bls12381G2Add implements EIP-2537 G2Add precompile.
|
||||
type bls12381G2Add struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381G2Add) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0d})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381G2Add) RequiredGas(input []byte) uint64 {
|
||||
return params.Bls12381G2AddGas
|
||||
}
|
||||
|
||||
func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381G2Add) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 G2Add precompile.
|
||||
// > G2 addition call expects `512` bytes as an input that is interpreted as byte concatenation of two G2 points (`256` bytes each).
|
||||
// > Output is an encoding of addition operation result - single G2 point (`256` bytes).
|
||||
if len(input) != 512 {
|
||||
if len(contract.Input) != 512 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
var err error
|
||||
var p0, p1 *bls12381.G2Affine
|
||||
|
||||
// Decode G2 point p_0
|
||||
if p0, err = decodePointG2(input[:256]); err != nil {
|
||||
if p0, err = decodePointG2(contract.Input[:256]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Decode G2 point p_1
|
||||
if p1, err = decodePointG2(input[256:]); err != nil {
|
||||
if p1, err = decodePointG2(contract.Input[256:]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
|
|
@ -909,6 +1021,12 @@ func (c *bls12381G2Add) Run(input []byte) ([]byte, error) {
|
|||
// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
|
||||
type bls12381G2MultiExp struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381G2MultiExp) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0e})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
|
||||
// Calculate G2 point, scalar value pair length
|
||||
|
|
@ -928,12 +1046,12 @@ func (c *bls12381G2MultiExp) RequiredGas(input []byte) uint64 {
|
|||
return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
|
||||
}
|
||||
|
||||
func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381G2MultiExp) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 G2MultiExp precompile logic
|
||||
// > G2 multiplication call expects `288*k` bytes as an input that is interpreted as byte concatenation of `k` slices each of them being a byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
|
||||
// > Output is an encoding of multiexponentiation operation result - single G2 point (`256` bytes).
|
||||
k := len(input) / 288
|
||||
if len(input) == 0 || len(input)%288 != 0 {
|
||||
k := len(contract.Input) / 288
|
||||
if len(contract.Input) == 0 || len(contract.Input)%288 != 0 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
points := make([]bls12381.G2Affine, k)
|
||||
|
|
@ -944,7 +1062,7 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
|||
off := 288 * i
|
||||
t0, t1, t2 := off, off+256, off+288
|
||||
// Decode G2 point
|
||||
p, err := decodePointG2(input[t0:t1])
|
||||
p, err := decodePointG2(contract.Input[t0:t1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -955,7 +1073,7 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
|||
}
|
||||
points[i] = *p
|
||||
// Decode scalar value
|
||||
scalars[i] = *new(fr.Element).SetBytes(input[t1:t2])
|
||||
scalars[i] = *new(fr.Element).SetBytes(contract.Input[t1:t2])
|
||||
}
|
||||
|
||||
// Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
|
||||
|
|
@ -969,20 +1087,26 @@ func (c *bls12381G2MultiExp) Run(input []byte) ([]byte, error) {
|
|||
// bls12381Pairing implements EIP-2537 Pairing precompile.
|
||||
type bls12381Pairing struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381Pairing) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0f})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381Pairing) RequiredGas(input []byte) uint64 {
|
||||
return params.Bls12381PairingBaseGas + uint64(len(input)/384)*params.Bls12381PairingPerPairGas
|
||||
}
|
||||
|
||||
func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381Pairing) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 Pairing precompile logic.
|
||||
// > Pairing call expects `384*k` bytes as an inputs that is interpreted as byte concatenation of `k` slices. Each slice has the following structure:
|
||||
// > - `128` bytes of G1 point encoding
|
||||
// > - `256` bytes of G2 point encoding
|
||||
// > Output is a `32` bytes where last single byte is `0x01` if pairing result is equal to multiplicative identity in a pairing target field and `0x00` otherwise
|
||||
// > (which is equivalent of Big Endian encoding of Solidity values `uint256(1)` and `uin256(0)` respectively).
|
||||
k := len(input) / 384
|
||||
if len(input) == 0 || len(input)%384 != 0 {
|
||||
k := len(contract.Input) / 384
|
||||
if len(contract.Input) == 0 || len(contract.Input)%384 != 0 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
|
||||
|
|
@ -997,12 +1121,12 @@ func (c *bls12381Pairing) Run(input []byte) ([]byte, error) {
|
|||
t0, t1, t2 := off, off+128, off+384
|
||||
|
||||
// Decode G1 point
|
||||
p1, err := decodePointG1(input[t0:t1])
|
||||
p1, err := decodePointG1(contract.Input[t0:t1])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Decode G2 point
|
||||
p2, err := decodePointG2(input[t1:t2])
|
||||
p2, err := decodePointG2(contract.Input[t1:t2])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1121,21 +1245,27 @@ func encodePointG2(p *bls12381.G2Affine) []byte {
|
|||
// bls12381MapG1 implements EIP-2537 MapG1 precompile.
|
||||
type bls12381MapG1 struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381MapG1) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x10})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381MapG1) RequiredGas(input []byte) uint64 {
|
||||
return params.Bls12381MapG1Gas
|
||||
}
|
||||
|
||||
func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381MapG1) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 Map_To_G1 precompile.
|
||||
// > Field-to-curve call expects an `64` bytes input that is interpreted as an element of the base field.
|
||||
// > Output of this call is `128` bytes and is G1 point following respective encoding rules.
|
||||
if len(input) != 64 {
|
||||
if len(contract.Input) != 64 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
|
||||
// Decode input field element
|
||||
fe, err := decodeBLS12381FieldElement(input)
|
||||
fe, err := decodeBLS12381FieldElement(contract.Input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1150,25 +1280,31 @@ func (c *bls12381MapG1) Run(input []byte) ([]byte, error) {
|
|||
// bls12381MapG2 implements EIP-2537 MapG2 precompile.
|
||||
type bls12381MapG2 struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (bls12381MapG2) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x11})
|
||||
}
|
||||
|
||||
// RequiredGas returns the gas required to execute the pre-compiled contract.
|
||||
func (c *bls12381MapG2) RequiredGas(input []byte) uint64 {
|
||||
return params.Bls12381MapG2Gas
|
||||
}
|
||||
|
||||
func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
|
||||
func (c *bls12381MapG2) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
// Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
|
||||
// > Field-to-curve call expects an `128` bytes input that is interpreted as an element of the quadratic extension field.
|
||||
// > Output of this call is `256` bytes and is G2 point following respective encoding rules.
|
||||
if len(input) != 128 {
|
||||
if len(contract.Input) != 128 {
|
||||
return nil, errBLS12381InvalidInputLength
|
||||
}
|
||||
|
||||
// Decode input field element
|
||||
c0, err := decodeBLS12381FieldElement(input[:64])
|
||||
c0, err := decodeBLS12381FieldElement(contract.Input[:64])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c1, err := decodeBLS12381FieldElement(input[64:])
|
||||
c1, err := decodeBLS12381FieldElement(contract.Input[64:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
|
@ -1183,6 +1319,12 @@ func (c *bls12381MapG2) Run(input []byte) ([]byte, error) {
|
|||
// kzgPointEvaluation implements the EIP-4844 point evaluation precompile.
|
||||
type kzgPointEvaluation struct{}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (kzgPointEvaluation) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x0a})
|
||||
}
|
||||
|
||||
// RequiredGas estimates the gas required for running the point evaluation precompile.
|
||||
func (b *kzgPointEvaluation) RequiredGas(input []byte) uint64 {
|
||||
return params.BlobTxPointEvaluationPrecompileGas
|
||||
|
|
@ -1201,33 +1343,33 @@ var (
|
|||
)
|
||||
|
||||
// Run executes the point evaluation precompile.
|
||||
func (b *kzgPointEvaluation) Run(input []byte) ([]byte, error) {
|
||||
if len(input) != blobVerifyInputLength {
|
||||
func (b *kzgPointEvaluation) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
if len(contract.Input) != blobVerifyInputLength {
|
||||
return nil, errBlobVerifyInvalidInputLength
|
||||
}
|
||||
// versioned hash: first 32 bytes
|
||||
var versionedHash common.Hash
|
||||
copy(versionedHash[:], input[:])
|
||||
copy(versionedHash[:], contract.Input[:])
|
||||
|
||||
var (
|
||||
point kzg4844.Point
|
||||
claim kzg4844.Claim
|
||||
)
|
||||
// Evaluation point: next 32 bytes
|
||||
copy(point[:], input[32:])
|
||||
copy(point[:], contract.Input[32:])
|
||||
// Expected output: next 32 bytes
|
||||
copy(claim[:], input[64:])
|
||||
copy(claim[:], contract.Input[64:])
|
||||
|
||||
// input kzg point: next 48 bytes
|
||||
var commitment kzg4844.Commitment
|
||||
copy(commitment[:], input[96:])
|
||||
copy(commitment[:], contract.Input[96:])
|
||||
if kZGToVersionedHash(commitment) != versionedHash {
|
||||
return nil, errBlobVerifyMismatchedVersion
|
||||
}
|
||||
|
||||
// Proof: next 48 bytes
|
||||
var proof kzg4844.Proof
|
||||
copy(proof[:], input[144:])
|
||||
copy(proof[:], contract.Input[144:])
|
||||
|
||||
if err := kzg4844.VerifyProof(commitment, point, claim, proof); err != nil {
|
||||
return nil, fmt.Errorf("%w: %v", errBlobVerifyKZGProof, err)
|
||||
|
|
@ -1254,16 +1396,16 @@ func (c *p256Verify) RequiredGas(input []byte) uint64 {
|
|||
}
|
||||
|
||||
// Run executes the precompiled contract with given 160 bytes of param, returning the output and the used gas
|
||||
func (c *p256Verify) Run(input []byte) ([]byte, error) {
|
||||
func (c *p256Verify) Run(evm *EVM, contract *Contract, readonly bool) ([]byte, error) {
|
||||
const p256VerifyInputLength = 160
|
||||
if len(input) != p256VerifyInputLength {
|
||||
if len(contract.Input) != p256VerifyInputLength {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Extract hash, r, s, x, y from the input.
|
||||
hash := input[0:32]
|
||||
r, s := new(big.Int).SetBytes(input[32:64]), new(big.Int).SetBytes(input[64:96])
|
||||
x, y := new(big.Int).SetBytes(input[96:128]), new(big.Int).SetBytes(input[128:160])
|
||||
hash := contract.Input[0:32]
|
||||
r, s := new(big.Int).SetBytes(contract.Input[32:64]), new(big.Int).SetBytes(contract.Input[64:96])
|
||||
x, y := new(big.Int).SetBytes(contract.Input[96:128]), new(big.Int).SetBytes(contract.Input[128:160])
|
||||
|
||||
// Verify the signature.
|
||||
if secp256r1.Verify(hash, r, s, x, y) {
|
||||
|
|
@ -1271,3 +1413,9 @@ func (c *p256Verify) Run(input []byte) ([]byte, error) {
|
|||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Address defines the precompiled contract address. This MUST match the address
|
||||
// set in the precompiled contract map.
|
||||
func (c *p256Verify) Address() common.Address {
|
||||
return common.BytesToAddress([]byte{0x1, 0x00})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ import (
|
|||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
func FuzzPrecompiledContracts(f *testing.F) {
|
||||
|
|
@ -36,7 +37,8 @@ func FuzzPrecompiledContracts(f *testing.F) {
|
|||
return
|
||||
}
|
||||
inWant := string(input)
|
||||
RunPrecompiledContract(p, input, gas, nil)
|
||||
|
||||
runPrecompiledContract(nil, p, common.Address{}, input, gas, new(uint256.Int), false, nil)
|
||||
if inHave := string(input); inWant != inHave {
|
||||
t.Errorf("Precompiled %v modified input data", a)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// precompiledTest defines the input/output pairs for precompiled contract tests.
|
||||
|
|
@ -99,7 +100,7 @@ func testPrecompiled(addr string, test precompiledTest, t *testing.T) {
|
|||
in := common.Hex2Bytes(test.Input)
|
||||
gas := p.RequiredGas(in)
|
||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
||||
if res, _, err := RunPrecompiledContract(p, in, gas, nil); err != nil {
|
||||
if res, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil); err != nil {
|
||||
t.Error(err)
|
||||
} else if common.Bytes2Hex(res) != test.Expected {
|
||||
t.Errorf("Expected %v, got %v", test.Expected, common.Bytes2Hex(res))
|
||||
|
|
@ -121,7 +122,7 @@ func testPrecompiledOOG(addr string, test precompiledTest, t *testing.T) {
|
|||
gas := p.RequiredGas(in) - 1
|
||||
|
||||
t.Run(fmt.Sprintf("%s-Gas=%d", test.Name, gas), func(t *testing.T) {
|
||||
_, _, err := RunPrecompiledContract(p, in, gas, nil)
|
||||
_, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil)
|
||||
if err.Error() != "out of gas" {
|
||||
t.Errorf("Expected error [out of gas], got [%v]", err)
|
||||
}
|
||||
|
|
@ -138,7 +139,7 @@ func testPrecompiledFailure(addr string, test precompiledFailureTest, t *testing
|
|||
in := common.Hex2Bytes(test.Input)
|
||||
gas := p.RequiredGas(in)
|
||||
t.Run(test.Name, func(t *testing.T) {
|
||||
_, _, err := RunPrecompiledContract(p, in, gas, nil)
|
||||
_, _, err := runPrecompiledContract(nil, p, common.Address{}, in, gas, new(uint256.Int), false, nil)
|
||||
if err.Error() != test.ExpectedError {
|
||||
t.Errorf("Expected error [%v], got [%v]", test.ExpectedError, err)
|
||||
}
|
||||
|
|
@ -170,7 +171,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
|
|||
bench.ResetTimer()
|
||||
for i := 0; i < bench.N; i++ {
|
||||
copy(data, in)
|
||||
res, _, err = RunPrecompiledContract(p, data, reqGas, nil)
|
||||
res, _, err = runPrecompiledContract(nil, p, common.Address{}, in, reqGas, new(uint256.Int), false, nil)
|
||||
}
|
||||
bench.StopTimer()
|
||||
elapsed := uint64(time.Since(start))
|
||||
|
|
@ -182,7 +183,7 @@ func benchmarkPrecompiled(addr string, test precompiledTest, bench *testing.B) {
|
|||
// Keep it as uint64, multiply 100 to get two digit float later
|
||||
mgasps := (100 * 1000 * gasUsed) / elapsed
|
||||
bench.ReportMetric(float64(mgasps)/100, "mgas/s")
|
||||
//Check if it is correct
|
||||
// Check if it is correct
|
||||
if err != nil {
|
||||
bench.Error(err)
|
||||
return
|
||||
|
|
|
|||
30
core/vm/custom_contract.go
Normal file
30
core/vm/custom_contract.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
// AccountRef implements ContractRef.
|
||||
//
|
||||
// Account references are used during EVM initialisation and
|
||||
// its primary use is to fetch addresses. Removing this object
|
||||
// proves difficult because of the cached jump destinations which
|
||||
// are fetched from the parent contract (i.e. the caller), which
|
||||
// is a ContractRef.
|
||||
type AccountRef common.Address
|
||||
|
||||
// Address casts AccountRef to an Address
|
||||
func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
|
||||
|
||||
// NewPrecompile returns a new instance of a precompiled contract environment for the execution of EVM.
|
||||
func NewPrecompile(caller, address common.Address, value *uint256.Int, gas uint64) *Contract {
|
||||
c := NewContract(caller, address, value, gas, nil)
|
||||
c.isPrecompile = true
|
||||
return c
|
||||
}
|
||||
|
||||
// IsPrecompile returns true if the contract is a precompiled contract environment
|
||||
func (c *Contract) IsPrecompile() bool {
|
||||
return c.isPrecompile
|
||||
}
|
||||
66
core/vm/custom_contracts.go
Normal file
66
core/vm/custom_contracts.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// ActivePrecompiles returns the precompiles enabled with the current configuration.
|
||||
func (evm *EVM) ActivePrecompiles() []common.Address {
|
||||
addrs := make([]common.Address, len(evm.precompiles))
|
||||
i := 0
|
||||
for addr, _ := range evm.precompiles {
|
||||
addrs[i] = addr
|
||||
i++
|
||||
}
|
||||
return addrs
|
||||
}
|
||||
|
||||
// Precompile returns a precompiled contract for the given address. This
|
||||
// function returns false if the address is not a registered precompile.
|
||||
func (evm *EVM) Precompile(addr common.Address) (PrecompiledContract, bool) {
|
||||
p, ok := evm.precompiles[addr]
|
||||
return p, ok
|
||||
}
|
||||
|
||||
// WithPrecompiles sets the precompiled contracts and the slice of actives precompiles.
|
||||
// IMPORTANT: This function does NOT validate the precompiles provided to the EVM. The caller should
|
||||
// use the ValidatePrecompiles function for this purpose prior to calling WithPrecompiles.
|
||||
func (evm *EVM) WithPrecompiles(precompiles map[common.Address]PrecompiledContract) {
|
||||
evm.precompiles = precompiles
|
||||
}
|
||||
|
||||
// ValidatePrecompiles validates the precompile map against the active
|
||||
// precompile slice.
|
||||
// It returns an error if the precompiled contract map has a different length
|
||||
// than the slice of active contract addresses. This function also checks for
|
||||
// duplicates, invalid addresses and empty precompile contract instances.
|
||||
func ValidatePrecompiles(
|
||||
precompiles PrecompiledContracts,
|
||||
) error {
|
||||
dupActivePrecompiles := make(map[common.Address]bool)
|
||||
|
||||
for addr, precompile := range precompiles {
|
||||
if dupActivePrecompiles[addr] {
|
||||
return fmt.Errorf("duplicate active precompile: %s", addr)
|
||||
}
|
||||
|
||||
if precompile == nil {
|
||||
return fmt.Errorf("precompile contract cannot be nil: %s", addr)
|
||||
}
|
||||
|
||||
if bytes.Equal(addr.Bytes(), common.Address{}.Bytes()) {
|
||||
return fmt.Errorf("precompile cannot be the zero address: %s", addr)
|
||||
}
|
||||
|
||||
if !bytes.Equal(addr.Bytes(), precompile.Address().Bytes()) {
|
||||
return fmt.Errorf("precompile address mismatch: %s != %s", addr, precompile.Address())
|
||||
}
|
||||
|
||||
dupActivePrecompiles[addr] = true
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
151
core/vm/custom_eip.go
Normal file
151
core/vm/custom_eip.go
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
// OpCodeInfo contains information required to identify an EVM operation.
|
||||
type OpCodeInfo struct {
|
||||
Number OpCode
|
||||
Name string
|
||||
}
|
||||
|
||||
// Operation is an utility struct that wraps the private type
|
||||
// operation.
|
||||
type Operation struct {
|
||||
Op *operation
|
||||
}
|
||||
|
||||
// ExtendActivators allows to merge the go ethereum activators map
|
||||
// with additional custom activators.
|
||||
func ExtendActivators(eips map[int]func(*JumpTable)) error {
|
||||
// Catch early duplicated eip.
|
||||
keys := make([]int, 0, len(eips))
|
||||
for k := range eips {
|
||||
if ValidEip(k) {
|
||||
return fmt.Errorf("duplicate activation: %d is already present in %s", k, ActivateableEips())
|
||||
}
|
||||
keys = append(keys, k)
|
||||
}
|
||||
|
||||
// Sorting keys to ensure deterministic execution.
|
||||
sort.Ints(keys)
|
||||
|
||||
for _, k := range keys {
|
||||
activators[k] = eips[k]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetActivatorsEipNumbers returns the name of EIPs registered in
|
||||
// the activators map.
|
||||
// Used only in tests.
|
||||
func GetActivatorsEipNumbers() []int {
|
||||
keys := maps.Keys(activators)
|
||||
|
||||
sort.Ints(keys)
|
||||
return keys
|
||||
}
|
||||
|
||||
// ExtendOperations returns an instance of the new operation and register it in the list
|
||||
// of available ones.
|
||||
// Return an error if an operation with the same name is already present.
|
||||
// This function is used to prevent the overwrite of an already existent operation.
|
||||
func ExtendOperations(
|
||||
opInfo OpCodeInfo,
|
||||
execute executionFunc,
|
||||
constantGas uint64,
|
||||
dynamicGas gasFunc,
|
||||
minStack int,
|
||||
maxStack int,
|
||||
memorySize memorySizeFunc,
|
||||
) (*Operation, error) {
|
||||
opName := strings.ToUpper(strings.TrimSpace(opInfo.Name))
|
||||
if err := extendOpCodeStringLists(opInfo.Number, opName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
operation := newOperation(execute, constantGas, dynamicGas, minStack, maxStack, memorySize)
|
||||
op := &Operation{operation}
|
||||
|
||||
return op, nil
|
||||
}
|
||||
|
||||
// newOperation returns an instance of a new EVM operation.
|
||||
func newOperation(
|
||||
execute executionFunc,
|
||||
constantGas uint64,
|
||||
dynamicGas gasFunc,
|
||||
minStack int,
|
||||
maxStack int,
|
||||
memorySize memorySizeFunc,
|
||||
) *operation {
|
||||
return &operation{
|
||||
execute: execute,
|
||||
constantGas: constantGas,
|
||||
dynamicGas: dynamicGas,
|
||||
minStack: minStack,
|
||||
maxStack: maxStack,
|
||||
memorySize: memorySize,
|
||||
}
|
||||
}
|
||||
|
||||
// GetConstantGas return the constant gas used by the operation.
|
||||
func (o *operation) GetConstantGas() uint64 {
|
||||
return o.constantGas
|
||||
}
|
||||
|
||||
// SetExecute sets the execution function of the operation.
|
||||
func (o *operation) SetExecute(ef executionFunc) {
|
||||
o.execute = ef
|
||||
}
|
||||
|
||||
// SetConstantGas changes the constant gas of the operation.
|
||||
func (o *operation) SetConstantGas(gas uint64) {
|
||||
o.constantGas = gas
|
||||
}
|
||||
|
||||
// SetDynamicGas sets the dynamic gas function of the operation.
|
||||
func (o *operation) SetDynamicGas(gf gasFunc) {
|
||||
o.dynamicGas = gf
|
||||
}
|
||||
|
||||
// SetMinStack sets the minimum stack size required for the operation.
|
||||
func (o *operation) SetMinStack(minStack int) {
|
||||
o.minStack = minStack
|
||||
}
|
||||
|
||||
// SetMaxStack sets the maximum stack size for the operation.
|
||||
func (o *operation) SetMaxStack(maxStack int) {
|
||||
o.maxStack = maxStack
|
||||
}
|
||||
|
||||
// SetMemorySize sets the memory size function for the operation.
|
||||
func (o *operation) SetMemorySize(msf memorySizeFunc) {
|
||||
o.memorySize = msf
|
||||
}
|
||||
|
||||
// extendOpCodeStringLists updates the lists mapping opcode number to the name
|
||||
// and viceversa. Return an error if the key is already set.
|
||||
//
|
||||
// ASSUMPTION: no opcode is registered as an empty string.
|
||||
func extendOpCodeStringLists(newOpCode OpCode, newOpName string) error {
|
||||
opName := opCodeToString[newOpCode]
|
||||
if opName != "" {
|
||||
return fmt.Errorf("opcode %d already exists: %s", newOpCode, opName)
|
||||
}
|
||||
opNumber := stringToOp[newOpName]
|
||||
// We need to check against the STOP opcode name because we have to discriminate
|
||||
// between 0x00 of this opcode and the default value of an empty key.
|
||||
stopName := opCodeToString[STOP]
|
||||
if opNumber != 0x00 || newOpName == stopName {
|
||||
return fmt.Errorf("opcode with name %s already exists", newOpName)
|
||||
}
|
||||
opCodeToString[newOpCode] = newOpName
|
||||
stringToOp[newOpName] = newOpCode
|
||||
return nil
|
||||
}
|
||||
192
core/vm/custom_eip_test.go
Normal file
192
core/vm/custom_eip_test.go
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
package vm
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestExtendActivators(t *testing.T) {
|
||||
eips_snapshot := GetActivatorsEipNumbers()
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
newActivators map[int]func(*JumpTable)
|
||||
expPass bool
|
||||
errContains string
|
||||
postCheck func()
|
||||
}{
|
||||
{
|
||||
"success - nil new activators",
|
||||
nil,
|
||||
true,
|
||||
"",
|
||||
func() {
|
||||
eips := GetActivatorsEipNumbers()
|
||||
require.ElementsMatch(t, eips_snapshot, eips, "expected eips number to be equal")
|
||||
},
|
||||
},
|
||||
{
|
||||
"success - single new activator",
|
||||
map[int]func(*JumpTable){
|
||||
0o000: func(jt *JumpTable) {},
|
||||
},
|
||||
true,
|
||||
"",
|
||||
func() {
|
||||
eips := GetActivatorsEipNumbers()
|
||||
require.ElementsMatch(t, append(eips_snapshot, 0), eips, "expected eips number to be equal")
|
||||
},
|
||||
},
|
||||
{
|
||||
"success - multiple new activators",
|
||||
map[int]func(*JumpTable){
|
||||
0o001: func(jt *JumpTable) {},
|
||||
0o002: func(jt *JumpTable) {},
|
||||
},
|
||||
true,
|
||||
"",
|
||||
func() {
|
||||
eips := GetActivatorsEipNumbers()
|
||||
// since we are working with a global function, tests are not independent
|
||||
require.ElementsMatch(t, append(eips_snapshot, 0, 1, 2), eips,
|
||||
"expected eips number to be equal")
|
||||
},
|
||||
},
|
||||
{
|
||||
"fail - repeated activator",
|
||||
map[int]func(*JumpTable){
|
||||
3855: func(jt *JumpTable) {},
|
||||
},
|
||||
false,
|
||||
"",
|
||||
func() {
|
||||
eips := GetActivatorsEipNumbers()
|
||||
// since we are working with a global function, tests are not independent
|
||||
require.ElementsMatch(t, append(eips_snapshot, 0, 1, 2), eips,
|
||||
"expected eips number to be equal")
|
||||
},
|
||||
},
|
||||
{
|
||||
"fail - valid activator is not stored if a repeated is present",
|
||||
map[int]func(*JumpTable){
|
||||
0o003: func(jt *JumpTable) {},
|
||||
3855: func(jt *JumpTable) {},
|
||||
},
|
||||
false,
|
||||
"",
|
||||
func() {
|
||||
eips := GetActivatorsEipNumbers()
|
||||
// since we are working with a global function, tests are not independent
|
||||
require.ElementsMatch(t, append(eips_snapshot, 0o000, 0o001, 0o002), eips,
|
||||
"expected eips number to be equal")
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
err := ExtendActivators(tc.newActivators)
|
||||
if tc.expPass {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.errContains, "expected different error")
|
||||
}
|
||||
|
||||
tc.postCheck()
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddOperation(t *testing.T) {
|
||||
// Functions used to create an operation.
|
||||
customExecute := func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
|
||||
// no - op
|
||||
return nil, nil
|
||||
}
|
||||
customDynamicGas := func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
|
||||
// no-op
|
||||
return 0, nil
|
||||
}
|
||||
customMemorySize := func(stack *Stack) (uint64, bool) {
|
||||
// no-op
|
||||
return 0, false
|
||||
}
|
||||
|
||||
const (
|
||||
EXISTENT OpCode = STOP
|
||||
NEW OpCode = 0xf
|
||||
)
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
opName string
|
||||
opNumber OpCode
|
||||
expPass bool
|
||||
errContains string
|
||||
postCheck func()
|
||||
}{
|
||||
{
|
||||
"fail - operation with same number already exists",
|
||||
"TEST",
|
||||
EXISTENT,
|
||||
false,
|
||||
"already exists",
|
||||
func() {
|
||||
name := EXISTENT.String()
|
||||
require.Equal(t, "STOP", name)
|
||||
},
|
||||
},
|
||||
{
|
||||
"fail - operation with same name already exists",
|
||||
"CREATE",
|
||||
NEW,
|
||||
false,
|
||||
"already exists",
|
||||
func() {
|
||||
name := NEW.String()
|
||||
require.Contains(t, name, "not defined")
|
||||
},
|
||||
},
|
||||
{
|
||||
"fail - operation with same name of STOP",
|
||||
"STOP",
|
||||
NEW,
|
||||
false,
|
||||
"already exists",
|
||||
func() {
|
||||
name := NEW.String()
|
||||
require.Contains(t, name, "not defined")
|
||||
},
|
||||
},
|
||||
{
|
||||
"pass - new operation added to the list",
|
||||
"TEST",
|
||||
NEW,
|
||||
true,
|
||||
"",
|
||||
func() {
|
||||
name := NEW.String()
|
||||
require.Equal(t, "TEST", name)
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
opInfo := OpCodeInfo{
|
||||
Number: tc.opNumber,
|
||||
Name: tc.opName,
|
||||
}
|
||||
_, err := ExtendOperations(opInfo, customExecute, 0, customDynamicGas, 0, 0, customMemorySize)
|
||||
|
||||
if tc.expPass {
|
||||
require.NoError(t, err)
|
||||
} else {
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), tc.errContains, "expected different error")
|
||||
}
|
||||
|
||||
tc.postCheck()
|
||||
})
|
||||
}
|
||||
}
|
||||
30
core/vm/custom_eip_testing.go
Normal file
30
core/vm/custom_eip_testing.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
//go:build test
|
||||
// +build test
|
||||
|
||||
// This file is used to allow the testing of EVM configuration initialization
|
||||
// without the need to introduce testing requirements in the final binary. In
|
||||
// this case, the file provides the possibility to restore the EIP activator
|
||||
// functions to the initial state without the need to compile ResetActivators
|
||||
// in the final binary.
|
||||
|
||||
package vm
|
||||
|
||||
var originalActivators = make(map[int]func(*JumpTable))
|
||||
|
||||
func init() {
|
||||
keys := GetActivatorsEipNumbers()
|
||||
|
||||
originalActivators = make(map[int]func(*JumpTable), len(keys))
|
||||
|
||||
for _, k := range keys {
|
||||
originalActivators[k] = activators[k]
|
||||
}
|
||||
}
|
||||
|
||||
// ResetActivators resets activators to the original go ethereum activators map
|
||||
func ResetActivators() {
|
||||
activators = make(map[int]func(*JumpTable))
|
||||
for k, v := range originalActivators {
|
||||
activators[k] = v
|
||||
}
|
||||
}
|
||||
|
|
@ -110,7 +110,6 @@ type EVM struct {
|
|||
// global (to this context) ethereum virtual machine used throughout
|
||||
// the execution of the tx
|
||||
interpreter *EVMInterpreter
|
||||
|
||||
// abort is used to abort the EVM calling operations
|
||||
abort atomic.Bool
|
||||
|
||||
|
|
@ -124,6 +123,11 @@ type EVM struct {
|
|||
|
||||
// jumpDests stores results of JUMPDEST analysis.
|
||||
jumpDests JumpDestCache
|
||||
|
||||
// hooks is a set of functions that can be used to intercept and modify the
|
||||
// behavior of the EVM when executing certain opcodes.
|
||||
// The hooks are called before the execution of the respective opcodes.
|
||||
hooks OpCodeHooks
|
||||
}
|
||||
|
||||
// NewEVM constructs an EVM instance with the supplied block context, state
|
||||
|
|
@ -138,9 +142,21 @@ func NewEVM(blockCtx BlockContext, statedb StateDB, chainConfig *params.ChainCon
|
|||
chainConfig: chainConfig,
|
||||
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
|
||||
jumpDests: newMapJumpDests(),
|
||||
hooks: newNoopOpCodeHooks(),
|
||||
}
|
||||
evm.precompiles = activePrecompiledContracts(evm.chainRules)
|
||||
evm.interpreter = NewEVMInterpreter(evm)
|
||||
|
||||
return evm
|
||||
}
|
||||
|
||||
// NewEVMWithHooks returns a new EVM and takes a custom OpCodeHooks. The returned EVM is
|
||||
// not thread safe and should only ever be used *once*.
|
||||
func NewEVMWithHooks(hooks OpCodeHooks, blockCtx BlockContext, txCtx TxContext, statedb StateDB, chainConfig *params.ChainConfig, config Config) *EVM {
|
||||
evm := NewEVM(blockCtx, statedb, chainConfig, config)
|
||||
evm.hooks = hooks
|
||||
evm.TxContext = txCtx
|
||||
|
||||
return evm
|
||||
}
|
||||
|
||||
|
|
@ -181,6 +197,11 @@ func (evm *EVM) Interpreter() *EVMInterpreter {
|
|||
return evm.interpreter
|
||||
}
|
||||
|
||||
// WithInterpreter sets the interpreter to the EVM instance
|
||||
func (evm *EVM) WithInterpreter(interpreter *EVMInterpreter) {
|
||||
evm.interpreter = interpreter
|
||||
}
|
||||
|
||||
func isSystemCall(caller common.Address) bool {
|
||||
return caller == params.SystemAddress
|
||||
}
|
||||
|
|
@ -197,6 +218,10 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
if err = evm.hooks.CallHook(evm, caller, addr); err != nil {
|
||||
return nil, gas, err
|
||||
}
|
||||
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -205,8 +230,9 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
if !value.IsZero() && !evm.Context.CanTransfer(evm.StateDB, caller, value) {
|
||||
return nil, gas, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
snapshot := evm.StateDB.Snapshot()
|
||||
p, isPrecompile := evm.precompile(addr)
|
||||
p, isPrecompile := evm.Precompile(addr)
|
||||
|
||||
if !evm.StateDB.Exist(addr) {
|
||||
if !isPrecompile && evm.chainRules.IsEIP4762 && !isSystemCall(caller) {
|
||||
|
|
@ -234,7 +260,7 @@ func (evm *EVM) Call(caller common.Address, addr common.Address, input []byte, g
|
|||
evm.Context.Transfer(evm.StateDB, caller, addr, value)
|
||||
|
||||
if isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, false, evm.Config.Tracer)
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
code := evm.resolveCode(addr)
|
||||
|
|
@ -283,6 +309,9 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
if err = evm.hooks.CallHook(evm, caller, addr); err != nil {
|
||||
return nil, gas, err
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -297,8 +326,8 @@ func (evm *EVM) CallCode(caller common.Address, addr common.Address, input []byt
|
|||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
// It is allowed to call precompiles, even via delegatecall
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
if p, isPrecompile := evm.Precompile(addr); isPrecompile {
|
||||
ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, value, true, evm.Config.Tracer)
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
|
|
@ -333,6 +362,9 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
if err = evm.hooks.CallHook(evm, caller, addr); err != nil {
|
||||
return nil, gas, err
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -340,8 +372,8 @@ func (evm *EVM) DelegateCall(originCaller common.Address, caller common.Address,
|
|||
var snapshot = evm.StateDB.Snapshot()
|
||||
|
||||
// It is allowed to call precompiles, even via delegatecall
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
if p, isPrecompile := evm.Precompile(addr); isPrecompile {
|
||||
ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, nil, true, evm.Config.Tracer)
|
||||
} else {
|
||||
// Initialise a new contract and make initialise the delegate values
|
||||
//
|
||||
|
|
@ -375,6 +407,9 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
evm.captureEnd(evm.depth, startGas, leftOverGas, ret, err)
|
||||
}(gas)
|
||||
}
|
||||
if err = evm.hooks.CallHook(evm, caller, addr); err != nil {
|
||||
return nil, gas, err
|
||||
}
|
||||
// Fail if we're trying to execute above the call depth limit
|
||||
if evm.depth > int(params.CallCreateDepth) {
|
||||
return nil, gas, ErrDepth
|
||||
|
|
@ -392,8 +427,8 @@ func (evm *EVM) StaticCall(caller common.Address, addr common.Address, input []b
|
|||
// future scenarios
|
||||
evm.StateDB.AddBalance(addr, new(uint256.Int), tracing.BalanceChangeTouchAccount)
|
||||
|
||||
if p, isPrecompile := evm.precompile(addr); isPrecompile {
|
||||
ret, gas, err = RunPrecompiledContract(p, input, gas, evm.Config.Tracer)
|
||||
if p, isPrecompile := evm.Precompile(addr); isPrecompile {
|
||||
ret, gas, err = evm.RunPrecompiledContract(p, caller, input, gas, new(uint256.Int), true, evm.Config.Tracer)
|
||||
} else {
|
||||
// Initialise a new contract and set the code that is to be used by the EVM.
|
||||
// The contract is a scoped environment for this execution context only.
|
||||
|
|
@ -558,6 +593,9 @@ func (evm *EVM) initNewContract(contract *Contract, address common.Address) ([]b
|
|||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
if err = evm.hooks.CreateHook(evm, caller); err != nil {
|
||||
return nil, common.Address{}, gas, err
|
||||
}
|
||||
contractAddr = crypto.CreateAddress(caller, evm.StateDB.GetNonce(caller))
|
||||
return evm.create(caller, code, gas, value, contractAddr, CREATE)
|
||||
}
|
||||
|
|
@ -567,6 +605,10 @@ func (evm *EVM) Create(caller common.Address, code []byte, gas uint64, value *ui
|
|||
// The different between Create2 with Create is Create2 uses keccak256(0xff ++ msg.sender ++ salt ++ keccak256(init_code))[12:]
|
||||
// instead of the usual sender-and-nonce-hash as the address where the contract is initialized at.
|
||||
func (evm *EVM) Create2(caller common.Address, code []byte, gas uint64, endowment *uint256.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
|
||||
if err = evm.hooks.CreateHook(evm, caller); err != nil {
|
||||
return nil, common.Address{}, gas, err
|
||||
}
|
||||
|
||||
inithash := crypto.HashData(evm.interpreter.hasher, code)
|
||||
contractAddr = crypto.CreateAddress2(caller, salt.Bytes32(), inithash[:])
|
||||
return evm.create(caller, code, gas, endowment, contractAddr, CREATE2)
|
||||
|
|
|
|||
|
|
@ -102,3 +102,23 @@ type StateDB interface {
|
|||
// Finalise must be invoked at the end of a transaction
|
||||
Finalise(bool)
|
||||
}
|
||||
|
||||
// Interpreter is used to run Ethereum based contracts and will utilize the
|
||||
// passed environment to query external sources for state information.
|
||||
// The Interpreter will run the byte code VM based on the passed
|
||||
// configuration.
|
||||
type Interpreter interface {
|
||||
// EVM returns the EVM instance
|
||||
EVM() *EVM
|
||||
// Config returns the configuration of the interpreter
|
||||
Config() Config
|
||||
// ReadOnly returns whether the interpreter is in read-only mode
|
||||
ReadOnly() bool
|
||||
// ReturnData gets the last CALL's return data for subsequent reuse
|
||||
ReturnData() []byte
|
||||
// SetReturnData sets the last CALL's return data
|
||||
SetReturnData([]byte)
|
||||
// Run loops and evaluates the contract's code with the given input data and returns
|
||||
// the return byte-slice and an error if one occurred.
|
||||
Run(contract *Contract, input []byte, static bool) ([]byte, error)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,6 +27,8 @@ import (
|
|||
"github.com/holiman/uint256"
|
||||
)
|
||||
|
||||
var _ Interpreter = &EVMInterpreter{}
|
||||
|
||||
// Config are the configuration options for the Interpreter
|
||||
type Config struct {
|
||||
Tracer *tracing.Hooks
|
||||
|
|
@ -328,3 +330,28 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
|
|||
|
||||
return res, err
|
||||
}
|
||||
|
||||
// EVM returns the EVM instance
|
||||
func (in *EVMInterpreter) EVM() *EVM {
|
||||
return in.evm
|
||||
}
|
||||
|
||||
// Config returns the configuration of the interpreter
|
||||
func (in EVMInterpreter) Config() Config {
|
||||
return in.evm.Config
|
||||
}
|
||||
|
||||
// ReadOnly returns whether the interpreter is in read-only mode
|
||||
func (in EVMInterpreter) ReadOnly() bool {
|
||||
return in.readOnly
|
||||
}
|
||||
|
||||
// ReturnData gets the last CALL's return data for subsequent reuse
|
||||
func (in *EVMInterpreter) ReturnData() []byte {
|
||||
return in.returnData
|
||||
}
|
||||
|
||||
// SetReturnData sets the last CALL's return data
|
||||
func (in *EVMInterpreter) SetReturnData(data []byte) {
|
||||
in.returnData = data
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import (
|
|||
|
||||
type (
|
||||
executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
|
||||
gasFunc func(*EVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
|
||||
gasFunc func(evm *EVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error)
|
||||
// memorySizeFunc returns the required size, and whether the operation overflowed a uint64
|
||||
memorySizeFunc func(*Stack) (size uint64, overflow bool)
|
||||
)
|
||||
|
|
|
|||
48
core/vm/opcode_hooks.go
Normal file
48
core/vm/opcode_hooks.go
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import "github.com/ethereum/go-ethereum/common"
|
||||
|
||||
// OpCodeHooks is a set of hooks that can be used to intercept and modify the
|
||||
// behavior of the EVM when executing certain opcodes.
|
||||
// The hooks are called before the execution of the respective opcodes.
|
||||
type OpCodeHooks interface {
|
||||
// CallHook is called before executing a CALL, CALLCODE, DELEGATECALL and STATICCALL opcodes.
|
||||
CallHook(evm *EVM, caller common.Address, recipient common.Address) error
|
||||
// CreateHook is called before executing a CREATE and CREATE2 opcodes.
|
||||
CreateHook(evm *EVM, caller common.Address) error
|
||||
}
|
||||
|
||||
type NoopOpCodeHooks struct {
|
||||
}
|
||||
|
||||
func (NoopOpCodeHooks) CallHook(evm *EVM, caller common.Address, recipient common.Address) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (NoopOpCodeHooks) CreateHook(evm *EVM, caller common.Address) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func newNoopOpCodeHooks() OpCodeHooks {
|
||||
return NoopOpCodeHooks{}
|
||||
}
|
||||
|
||||
func NewDefaultOpCodeHooks() OpCodeHooks {
|
||||
return newNoopOpCodeHooks()
|
||||
}
|
||||
|
|
@ -1731,13 +1731,13 @@ func TestSimulateV1(t *testing.T) {
|
|||
want: []blockRes{{
|
||||
Number: "0xb",
|
||||
GasLimit: "0x47e7c4",
|
||||
GasUsed: "0x52f6",
|
||||
GasUsed: "0x5cba",
|
||||
Miner: coinbase,
|
||||
BaseFeePerGas: "0x0",
|
||||
Calls: []callRes{{
|
||||
// Caller is in this case the contract that invokes ecrecover.
|
||||
ReturnValue: strings.ToLower(randomAccounts[2].addr.String()),
|
||||
GasUsed: "0x52f6",
|
||||
GasUsed: "0x5cba",
|
||||
Logs: []log{},
|
||||
Status: "0x1",
|
||||
}},
|
||||
|
|
|
|||
|
|
@ -31,9 +31,13 @@ import (
|
|||
|
||||
type precompileContract struct{}
|
||||
|
||||
func (p *precompileContract) Address() common.Address { return common.Address{} }
|
||||
|
||||
func (p *precompileContract) RequiredGas(input []byte) uint64 { return 0 }
|
||||
|
||||
func (p *precompileContract) Run(input []byte) ([]byte, error) { return nil, nil }
|
||||
func (p *precompileContract) Run(evm *vm.EVM, contract *vm.Contract, readonly bool) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func TestStateOverrideMovePrecompile(t *testing.T) {
|
||||
db := state.NewDatabase(triedb.NewDatabase(rawdb.NewMemoryDatabase(), nil), nil)
|
||||
|
|
|
|||
|
|
@ -76,7 +76,9 @@ func fuzz(id byte, data []byte) int {
|
|||
}
|
||||
cpy := make([]byte, len(data))
|
||||
copy(cpy, data)
|
||||
_, err := precompile.Run(cpy)
|
||||
contract := vm.NewPrecompile(common.Address{}, precompile.Address(), common.U2560, gas)
|
||||
contract.Input = cpy
|
||||
_, err := precompile.Run(nil, contract, false)
|
||||
if !bytes.Equal(cpy, data) {
|
||||
panic(fmt.Sprintf("input data modified, precompile %d: %x %x", id, data, cpy))
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue