diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index d7e884b7da..5d2d399ac4 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -15,7 +15,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
- go-version: 1.18.x
+ go-version: 1.19.x
- name: Checkout code
uses: actions/checkout@v3
- name: Lint
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 593ebd8edb..22ce39f09d 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -17,7 +17,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v3
with:
- go-version: 1.18
+ go-version: 1.19
- name: Get packr
run: go install github.com/gobuffalo/packr/v2/packr2@v2.8.3
diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml
index 145b1532d4..cb7691a46c 100644
--- a/.github/workflows/test-e2e.yml
+++ b/.github/workflows/test-e2e.yml
@@ -18,7 +18,7 @@ jobs:
strategy:
fail-fast: false
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 1, 2, 3, 4, 5, 6, 7 ]
runs-on: ubuntu-latest
@@ -57,7 +57,7 @@ jobs:
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 1, 2, 3, 4, 5, 6, 7 ]
runs-on: ubuntu-latest
diff --git a/.github/workflows/test-from-prover.yml b/.github/workflows/test-from-prover.yml
index 5502d4162c..ca917668a4 100644
--- a/.github/workflows/test-from-prover.yml
+++ b/.github/workflows/test-from-prover.yml
@@ -17,7 +17,7 @@ jobs:
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 2 ]
diff --git a/.github/workflows/test-full-non-e2e.yml b/.github/workflows/test-full-non-e2e.yml
index 71bfe903f5..5514a0dde8 100644
--- a/.github/workflows/test-full-non-e2e.yml
+++ b/.github/workflows/test-full-non-e2e.yml
@@ -17,7 +17,7 @@ jobs:
if: github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name == github.repository
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
runs-on: ubuntu-latest
steps:
@@ -54,7 +54,7 @@ jobs:
contains(github.event.client_payload.pull_request.head.sha, github.event.client_payload.slash_command.sha)
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
runs-on: ubuntu-latest
steps:
diff --git a/.github/workflows/updatedeps.yml b/.github/workflows/updatedeps.yml
index 900cd3eb94..7f0896525c 100644
--- a/.github/workflows/updatedeps.yml
+++ b/.github/workflows/updatedeps.yml
@@ -14,7 +14,7 @@ jobs:
- name: Install Go
uses: actions/setup-go@v3
with:
- go-version: "1.18.x"
+ go-version: "1.19.x"
env:
GOARCH: "amd64"
diff --git a/.golangci.yml b/.golangci.yml
index 87d6e0e5f2..1abbace218 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,6 +1,9 @@
---
run:
timeout: 5m
+ skip-dirs:
+ - state/runtime/fakevm
+ - state/runtime/instrumentation
linters:
enable:
diff --git a/Dockerfile b/Dockerfile
index de1ef42b43..70785db41b 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# CONTAINER FOR BUILDING BINARY
-FROM golang:1.18 AS build
+FROM golang:1.19 AS build
# INSTALL DEPENDENCIES
RUN go install github.com/gobuffalo/packr/v2/packr2@v2.8.3
diff --git a/Makefile b/Makefile
index 83878cb65e..b9ebffb686 100644
--- a/Makefile
+++ b/Makefile
@@ -52,7 +52,7 @@ install-linter: ## Installs the linter
.PHONY: lint
lint: ## Runs the linter
- $$(go env GOPATH)/bin/golangci-lint run
+ export "GOROOT=$$(go env GOROOT)" && $$(go env GOPATH)/bin/golangci-lint run
.PHONY: update-external-dependencies
update-external-dependencies: ## Updates external dependencies like images, test vectors or proto files
diff --git a/README.md b/README.md
index f4d362c6c7..ad8d2d1c96 100644
--- a/README.md
+++ b/README.md
@@ -110,7 +110,7 @@ It's recommended to use `make` for building, and testing the code, ... Run `make
### Requirements
-- Go 1.18
+- Go 1.19
- Docker
- Docker Compose
- Make
diff --git a/docker-compose.yml b/docker-compose.yml
index 049e86dbb6..2619f9f65b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -107,7 +107,7 @@ services:
zkevm-prover:
container_name: zkevm-prover
restart: unless-stopped
- image: hermeznetwork/zkevm-prover:c514cd1
+ image: hermeznetwork/zkevm-prover:4e3272b
depends_on:
zkevm-state-db:
condition: service_healthy
diff --git a/docs/ci/groups.md b/docs/ci/groups.md
index b411805538..7efa50dd27 100644
--- a/docs/ci/groups.md
+++ b/docs/ci/groups.md
@@ -37,7 +37,7 @@ for each of the jobs looks like this:
```
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 1, 2, 3 ]
```
@@ -54,7 +54,7 @@ groups 1 and 3, the matrix strategy config should look like:
```
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 2 ]
```
@@ -62,7 +62,7 @@ If we want to re-add group 1:
```
strategy:
matrix:
- go-version: [ 1.18.x ]
+ go-version: [ 1.19.x ]
goarch: [ "amd64" ]
e2e-group: [ 1, 2 ]
```
diff --git a/etherman/etherman.go b/etherman/etherman.go
index cd3b6e6add..5e2314f25c 100644
--- a/etherman/etherman.go
+++ b/etherman/etherman.go
@@ -28,6 +28,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
@@ -594,11 +595,11 @@ func (etherMan *Client) forcedBatchEvent(ctx context.Context, vLog types.Log, bl
} else if isPending {
return fmt.Errorf("error: tx is still pending. TxHash: %s", tx.Hash().String())
}
- msg, err := tx.AsMessage(types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
+ msg, err := core.TransactionToMessage(tx, types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
if err != nil {
return err
}
- if fb.Sequencer == msg.From() {
+ if fb.Sequencer == msg.From {
txData := tx.Data()
// Extract coded txs.
// Load contract ABI
@@ -661,11 +662,11 @@ func (etherMan *Client) sequencedBatchesEvent(ctx context.Context, vLog types.Lo
} else if isPending {
return fmt.Errorf("error tx is still pending. TxHash: %s", tx.Hash().String())
}
- msg, err := tx.AsMessage(types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
+ msg, err := core.TransactionToMessage(tx, types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
if err != nil {
return err
}
- sequences, err := decodeSequences(tx.Data(), sb.NumBatch, msg.From(), vLog.TxHash, msg.Nonce())
+ sequences, err := decodeSequences(tx.Data(), sb.NumBatch, msg.From, vLog.TxHash, msg.Nonce)
if err != nil {
return fmt.Errorf("error decoding the sequences: %v", err)
}
@@ -786,7 +787,7 @@ func (etherMan *Client) forceSequencedBatchesEvent(ctx context.Context, vLog typ
} else if isPending {
return fmt.Errorf("error: tx is still pending. TxHash: %s", tx.Hash().String())
}
- msg, err := tx.AsMessage(types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
+ msg, err := core.TransactionToMessage(tx, types.NewLondonSigner(tx.ChainId()), big.NewInt(0))
if err != nil {
return err
}
@@ -794,7 +795,7 @@ func (etherMan *Client) forceSequencedBatchesEvent(ctx context.Context, vLog typ
if err != nil {
return fmt.Errorf("error getting hashParent. BlockNumber: %d. Error: %w", vLog.BlockNumber, err)
}
- sequencedForceBatch, err := decodeSequencedForceBatches(tx.Data(), fsb.NumBatch, msg.From(), vLog.TxHash, fullBlock, msg.Nonce())
+ sequencedForceBatch, err := decodeSequencedForceBatches(tx.Data(), fsb.NumBatch, msg.From, vLog.TxHash, fullBlock, msg.Nonce)
if err != nil {
return err
}
diff --git a/go.mod b/go.mod
index 746870c460..6fa1acfbd8 100644
--- a/go.mod
+++ b/go.mod
@@ -1,11 +1,11 @@
module github.com/0xPolygonHermez/zkevm-node
-go 1.18
+go 1.19
require (
github.com/didip/tollbooth/v6 v6.1.2
github.com/dop251/goja v0.0.0-20230122112309-96b1610dd4f7
- github.com/ethereum/go-ethereum v1.11.0
+ github.com/ethereum/go-ethereum v1.11.5
github.com/go-git/go-billy/v5 v5.4.1
github.com/go-git/go-git/v5 v5.6.1
github.com/gobuffalo/packr/v2 v2.8.3
@@ -24,7 +24,7 @@ require (
github.com/umbracle/ethgo v0.1.3
github.com/urfave/cli/v2 v2.25.1
go.uber.org/zap v1.24.0
- golang.org/x/crypto v0.7.0
+ golang.org/x/crypto v0.8.0
golang.org/x/sync v0.1.0
google.golang.org/grpc v1.54.0
google.golang.org/protobuf v1.30.0
@@ -40,7 +40,7 @@ require (
github.com/VictoriaMetrics/fastcache v1.6.0 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/beorn7/perks v1.0.1 // indirect
- github.com/btcsuite/btcd/btcec/v2 v2.2.0 // indirect
+ github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/cloudflare/circl v1.1.0 // indirect
github.com/cockroachdb/errors v1.9.1 // indirect
@@ -50,7 +50,7 @@ require (
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/deckarep/golang-set/v2 v2.1.0 // indirect
- github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect
+ github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 // indirect
github.com/dlclark/regexp2 v1.7.0 // indirect
github.com/edsrzf/mmap-go v1.0.0 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
@@ -66,6 +66,7 @@ require (
github.com/go-stack/stack v1.8.1 // indirect
github.com/gobuffalo/logger v1.0.7 // indirect
github.com/gobuffalo/packd v1.0.2 // indirect
+ github.com/gofrs/flock v0.8.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/golang/snappy v0.0.4 // indirect
@@ -124,10 +125,10 @@ require (
go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect
golang.org/x/exp v0.0.0-20230206171751-46f607a40771 // indirect
- golang.org/x/net v0.8.0 // indirect
- golang.org/x/sys v0.6.0 // indirect
- golang.org/x/term v0.6.0 // indirect
- golang.org/x/text v0.8.0 // indirect
+ golang.org/x/net v0.9.0 // indirect
+ golang.org/x/sys v0.7.0 // indirect
+ golang.org/x/term v0.7.0 // indirect
+ golang.org/x/text v0.9.0 // indirect
golang.org/x/time v0.1.0 // indirect
google.golang.org/genproto v0.0.0-20230110181048-76db0878b65f // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
diff --git a/go.sum b/go.sum
index deae68f9ca..f3be5ed384 100644
--- a/go.sum
+++ b/go.sum
@@ -95,6 +95,8 @@ github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqO
github.com/btcsuite/btcd v0.22.1 h1:CnwP9LM/M9xuRrGSCGeMVs9iv09uMqwsVX7EeIpgV2c=
github.com/btcsuite/btcd/btcec/v2 v2.2.0 h1:fzn1qaOt32TuLjFlkzYSsBC35Q3KUjT1SwPxiMSCF5k=
github.com/btcsuite/btcd/btcec/v2 v2.2.0/go.mod h1:U7MHm051Al6XmscBQ0BoNydpOTsFAn707034b5nY8zU=
+github.com/btcsuite/btcd/btcec/v2 v2.3.2 h1:5n0X6hX0Zk+6omWcihdYvdAlGf2DfasC0GMf7DClJ3U=
+github.com/btcsuite/btcd/btcec/v2 v2.3.2/go.mod h1:zYzJ8etWJQIv1Ogk7OzpWjowwOdXY1W/17j2MW85J04=
github.com/btcsuite/btcd/chaincfg/chainhash v1.0.1 h1:q0rUy8C/TYNBQS1+CGKw68tLOFYSNEs0TFnxxnS9+4U=
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
@@ -152,6 +154,8 @@ github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK
github.com/decred/dcrd/crypto/blake256 v1.0.0/go.mod h1:sQl2p6Y26YV+ZOcSTP6thNdn47hh8kt6rqSlvmrXFAc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 h1:YLtO71vCjJRCBcrPMtQ9nqBsqpA1m5sE92cU+pd5Mcc=
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1/go.mod h1:hyedUtir6IdtD/7lIxGeCxkaw7y45JueMRL4DIyJDKs=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0 h1:HbphB4TFFXpv7MNrT52FGrrgVXF1owhMVTHFZIlnvd4=
+github.com/decred/dcrd/dcrec/secp256k1/v4 v4.1.0/go.mod h1:DZGJHZMqrU4JJqFAWUS2UO1+lbSKsdiOoYi9Zzey7Fc=
github.com/denisenkom/go-mssqldb v0.9.0/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU=
github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
@@ -185,6 +189,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/ethereum/go-ethereum v1.11.0 h1:5ervzucOW7z0TnTMPfWPgkb12utq7mmPb4/OmYnoTq8=
github.com/ethereum/go-ethereum v1.11.0/go.mod h1:DuefStAgaxoaYGLR0FueVcVbehmn5n9QUcVrMCuOvuc=
+github.com/ethereum/go-ethereum v1.11.5 h1:3M1uan+LAUvdn+7wCEFrcMM4LJTeuxDrPTg/f31a5QQ=
+github.com/ethereum/go-ethereum v1.11.5/go.mod h1:it7x0DWnTDMfVFdXcU6Ti4KEFQynLHVRarcSlPr0HBo=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.9.0/go.mod h1:eQcE1qtQxscV5RaZvpXrrb8Drkc3/DdQ+uUYCNjL+zU=
@@ -260,6 +266,8 @@ github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6Wezm
github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/godror/godror v0.24.2/go.mod h1:wZv/9vPiUib6tkoDl+AZ/QLf5YZgMravZ7jxH2eQWAE=
+github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
+github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw=
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
@@ -870,6 +878,8 @@ golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
+golang.org/x/crypto v0.8.0 h1:pd9TJtTueMTVQXzk8E2XESSMQDj/U7OUu0PqJqPXQjQ=
+golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -969,6 +979,8 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
+golang.org/x/net v0.9.0 h1:aWJ/m6xSmxWBx+V0XRHTlrYrPG56jKsLdTFmsSsCzOM=
+golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1081,6 +1093,8 @@ golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0 h1:MVltZSvRTcU2ljQOhs94SXPftV6DCNnZViHeQps87pQ=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU=
+golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1091,6 +1105,8 @@ golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0 h1:clScbb1cHjoCkyRbWwBEUZ5H/tIFu5TAXIqaZD0Gcjw=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
+golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ=
+golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1105,6 +1121,8 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0 h1:57P1ETyNKtuIjB4SRd15iJxuhj8Gc416Y78H3qgMh68=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
diff --git a/jsonrpc/endpoints_debug.go b/jsonrpc/endpoints_debug.go
index fb89f8f3ff..dad3f900c6 100644
--- a/jsonrpc/endpoints_debug.go
+++ b/jsonrpc/endpoints_debug.go
@@ -3,8 +3,10 @@ package jsonrpc
import (
"context"
"encoding/hex"
+ "encoding/json"
"errors"
"fmt"
+ "strings"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/0xPolygonHermez/zkevm-node/log"
@@ -30,11 +32,12 @@ type DebugEndpoints struct {
}
type traceConfig struct {
- DisableStorage bool `json:"disableStorage"`
- DisableStack bool `json:"disableStack"`
- EnableMemory bool `json:"enableMemory"`
- EnableReturnData bool `json:"enableReturnData"`
- Tracer *string `json:"tracer"`
+ DisableStorage bool `json:"disableStorage"`
+ DisableStack bool `json:"disableStack"`
+ EnableMemory bool `json:"enableMemory"`
+ EnableReturnData bool `json:"enableReturnData"`
+ Tracer *string `json:"tracer"`
+ TracerConfig json.RawMessage `json:"tracerConfig"`
}
// StructLogRes represents the debug trace information for each opcode
@@ -133,17 +136,23 @@ func (d *DebugEndpoints) buildTraceBlock(ctx context.Context, txs []*ethTypes.Tr
}
func (d *DebugEndpoints) buildTraceTransaction(ctx context.Context, hash common.Hash, cfg *traceConfig, dbTx pgx.Tx) (interface{}, types.Error) {
- trcCfg := cfg
- if trcCfg == nil {
- trcCfg = defaultTraceConfig
+ traceCfg := cfg
+ if traceCfg == nil {
+ traceCfg = defaultTraceConfig
+ }
+
+ // check tracer
+ if traceCfg.Tracer != nil && *traceCfg.Tracer != "" && !isBuiltInTracer(*traceCfg.Tracer) && !isJSCustomTracer(*traceCfg.Tracer) {
+ return rpcErrorResponse(types.DefaultErrorCode, "invalid tracer", nil)
}
stateTraceConfig := state.TraceConfig{
- DisableStack: trcCfg.DisableStack,
- DisableStorage: trcCfg.DisableStorage,
- EnableMemory: trcCfg.EnableMemory,
- EnableReturnData: trcCfg.EnableReturnData,
- Tracer: trcCfg.Tracer,
+ DisableStack: traceCfg.DisableStack,
+ DisableStorage: traceCfg.DisableStorage,
+ EnableMemory: traceCfg.EnableMemory,
+ EnableReturnData: traceCfg.EnableReturnData,
+ Tracer: traceCfg.Tracer,
+ TracerConfig: traceCfg.TracerConfig,
}
result, err := d.state.DebugTransaction(ctx, hash, stateTraceConfig, dbTx)
if errors.Is(err, state.ErrNotFound) {
@@ -154,17 +163,25 @@ func (d *DebugEndpoints) buildTraceTransaction(ctx context.Context, hash common.
return nil, types.NewRPCError(types.DefaultErrorCode, errorMessage)
}
+ // if a tracer was specified, then return the trace result
if stateTraceConfig.Tracer != nil && *stateTraceConfig.Tracer != "" && len(result.ExecutorTraceResult) > 0 {
return result.ExecutorTraceResult, nil
}
- failed := result.Failed()
+ receipt, err := d.state.GetTransactionReceipt(ctx, hash, dbTx)
+ if err != nil {
+ const errorMessage = "failed to tx receipt"
+ log.Errorf("%v: %v", errorMessage, err)
+ return nil, types.NewRPCError(types.DefaultErrorCode, errorMessage)
+ }
+
+ failed := receipt.Status == ethTypes.ReceiptStatusFailed
var returnValue interface{}
if stateTraceConfig.EnableReturnData {
returnValue = common.Bytes2Hex(result.ReturnValue)
}
- structLogs := d.buildStructLogs(result.StructLogs, *trcCfg)
+ structLogs := d.buildStructLogs(result.StructLogs, *traceCfg)
resp := traceTransactionResponse{
Gas: result.GasUsed,
@@ -235,3 +252,22 @@ func (d *DebugEndpoints) buildStructLogs(stateStructLogs []instrumentation.Struc
}
return structLogs
}
+
+// isBuiltInTracer checks if the tracer is one of the
+// built-in tracers
+func isBuiltInTracer(tracer string) bool {
+ // built-in tracers
+ switch tracer {
+ case "callTracer", "4byteTracer", "prestateTracer", "noopTracer":
+ return true
+ default:
+ return false
+ }
+}
+
+// isJSCustomTracer checks if the tracer contains the
+// functions result and fault which are required for a custom tracer
+// https://geth.ethereum.org/docs/developers/evm-tracing/custom-tracer
+func isJSCustomTracer(tracer string) bool {
+ return strings.Contains(tracer, "result") && strings.Contains(tracer, "fault")
+}
diff --git a/state/converters.go b/state/converters.go
index 7dceca65a2..693dcfc071 100644
--- a/state/converters.go
+++ b/state/converters.go
@@ -285,9 +285,10 @@ func convertToInstrumentationSteps(responses []*pb.TransactionStep) []instrument
step.Contract = convertToInstrumentationContract(response.Contract)
step.GasCost = fmt.Sprint(response.GasCost)
step.Stack = response.Stack
- step.Memory = convertByteArrayToStringArray(response.Memory)
- step.ReturnData = string(response.ReturnData)
-
+ step.Memory = make([]byte, len(response.Memory))
+ copy(step.Memory, response.Memory)
+ step.ReturnData = make([]byte, len(response.ReturnData))
+ copy(step.ReturnData, response.ReturnData)
results = append(results, *step)
}
return results
@@ -303,14 +304,6 @@ func convertToInstrumentationContract(response *pb.Contract) instrumentation.Con
}
}
-func convertByteArrayToStringArray(responses []byte) []string {
- results := make([]string, 0, len(responses))
- for _, response := range responses {
- results = append(results, string(response))
- }
- return results
-}
-
func convertToCounters(resp *pb.ProcessBatchResponse) ZKCounters {
return ZKCounters{
CumulativeGasUsed: resp.CumulativeGasUsed,
diff --git a/state/fakedb.go b/state/fakedb.go
index e47b223801..f204f8b9b3 100644
--- a/state/fakedb.go
+++ b/state/fakedb.go
@@ -6,6 +6,8 @@ import (
"github.com/0xPolygonHermez/zkevm-node/log"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
)
// FakeDB is the implementation of the fakeevm.FakeDB interface
@@ -19,6 +21,21 @@ func (f *FakeDB) SetStateRoot(stateRoot []byte) {
f.stateRoot = stateRoot
}
+// CreateAccount not implemented
+func (f *FakeDB) CreateAccount(common.Address) {
+ log.Error("FakeDB: CreateAccount method not implemented")
+}
+
+// SubBalance not implemented
+func (f *FakeDB) SubBalance(common.Address, *big.Int) {
+ log.Error("FakeDB: SubBalance method not implemented")
+}
+
+// AddBalance not implemented
+func (f *FakeDB) AddBalance(common.Address, *big.Int) {
+ log.Error("FakeDB: AddBalance method not implemented")
+}
+
// GetBalance returns the balance of the given address.
func (f *FakeDB) GetBalance(address common.Address) *big.Int {
ctx := context.Background()
@@ -46,6 +63,23 @@ func (f *FakeDB) GetNonce(address common.Address) uint64 {
return nonce.Uint64()
}
+// SetNonce not implemented
+func (f *FakeDB) SetNonce(common.Address, uint64) {
+ log.Error("FakeDB: SetNonce method not implemented")
+}
+
+// GetCodeHash gets the hash for the code at a given address
+func (f *FakeDB) GetCodeHash(address common.Address) common.Hash {
+ ctx := context.Background()
+ hash, err := f.State.GetTree().GetCodeHash(ctx, address, f.stateRoot)
+ if err != nil {
+ log.Errorf("error on FakeDB GetCodeHash for address %v, err: %v", address, err)
+ }
+
+ log.Debugf("FakeDB GetCodeHash for address %v => %v", address, common.BytesToHash(hash))
+ return common.BytesToHash(hash)
+}
+
// GetCode returns the SC code of the given address.
func (f *FakeDB) GetCode(address common.Address) []byte {
ctx := context.Background()
@@ -59,6 +93,38 @@ func (f *FakeDB) GetCode(address common.Address) []byte {
return code
}
+// SetCode not implemented
+func (f *FakeDB) SetCode(common.Address, []byte) {
+ log.Error("FakeDB: SetCode method not implemented")
+}
+
+// GetCodeSize get address code size
+func (f *FakeDB) GetCodeSize(address common.Address) int {
+ return len(f.GetCode(address))
+}
+
+// AddRefund not implemented
+func (f *FakeDB) AddRefund(uint64) {
+ log.Error("FakeDB: AddRefund method not implemented")
+}
+
+// SubRefund not implemented
+func (f *FakeDB) SubRefund(uint64) {
+ log.Error("FakeDB: SubRefund method not implemented")
+}
+
+// GetRefund not implemented
+func (f *FakeDB) GetRefund() uint64 {
+ log.Error("FakeDB: GetRefund method not implemented")
+ return 0
+}
+
+// GetCommittedState not implemented
+func (f *FakeDB) GetCommittedState(common.Address, common.Hash) common.Hash {
+ log.Error("FakeDB: GetCommittedState method not implemented")
+ return ZeroHash
+}
+
// GetState retrieves a value from the given account's storage trie.
func (f *FakeDB) GetState(address common.Address, hash common.Hash) common.Hash {
ctx := context.Background()
@@ -73,20 +139,94 @@ func (f *FakeDB) GetState(address common.Address, hash common.Hash) common.Hash
return common.BytesToHash(storage.Bytes())
}
-// Exist determines if the given address is in use.
+// SetState not implemented
+func (f *FakeDB) SetState(common.Address, common.Hash, common.Hash) {
+ log.Error("FakeDB: SetState method not implemented")
+}
+
+// GetTransientState not implemented
+func (f *FakeDB) GetTransientState(addr common.Address, key common.Hash) common.Hash {
+ log.Error("FakeDB: GetTransientState method not implemented")
+ return ZeroHash
+}
+
+// SetTransientState not implemented
+func (f *FakeDB) SetTransientState(addr common.Address, key, value common.Hash) {
+ log.Error("FakeDB: SetTransientState method not implemented")
+}
+
+// Suicide not implemented
+func (f *FakeDB) Suicide(common.Address) bool {
+ log.Error("FakeDB: Suicide method not implemented")
+ return false
+}
+
+// HasSuicided not implemented
+func (f *FakeDB) HasSuicided(common.Address) bool {
+ log.Error("FakeDB: HasSuicided method not implemented")
+ return false
+}
+
+// Exist reports whether the given account exists in state.
+// Notably this should also return true for suicided accounts.
func (f *FakeDB) Exist(address common.Address) bool {
return !(f.GetNonce(address) == 0 && f.GetBalance(address).Int64() == 0 && f.GetCodeHash(address) == ZeroHash)
}
-// GetCodeHash gets the hash for the code at a given address
-func (f *FakeDB) GetCodeHash(address common.Address) common.Hash {
- ctx := context.Background()
- hash, err := f.State.GetTree().GetCodeHash(ctx, address, f.stateRoot)
+// Empty returns whether the given account is empty. Empty
+// is defined according to EIP161 (balance = nonce = code = 0).
+func (f *FakeDB) Empty(address common.Address) bool {
+ return !(f.GetNonce(address) == 0 && f.GetBalance(address).Int64() == 0 && f.GetCodeHash(address) == ZeroHash)
+}
- if err != nil {
- log.Errorf("error on FakeDB GetCodeHash for address %v, err: %v", address, err)
- }
+// AddressInAccessList not implemented
+func (f *FakeDB) AddressInAccessList(addr common.Address) bool {
+ log.Error("FakeDB: AddressInAccessList method not implemented")
+ return false
+}
- log.Debugf("FakeDB GetCodeHash for address %v => %v", address, common.BytesToHash(hash))
- return common.BytesToHash(hash)
+// SlotInAccessList not implemented
+func (f *FakeDB) SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool) {
+ log.Error("FakeDB: SlotInAccessList method not implemented")
+ return false, false
+}
+
+// AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
+
+// AddAddressToAccessList not implemented// even if the feature/fork is not active yet
+func (f *FakeDB) AddAddressToAccessList(addr common.Address) {
+ log.Error("FakeDB: AddAddressToAccessList method not implemented")
+}
+
+// AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
+
+// AddSlotToAccessList not implemented// even if the feature/fork is not active yet
+func (f *FakeDB) AddSlotToAccessList(addr common.Address, slot common.Hash) {
+ log.Error("FakeDB: AddSlotToAccessList method not implemented")
+}
+
+// Prepare not implemented
+func (f *FakeDB) Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList) {
+ log.Error("FakeDB: Prepare method not implemented")
+}
+
+// RevertToSnapshot not implemented
+func (f *FakeDB) RevertToSnapshot(int) {
+ log.Error("FakeDB: RevertToSnapshot method not implemented")
+}
+
+// Snapshot not implemented
+func (f *FakeDB) Snapshot() int {
+ log.Error("FakeDB: Snapshot method not implemented")
+ return 0
+}
+
+// AddLog not implemented
+func (f *FakeDB) AddLog(*types.Log) {
+ log.Error("FakeDB: AddLog method not implemented")
+}
+
+// AddPreimage not implemented
+func (f *FakeDB) AddPreimage(common.Hash, []byte) {
+ log.Error("FakeDB: AddPreimage method not implemented")
}
diff --git a/state/runtime/fakevm/analysis.go b/state/runtime/fakevm/analysis.go
new file mode 100644
index 0000000000..3b6d81fad2
--- /dev/null
+++ b/state/runtime/fakevm/analysis.go
@@ -0,0 +1,118 @@
+// 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 .
+
+package fakevm
+
+const (
+ set2BitsMask = uint16(0b11)
+ set3BitsMask = uint16(0b111)
+ set4BitsMask = uint16(0b1111)
+ set5BitsMask = uint16(0b1_1111)
+ set6BitsMask = uint16(0b11_1111)
+ set7BitsMask = uint16(0b111_1111)
+)
+
+// bitvec is a bit vector which maps bytes in a program.
+// An unset bit means the byte is an opcode, a set bit means
+// it's data (i.e. argument of PUSHxx).
+type bitvec []byte
+
+func (bits bitvec) set1(pos uint64) {
+ bits[pos/8] |= 1 << (pos % 8)
+}
+
+func (bits bitvec) setN(flag uint16, pos uint64) {
+ a := flag << (pos % 8)
+ bits[pos/8] |= byte(a)
+ if b := byte(a >> 8); b != 0 {
+ bits[pos/8+1] = b
+ }
+}
+
+func (bits bitvec) set8(pos uint64) {
+ a := byte(0xFF << (pos % 8))
+ bits[pos/8] |= a
+ bits[pos/8+1] = ^a
+}
+
+func (bits bitvec) set16(pos uint64) {
+ a := byte(0xFF << (pos % 8))
+ bits[pos/8] |= a
+ bits[pos/8+1] = 0xFF
+ bits[pos/8+2] = ^a
+}
+
+// codeSegment checks if the position is in a code segment.
+func (bits *bitvec) codeSegment(pos uint64) bool {
+ return (((*bits)[pos/8] >> (pos % 8)) & 1) == 0
+}
+
+// codeBitmap collects data locations in code.
+func codeBitmap(code []byte) bitvec {
+ // The bitmap is 4 bytes longer than necessary, in case the code
+ // ends with a PUSH32, the algorithm will push zeroes onto the
+ // bitvector outside the bounds of the actual code.
+ bits := make(bitvec, len(code)/8+1+4)
+ return codeBitmapInternal(code, bits)
+}
+
+// codeBitmapInternal is the internal implementation of codeBitmap.
+// It exists for the purpose of being able to run benchmark tests
+// without dynamic allocations affecting the results.
+func codeBitmapInternal(code, bits bitvec) bitvec {
+ for pc := uint64(0); pc < uint64(len(code)); {
+ op := OpCode(code[pc])
+ pc++
+ if int8(op) < int8(PUSH1) { // If not PUSH (the int8(op) > int(PUSH32) is always false).
+ continue
+ }
+ numbits := op - PUSH1 + 1
+ if numbits >= 8 {
+ for ; numbits >= 16; numbits -= 16 {
+ bits.set16(pc)
+ pc += 16
+ }
+ for ; numbits >= 8; numbits -= 8 {
+ bits.set8(pc)
+ pc += 8
+ }
+ }
+ switch numbits {
+ case 1:
+ bits.set1(pc)
+ pc += 1
+ case 2:
+ bits.setN(set2BitsMask, pc)
+ pc += 2
+ case 3:
+ bits.setN(set3BitsMask, pc)
+ pc += 3
+ case 4:
+ bits.setN(set4BitsMask, pc)
+ pc += 4
+ case 5:
+ bits.setN(set5BitsMask, pc)
+ pc += 5
+ case 6:
+ bits.setN(set6BitsMask, pc)
+ pc += 6
+ case 7:
+ bits.setN(set7BitsMask, pc)
+ pc += 7
+ }
+ }
+ return bits
+}
diff --git a/state/runtime/fakevm/common.go b/state/runtime/fakevm/common.go
new file mode 100644
index 0000000000..c637c7fdd0
--- /dev/null
+++ b/state/runtime/fakevm/common.go
@@ -0,0 +1,82 @@
+// 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 .
+
+package fakevm
+
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/holiman/uint256"
+)
+
+// calcMemSize64 calculates the required memory size, and returns
+// the size and whether the result overflowed uint64
+func calcMemSize64(off, l *uint256.Int) (uint64, bool) {
+ if !l.IsUint64() {
+ return 0, true
+ }
+ return calcMemSize64WithUint(off, l.Uint64())
+}
+
+// calcMemSize64WithUint calculates the required memory size, and returns
+// the size and whether the result overflowed uint64
+// Identical to calcMemSize64, but length is a uint64
+func calcMemSize64WithUint(off *uint256.Int, length64 uint64) (uint64, bool) {
+ // if length is zero, memsize is always zero, regardless of offset
+ if length64 == 0 {
+ return 0, false
+ }
+ // Check that offset doesn't overflow
+ offset64, overflow := off.Uint64WithOverflow()
+ if overflow {
+ return 0, true
+ }
+ val := offset64 + length64
+ // if value < either of it's parts, then it overflowed
+ return val, val < offset64
+}
+
+// getData returns a slice from the data based on the start and size and pads
+// up to size with zero's. This function is overflow safe.
+func getData(data []byte, start uint64, size uint64) []byte {
+ length := uint64(len(data))
+ if start > length {
+ start = length
+ }
+ end := start + size
+ if end > length {
+ end = length
+ }
+ return common.RightPadBytes(data[start:end], int(size))
+}
+
+// toWordSize returns the ceiled word size required for memory expansion.
+func toWordSize(size uint64) uint64 {
+ if size > math.MaxUint64-31 {
+ return math.MaxUint64/32 + 1
+ }
+
+ return (size + 31) / 32
+}
+
+func allZero(b []byte) bool {
+ for _, byte := range b {
+ if byte != 0 {
+ return false
+ }
+ }
+ return true
+}
diff --git a/state/runtime/fakevm/contract.go b/state/runtime/fakevm/contract.go
new file mode 100644
index 0000000000..c4f4d90a7d
--- /dev/null
+++ b/state/runtime/fakevm/contract.go
@@ -0,0 +1,194 @@
+// Copyright 2015 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 .
+
+package fakevm
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/holiman/uint256"
+)
+
+// ContractRef is a reference to the contract's backing object
+type ContractRef interface {
+ Address() common.Address
+}
+
+// AccountRef implements ContractRef.
+//
+// Account references are used during EVM initialisation and
+// it's 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 a Address
+func (ar AccountRef) Address() common.Address { return (common.Address)(ar) }
+
+// Contract represents an ethereum contract in the state database. It contains
+// the contract code, calling arguments. Contract implements ContractRef
+type Contract struct {
+ // CallerAddress is the result of the caller which initialised this
+ // contract. However when the "call method" is delegated this value
+ // needs to be initialised to that of the caller's caller.
+ CallerAddress common.Address
+ caller ContractRef
+ self ContractRef
+
+ jumpdests map[common.Hash]bitvec // Aggregated result of JUMPDEST analysis.
+ analysis bitvec // Locally cached result of JUMPDEST analysis
+
+ Code []byte
+ CodeHash common.Hash
+ CodeAddr *common.Address
+ Input []byte
+
+ Gas uint64
+ value *big.Int
+}
+
+// NewContract returns a new contract environment for the execution of EVM.
+func NewContract(caller ContractRef, object ContractRef, value *big.Int, gas uint64) *Contract {
+ c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object}
+
+ if parent, ok := caller.(*Contract); ok {
+ // Reuse JUMPDEST analysis from parent context if available.
+ c.jumpdests = parent.jumpdests
+ } else {
+ c.jumpdests = make(map[common.Hash]bitvec)
+ }
+
+ // Gas should be a pointer so it can safely be reduced through the run
+ // This pointer will be off the state transition
+ c.Gas = gas
+ // ensures a value is set
+ c.value = value
+
+ return c
+}
+
+func (c *Contract) validJumpdest(dest *uint256.Int) bool {
+ 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.
+ if overflow || udest >= uint64(len(c.Code)) {
+ return false
+ }
+ // Only JUMPDESTs allowed for destinations
+ if OpCode(c.Code[udest]) != JUMPDEST {
+ return false
+ }
+ return c.isCode(udest)
+}
+
+// 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 {
+ // Do we already have an analysis laying around?
+ if c.analysis != nil {
+ return c.analysis.codeSegment(udest)
+ }
+ // Do we have a contract hash already?
+ // If we do have a hash, that means it's a 'regular' contract. For regular
+ // contracts ( not temporary initcode), we store the analysis in a map
+ if c.CodeHash != (common.Hash{}) {
+ // Does parent context have the analysis?
+ analysis, exist := c.jumpdests[c.CodeHash]
+ if !exist {
+ // Do the analysis and save in parent context
+ // We do not need to store it in c.analysis
+ analysis = codeBitmap(c.Code)
+ c.jumpdests[c.CodeHash] = analysis
+ }
+ // Also stash it in current contract for faster access
+ c.analysis = analysis
+ return analysis.codeSegment(udest)
+ }
+ // We don't have the code hash, most likely a piece of initcode not already
+ // in state trie. In that case, we do an analysis, and save it locally, so
+ // we don't have to recalculate it for every JUMP instruction in the execution
+ // However, we don't save it within the parent context
+ if c.analysis == nil {
+ c.analysis = codeBitmap(c.Code)
+ }
+ return c.analysis.codeSegment(udest)
+}
+
+// AsDelegate sets the contract to be a delegate call and returns the current
+// contract (for chaining calls)
+func (c *Contract) AsDelegate() *Contract {
+ // NOTE: caller must, at all times be a contract. It should never happen
+ // that caller is something other than a Contract.
+ parent := c.caller.(*Contract)
+ c.CallerAddress = parent.CallerAddress
+ c.value = parent.value
+
+ return c
+}
+
+// GetOp returns the n'th element in the contract's byte array
+func (c *Contract) GetOp(n uint64) OpCode {
+ if n < uint64(len(c.Code)) {
+ return OpCode(c.Code[n])
+ }
+
+ return STOP
+}
+
+// Caller returns the caller of the contract.
+//
+// Caller will recursively call caller when the contract is a delegate
+// call, including that of caller's caller.
+func (c *Contract) Caller() common.Address {
+ return c.CallerAddress
+}
+
+// UseGas attempts the use gas and subtracts it and returns true on success
+func (c *Contract) UseGas(gas uint64) (ok bool) {
+ if c.Gas < gas {
+ return false
+ }
+ c.Gas -= gas
+ return true
+}
+
+// Address returns the contracts address
+func (c *Contract) Address() common.Address {
+ return c.self.Address()
+}
+
+// Value returns the contract's value (sent to it from it's caller)
+func (c *Contract) Value() *big.Int {
+ return c.value
+}
+
+// SetCallCode sets the code of the contract and address of the backing data
+// object
+func (c *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
+ c.Code = code
+ c.CodeHash = hash
+ c.CodeAddr = addr
+}
+
+// SetCodeOptionalHash can be used to provide code, but it's optional to provide hash.
+// In case hash is not provided, the jumpdest analysis will not be saved to the parent context
+func (c *Contract) SetCodeOptionalHash(addr *common.Address, codeAndHash *codeAndHash) {
+ c.Code = codeAndHash.code
+ c.CodeHash = codeAndHash.hash
+ c.CodeAddr = addr
+}
diff --git a/state/runtime/fakevm/contracts.go b/state/runtime/fakevm/contracts.go
new file mode 100644
index 0000000000..c3ece760dc
--- /dev/null
+++ b/state/runtime/fakevm/contracts.go
@@ -0,0 +1,1050 @@
+// 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 .
+
+package fakevm
+
+import (
+ "crypto/sha256"
+ "encoding/binary"
+ "errors"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/crypto/blake2b"
+ "github.com/ethereum/go-ethereum/crypto/bls12381"
+ "github.com/ethereum/go-ethereum/crypto/bn256"
+ "github.com/ethereum/go-ethereum/params"
+ "golang.org/x/crypto/ripemd160"
+)
+
+// PrecompiledContract is the basic interface for native Go contracts. The implementation
+// requires a deterministic gas count based on the input size of the Run method of the
+// contract.
+type PrecompiledContract interface {
+ RequiredGas(input []byte) uint64 // RequiredPrice calculates the contract gas use
+ Run(input []byte) ([]byte, error) // Run runs the precompiled contract
+}
+
+// PrecompiledContractsHomestead contains the default set of pre-compiled Ethereum
+// contracts used in the Frontier and Homestead releases.
+var PrecompiledContractsHomestead = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+}
+
+// PrecompiledContractsByzantium contains the default set of pre-compiled Ethereum
+// contracts used in the Byzantium release.
+var PrecompiledContractsByzantium = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
+ common.BytesToAddress([]byte{6}): &bn256AddByzantium{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulByzantium{},
+ common.BytesToAddress([]byte{8}): &bn256PairingByzantium{},
+}
+
+// PrecompiledContractsIstanbul contains the default set of pre-compiled Ethereum
+// contracts used in the Istanbul release.
+var PrecompiledContractsIstanbul = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: false},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+}
+
+// PrecompiledContractsBerlin contains the default set of pre-compiled Ethereum
+// contracts used in the Berlin release.
+var PrecompiledContractsBerlin = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{1}): &ecrecover{},
+ common.BytesToAddress([]byte{2}): &sha256hash{},
+ common.BytesToAddress([]byte{3}): &ripemd160hash{},
+ common.BytesToAddress([]byte{4}): &dataCopy{},
+ common.BytesToAddress([]byte{5}): &bigModExp{eip2565: true},
+ common.BytesToAddress([]byte{6}): &bn256AddIstanbul{},
+ common.BytesToAddress([]byte{7}): &bn256ScalarMulIstanbul{},
+ common.BytesToAddress([]byte{8}): &bn256PairingIstanbul{},
+ common.BytesToAddress([]byte{9}): &blake2F{},
+}
+
+// PrecompiledContractsBLS contains the set of pre-compiled Ethereum
+// contracts specified in EIP-2537. These are exported for testing purposes.
+var PrecompiledContractsBLS = map[common.Address]PrecompiledContract{
+ common.BytesToAddress([]byte{10}): &bls12381G1Add{},
+ common.BytesToAddress([]byte{11}): &bls12381G1Mul{},
+ common.BytesToAddress([]byte{12}): &bls12381G1MultiExp{},
+ common.BytesToAddress([]byte{13}): &bls12381G2Add{},
+ common.BytesToAddress([]byte{14}): &bls12381G2Mul{},
+ common.BytesToAddress([]byte{15}): &bls12381G2MultiExp{},
+ common.BytesToAddress([]byte{16}): &bls12381Pairing{},
+ common.BytesToAddress([]byte{17}): &bls12381MapG1{},
+ common.BytesToAddress([]byte{18}): &bls12381MapG2{},
+}
+
+var (
+ PrecompiledAddressesBerlin []common.Address
+ PrecompiledAddressesIstanbul []common.Address
+ PrecompiledAddressesByzantium []common.Address
+ PrecompiledAddressesHomestead []common.Address
+)
+
+func init() {
+ for k := range PrecompiledContractsHomestead {
+ PrecompiledAddressesHomestead = append(PrecompiledAddressesHomestead, k)
+ }
+ for k := range PrecompiledContractsByzantium {
+ PrecompiledAddressesByzantium = append(PrecompiledAddressesByzantium, k)
+ }
+ for k := range PrecompiledContractsIstanbul {
+ PrecompiledAddressesIstanbul = append(PrecompiledAddressesIstanbul, k)
+ }
+ for k := range PrecompiledContractsBerlin {
+ PrecompiledAddressesBerlin = append(PrecompiledAddressesBerlin, k)
+ }
+}
+
+// ActivePrecompiles returns the precompiles enabled with the current configuration.
+func ActivePrecompiles(rules params.Rules) []common.Address {
+ switch {
+ case rules.IsBerlin:
+ return PrecompiledAddressesBerlin
+ case rules.IsIstanbul:
+ return PrecompiledAddressesIstanbul
+ case rules.IsByzantium:
+ return PrecompiledAddressesByzantium
+ default:
+ return PrecompiledAddressesHomestead
+ }
+}
+
+// RunPrecompiledContract runs and evaluates the output of a precompiled contract.
+// It returns
+// - the returned bytes,
+// - the _remaining_ gas,
+// - any error that occurred
+func RunPrecompiledContract(p PrecompiledContract, input []byte, suppliedGas uint64) (ret []byte, remainingGas uint64, err error) {
+ gasCost := p.RequiredGas(input)
+ if suppliedGas < gasCost {
+ return nil, 0, ErrOutOfGas
+ }
+ suppliedGas -= gasCost
+ output, err := p.Run(input)
+ return output, suppliedGas, err
+}
+
+// ECRECOVER implemented as a native contract.
+type ecrecover struct{}
+
+func (c *ecrecover) RequiredGas(input []byte) uint64 {
+ return params.EcrecoverGas
+}
+
+func (c *ecrecover) Run(input []byte) ([]byte, error) {
+ const ecRecoverInputLength = 128
+
+ input = common.RightPadBytes(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
+
+ // tighter sig s values input homestead only apply to tx sigs
+ if !allZero(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])
+ sig[64] = v
+ // v needs to be at the end for libsecp256k1
+ pubKey, err := crypto.Ecrecover(input[:32], sig)
+ // make sure the public key is a valid one
+ if err != nil {
+ return nil, nil
+ }
+
+ // the first byte of pubkey is bitcoin heritage
+ return common.LeftPadBytes(crypto.Keccak256(pubKey[1:])[12:], 32), nil
+}
+
+// SHA256 implemented as a native contract.
+type sha256hash struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+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)
+ return h[:], nil
+}
+
+// RIPEMD160 implemented as a native contract.
+type ripemd160hash struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+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) {
+ ripemd := ripemd160.New()
+ ripemd.Write(input)
+ return common.LeftPadBytes(ripemd.Sum(nil), 32), nil
+}
+
+// data copy implemented as a native contract.
+type dataCopy struct{}
+
+// 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
+// required for anything significant is so high it's impossible to pay for.
+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
+}
+
+// bigModExp implements a native big integer exponential modular operation.
+type bigModExp struct {
+ eip2565 bool
+}
+
+var (
+ big0 = big.NewInt(0)
+ big1 = big.NewInt(1)
+ big3 = big.NewInt(3)
+ big4 = big.NewInt(4)
+ big7 = big.NewInt(7)
+ big8 = big.NewInt(8)
+ big16 = big.NewInt(16)
+ big20 = big.NewInt(20)
+ big32 = big.NewInt(32)
+ big64 = big.NewInt(64)
+ big96 = big.NewInt(96)
+ big480 = big.NewInt(480)
+ big1024 = big.NewInt(1024)
+ big3072 = big.NewInt(3072)
+ big199680 = big.NewInt(199680)
+)
+
+// modexpMultComplexity implements bigModexp multComplexity formula, as defined in EIP-198
+//
+// def mult_complexity(x):
+// if x <= 64: return x ** 2
+// elif x <= 1024: return x ** 2 // 4 + 96 * x - 3072
+// else: return x ** 2 // 16 + 480 * x - 199680
+//
+// where is x is max(length_of_MODULUS, length_of_BASE)
+func modexpMultComplexity(x *big.Int) *big.Int {
+ switch {
+ case x.Cmp(big64) <= 0:
+ x.Mul(x, x) // x ** 2
+ case x.Cmp(big1024) <= 0:
+ // (x ** 2 // 4 ) + ( 96 * x - 3072)
+ x = new(big.Int).Add(
+ new(big.Int).Div(new(big.Int).Mul(x, x), big4),
+ new(big.Int).Sub(new(big.Int).Mul(big96, x), big3072),
+ )
+ default:
+ // (x ** 2 // 16) + (480 * x - 199680)
+ x = new(big.Int).Add(
+ new(big.Int).Div(new(big.Int).Mul(x, x), big16),
+ new(big.Int).Sub(new(big.Int).Mul(big480, x), big199680),
+ )
+ }
+ return x
+}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bigModExp) RequiredGas(input []byte) uint64 {
+ var (
+ baseLen = new(big.Int).SetBytes(getData(input, 0, 32))
+ expLen = new(big.Int).SetBytes(getData(input, 32, 32))
+ modLen = new(big.Int).SetBytes(getData(input, 64, 32))
+ )
+ if len(input) > 96 {
+ input = input[96:]
+ } else {
+ input = input[:0]
+ }
+ // Retrieve the head 32 bytes of exp for the adjusted exponent length
+ var expHead *big.Int
+ if big.NewInt(int64(len(input))).Cmp(baseLen) <= 0 {
+ expHead = new(big.Int)
+ } else {
+ if expLen.Cmp(big32) > 0 {
+ expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), 32))
+ } else {
+ expHead = new(big.Int).SetBytes(getData(input, baseLen.Uint64(), expLen.Uint64()))
+ }
+ }
+ // Calculate the adjusted exponent length
+ var msb int
+ if bitlen := expHead.BitLen(); bitlen > 0 {
+ msb = bitlen - 1
+ }
+ adjExpLen := new(big.Int)
+ if expLen.Cmp(big32) > 0 {
+ adjExpLen.Sub(expLen, big32)
+ adjExpLen.Mul(big8, adjExpLen)
+ }
+ adjExpLen.Add(adjExpLen, big.NewInt(int64(msb)))
+ // Calculate the gas cost of the operation
+ gas := new(big.Int).Set(math.BigMax(modLen, baseLen))
+ if c.eip2565 {
+ // EIP-2565 has three changes
+ // 1. Different multComplexity (inlined here)
+ // in EIP-2565 (https://eips.ethereum.org/EIPS/eip-2565):
+ //
+ // def mult_complexity(x):
+ // ceiling(x/8)^2
+ //
+ //where is x is max(length_of_MODULUS, length_of_BASE)
+ gas = gas.Add(gas, big7)
+ gas = gas.Div(gas, big8)
+ gas.Mul(gas, gas)
+
+ gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ // 2. Different divisor (`GQUADDIVISOR`) (3)
+ gas.Div(gas, big3)
+ if gas.BitLen() > 64 {
+ return math.MaxUint64
+ }
+ // 3. Minimum price of 200 gas
+ if gas.Uint64() < 200 {
+ return 200
+ }
+ return gas.Uint64()
+ }
+ gas = modexpMultComplexity(gas)
+ gas.Mul(gas, math.BigMax(adjExpLen, big1))
+ gas.Div(gas, big20)
+
+ if gas.BitLen() > 64 {
+ return math.MaxUint64
+ }
+ return gas.Uint64()
+}
+
+func (c *bigModExp) Run(input []byte) ([]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()
+ )
+ if len(input) > 96 {
+ input = input[96:]
+ } else {
+ input = input[:0]
+ }
+ // Handle a special case when both the base and mod length is zero
+ if baseLen == 0 && modLen == 0 {
+ return []byte{}, nil
+ }
+ // 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))
+ v []byte
+ )
+ switch {
+ case mod.BitLen() == 0:
+ // Modulo 0 is undefined, return zero
+ return common.LeftPadBytes([]byte{}, int(modLen)), nil
+ case base.BitLen() == 1: // a bit length of 1 means it's 1 (or -1).
+ //If base == 1, then we can just return base % mod (if mod >= 1, which it is)
+ v = base.Mod(base, mod).Bytes()
+ default:
+ v = base.Exp(base, exp, mod).Bytes()
+ }
+ return common.LeftPadBytes(v, int(modLen)), nil
+}
+
+// newCurvePoint unmarshals a binary blob into a bn256 elliptic curve point,
+// returning it, or an error if the point is invalid.
+func newCurvePoint(blob []byte) (*bn256.G1, error) {
+ p := new(bn256.G1)
+ if _, err := p.Unmarshal(blob); err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+// newTwistPoint unmarshals a binary blob into a bn256 elliptic curve point,
+// returning it, or an error if the point is invalid.
+func newTwistPoint(blob []byte) (*bn256.G2, error) {
+ p := new(bn256.G2)
+ if _, err := p.Unmarshal(blob); err != nil {
+ return nil, err
+ }
+ return p, nil
+}
+
+// runBn256Add implements the Bn256Add precompile, referenced by both
+// Byzantium and Istanbul operations.
+func runBn256Add(input []byte) ([]byte, error) {
+ x, err := newCurvePoint(getData(input, 0, 64))
+ if err != nil {
+ return nil, err
+ }
+ y, err := newCurvePoint(getData(input, 64, 64))
+ if err != nil {
+ return nil, err
+ }
+ res := new(bn256.G1)
+ res.Add(x, y)
+ return res.Marshal(), nil
+}
+
+// bn256Add implements a native elliptic curve point addition conforming to
+// Istanbul consensus rules.
+type bn256AddIstanbul struct{}
+
+// 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)
+}
+
+// bn256AddByzantium implements a native elliptic curve point addition
+// conforming to Byzantium consensus rules.
+type bn256AddByzantium struct{}
+
+// 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)
+}
+
+// runBn256ScalarMul implements the Bn256ScalarMul precompile, referenced by
+// both Byzantium and Istanbul operations.
+func runBn256ScalarMul(input []byte) ([]byte, error) {
+ p, err := newCurvePoint(getData(input, 0, 64))
+ if err != nil {
+ return nil, err
+ }
+ res := new(bn256.G1)
+ res.ScalarMult(p, new(big.Int).SetBytes(getData(input, 64, 32)))
+ return res.Marshal(), nil
+}
+
+// bn256ScalarMulIstanbul implements a native elliptic curve scalar
+// multiplication conforming to Istanbul consensus rules.
+type bn256ScalarMulIstanbul struct{}
+
+// 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)
+}
+
+// bn256ScalarMulByzantium implements a native elliptic curve scalar
+// multiplication conforming to Byzantium consensus rules.
+type bn256ScalarMulByzantium struct{}
+
+// 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)
+}
+
+var (
+ // true32Byte is returned if the bn256 pairing check succeeds.
+ true32Byte = []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}
+
+ // false32Byte is returned if the bn256 pairing check fails.
+ false32Byte = make([]byte, 32)
+
+ // errBadPairingInput is returned if the bn256 pairing input is invalid.
+ errBadPairingInput = errors.New("bad elliptic curve pairing size")
+)
+
+// runBn256Pairing implements the Bn256Pairing precompile, referenced by both
+// Byzantium and Istanbul operations.
+func runBn256Pairing(input []byte) ([]byte, error) {
+ // Handle some corner cases cheaply
+ if len(input)%192 > 0 {
+ return nil, errBadPairingInput
+ }
+ // Convert the input into a set of coordinates
+ var (
+ cs []*bn256.G1
+ ts []*bn256.G2
+ )
+ for i := 0; i < len(input); i += 192 {
+ c, err := newCurvePoint(input[i : i+64])
+ if err != nil {
+ return nil, err
+ }
+ t, err := newTwistPoint(input[i+64 : i+192])
+ if err != nil {
+ return nil, err
+ }
+ cs = append(cs, c)
+ ts = append(ts, t)
+ }
+ // Execute the pairing checks and return the results
+ if bn256.PairingCheck(cs, ts) {
+ return true32Byte, nil
+ }
+ return false32Byte, nil
+}
+
+// bn256PairingIstanbul implements a pairing pre-compile for the bn256 curve
+// conforming to Istanbul consensus rules.
+type bn256PairingIstanbul struct{}
+
+// 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)
+}
+
+// bn256PairingByzantium implements a pairing pre-compile for the bn256 curve
+// conforming to Byzantium consensus rules.
+type bn256PairingByzantium struct{}
+
+// 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)
+}
+
+type blake2F struct{}
+
+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.
+ if len(input) != blake2FInputLength {
+ return 0
+ }
+ return uint64(binary.BigEndian.Uint32(input[0:4]))
+}
+
+const (
+ blake2FInputLength = 213
+ blake2FFinalBlockBytes = byte(1)
+ blake2FNonFinalBlockBytes = byte(0)
+)
+
+var (
+ errBlake2FInvalidInputLength = errors.New("invalid input length")
+ errBlake2FInvalidFinalFlag = errors.New("invalid final flag")
+)
+
+func (c *blake2F) Run(input []byte) ([]byte, error) {
+ // Make sure the input is valid (correct length and final flag)
+ if len(input) != blake2FInputLength {
+ return nil, errBlake2FInvalidInputLength
+ }
+ if input[212] != blake2FNonFinalBlockBytes && 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
+
+ h [8]uint64
+ m [16]uint64
+ t [2]uint64
+ )
+ for i := 0; i < 8; i++ {
+ offset := 4 + i*8
+ h[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ }
+ for i := 0; i < 16; i++ {
+ offset := 68 + i*8
+ m[i] = binary.LittleEndian.Uint64(input[offset : offset+8])
+ }
+ t[0] = binary.LittleEndian.Uint64(input[196:204])
+ t[1] = binary.LittleEndian.Uint64(input[204:212])
+
+ // Execute the compression function, extract and return the result
+ blake2b.F(&h, m, t, final, rounds)
+
+ output := make([]byte, 64)
+ for i := 0; i < 8; i++ {
+ offset := i * 8
+ binary.LittleEndian.PutUint64(output[offset:offset+8], h[i])
+ }
+ return output, nil
+}
+
+var (
+ errBLS12381InvalidInputLength = errors.New("invalid input length")
+ errBLS12381InvalidFieldElementTopBytes = errors.New("invalid field element top bytes")
+ errBLS12381G1PointSubgroup = errors.New("g1 point is not on correct subgroup")
+ errBLS12381G2PointSubgroup = errors.New("g2 point is not on correct subgroup")
+)
+
+// bls12381G1Add implements EIP-2537 G1Add precompile.
+type bls12381G1Add struct{}
+
+// 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) {
+ // 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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point p_0
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode G1 point p_1
+ if p1, err = g.DecodePoint(input[128:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ r := g.New()
+ g.Add(r, p0, p1)
+
+ // Encode the G1 point result into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1Mul implements EIP-2537 G1Mul precompile.
+type bls12381G1Mul struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G1Mul) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G1MulGas
+}
+
+func (c *bls12381G1Mul) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G1Mul precompile.
+ // > G1 multiplication call expects `160` bytes as an input that is interpreted as byte concatenation of encoding of G1 point (`128` bytes) and encoding of a scalar value (`32` bytes).
+ // > Output is an encoding of multiplication operation result - single G1 point (`128` bytes).
+ if len(input) != 160 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG1
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode G1 point
+ if p0, err = g.DecodePoint(input[:128]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[128:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G1 point into 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G1MultiExp implements EIP-2537 G1MultiExp precompile.
+type bls12381G1MultiExp struct{}
+
+// 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
+ k := len(input) / 160
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G1 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G1MulGas * discount) / 1000
+}
+
+func (c *bls12381G1MultiExp) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG1, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ off := 160 * i
+ t0, t1, t2 := off, off+128, off+160
+ // Decode G1 point
+ if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ g.MultiExp(r, points, scalars)
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Add implements EIP-2537 G2Add precompile.
+type bls12381G2Add struct{}
+
+// 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) {
+ // 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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0, p1 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+ r := g.New()
+
+ // Decode G2 point p_0
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode G2 point p_1
+ if p1, err = g.DecodePoint(input[256:]); err != nil {
+ return nil, err
+ }
+
+ // Compute r = p_0 + p_1
+ g.Add(r, p0, p1)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2Mul implements EIP-2537 G2Mul precompile.
+type bls12381G2Mul struct{}
+
+// RequiredGas returns the gas required to execute the pre-compiled contract.
+func (c *bls12381G2Mul) RequiredGas(input []byte) uint64 {
+ return params.Bls12381G2MulGas
+}
+
+func (c *bls12381G2Mul) Run(input []byte) ([]byte, error) {
+ // Implements EIP-2537 G2MUL precompile logic.
+ // > G2 multiplication call expects `288` bytes as an input that is interpreted as byte concatenation of encoding of G2 point (`256` bytes) and encoding of a scalar value (`32` bytes).
+ // > Output is an encoding of multiplication operation result - single G2 point (`256` bytes).
+ if len(input) != 288 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ var p0 *bls12381.PointG2
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode G2 point
+ if p0, err = g.DecodePoint(input[:256]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ e := new(big.Int).SetBytes(input[256:])
+
+ // Compute r = e * p_0
+ r := g.New()
+ g.MulScalar(r, p0, e)
+
+ // Encode the G2 point into 256 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381G2MultiExp implements EIP-2537 G2MultiExp precompile.
+type bls12381G2MultiExp struct{}
+
+// 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
+ k := len(input) / 288
+ if k == 0 {
+ // Return 0 gas for small input length
+ return 0
+ }
+ // Lookup discount value for G2 point, scalar value pair length
+ var discount uint64
+ if dLen := len(params.Bls12381MultiExpDiscountTable); k < dLen {
+ discount = params.Bls12381MultiExpDiscountTable[k-1]
+ } else {
+ discount = params.Bls12381MultiExpDiscountTable[dLen-1]
+ }
+ // Calculate gas and return the result
+ return (uint64(k) * params.Bls12381G2MulGas * discount) / 1000
+}
+
+func (c *bls12381G2MultiExp) Run(input []byte) ([]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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+ var err error
+ points := make([]*bls12381.PointG2, k)
+ scalars := make([]*big.Int, k)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Decode point scalar pairs
+ for i := 0; i < k; i++ {
+ off := 288 * i
+ t0, t1, t2 := off, off+256, off+288
+ // Decode G1 point
+ if points[i], err = g.DecodePoint(input[t0:t1]); err != nil {
+ return nil, err
+ }
+ // Decode scalar value
+ scalars[i] = new(big.Int).SetBytes(input[t1:t2])
+ }
+
+ // Compute r = e_0 * p_0 + e_1 * p_1 + ... + e_(k-1) * p_(k-1)
+ r := g.New()
+ g.MultiExp(r, points, scalars)
+
+ // Encode the G2 point to 256 bytes.
+ return g.EncodePoint(r), nil
+}
+
+// bls12381Pairing implements EIP-2537 Pairing precompile.
+type bls12381Pairing struct{}
+
+// 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) {
+ // 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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Initialize BLS12-381 pairing engine
+ e := bls12381.NewPairingEngine()
+ g1, g2 := e.G1, e.G2
+
+ // Decode pairs
+ for i := 0; i < k; i++ {
+ off := 384 * i
+ t0, t1, t2 := off, off+128, off+384
+
+ // Decode G1 point
+ p1, err := g1.DecodePoint(input[t0:t1])
+ if err != nil {
+ return nil, err
+ }
+ // Decode G2 point
+ p2, err := g2.DecodePoint(input[t1:t2])
+ if err != nil {
+ return nil, err
+ }
+
+ // 'point is on curve' check already done,
+ // Here we need to apply subgroup checks.
+ if !g1.InCorrectSubgroup(p1) {
+ return nil, errBLS12381G1PointSubgroup
+ }
+ if !g2.InCorrectSubgroup(p2) {
+ return nil, errBLS12381G2PointSubgroup
+ }
+
+ // Update pairing engine with G1 and G2 points
+ e.AddPair(p1, p2)
+ }
+ // Prepare 32 byte output
+ out := make([]byte, 32)
+
+ // Compute pairing and set the result
+ if e.Check() {
+ out[31] = 1
+ }
+ return out, nil
+}
+
+// decodeBLS12381FieldElement decodes BLS12-381 elliptic curve field element.
+// Removes top 16 bytes of 64 byte input.
+func decodeBLS12381FieldElement(in []byte) ([]byte, error) {
+ if len(in) != 64 {
+ return nil, errors.New("invalid field element length")
+ }
+ // check top bytes
+ for i := 0; i < 16; i++ {
+ if in[i] != byte(0x00) {
+ return nil, errBLS12381InvalidFieldElementTopBytes
+ }
+ }
+ out := make([]byte, 48)
+ copy(out[:], in[16:])
+ return out, nil
+}
+
+// bls12381MapG1 implements EIP-2537 MapG1 precompile.
+type bls12381MapG1 struct{}
+
+// 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) {
+ // Implements EIP-2537 Map_To_G1 precompile.
+ // > Field-to-curve call expects `64` bytes an an input that is interpreted as a 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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe, err := decodeBLS12381FieldElement(input)
+ if err != nil {
+ return nil, err
+ }
+
+ // Initialize G1
+ g := bls12381.NewG1()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G1 point to 128 bytes
+ return g.EncodePoint(r), nil
+}
+
+// bls12381MapG2 implements EIP-2537 MapG2 precompile.
+type bls12381MapG2 struct{}
+
+// 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) {
+ // Implements EIP-2537 Map_FP2_TO_G2 precompile logic.
+ // > Field-to-curve call expects `128` bytes an an input that is interpreted as a 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 {
+ return nil, errBLS12381InvalidInputLength
+ }
+
+ // Decode input field element
+ fe := make([]byte, 96)
+ c0, err := decodeBLS12381FieldElement(input[:64])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[48:], c0)
+ c1, err := decodeBLS12381FieldElement(input[64:])
+ if err != nil {
+ return nil, err
+ }
+ copy(fe[:48], c1)
+
+ // Initialize G2
+ g := bls12381.NewG2()
+
+ // Compute mapping
+ r, err := g.MapToCurve(fe)
+ if err != nil {
+ return nil, err
+ }
+
+ // Encode the G2 point to 256 bytes
+ return g.EncodePoint(r), nil
+}
diff --git a/state/runtime/fakevm/eips.go b/state/runtime/fakevm/eips.go
new file mode 100644
index 0000000000..5c4674e889
--- /dev/null
+++ b/state/runtime/fakevm/eips.go
@@ -0,0 +1,243 @@
+// Copyright 2019 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 .
+
+package fakevm
+
+import (
+ "fmt"
+ "sort"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
+)
+
+var activators = map[int]func(*JumpTable){
+ 3855: enable3855,
+ 3860: enable3860,
+ 3529: enable3529,
+ 3198: enable3198,
+ 2929: enable2929,
+ 2200: enable2200,
+ 1884: enable1884,
+ 1344: enable1344,
+ 1153: enable1153,
+}
+
+// EnableEIP enables the given EIP on the config.
+// This operation writes in-place, and callers need to ensure that the globally
+// defined jump tables are not polluted.
+func EnableEIP(eipNum int, jt *JumpTable) error {
+ enablerFn, ok := activators[eipNum]
+ if !ok {
+ return fmt.Errorf("undefined eip %d", eipNum)
+ }
+ enablerFn(jt)
+ return nil
+}
+
+func ValidEip(eipNum int) bool {
+ _, ok := activators[eipNum]
+ return ok
+}
+func ActivateableEips() []string {
+ var nums []string
+ for k := range activators {
+ nums = append(nums, fmt.Sprintf("%d", k))
+ }
+ sort.Strings(nums)
+ return nums
+}
+
+// enable1884 applies EIP-1884 to the given jump table:
+// - Increase cost of BALANCE to 700
+// - Increase cost of EXTCODEHASH to 700
+// - Increase cost of SLOAD to 800
+// - Define SELFBALANCE, with cost GasFastStep (5)
+func enable1884(jt *JumpTable) {
+ // Gas cost changes
+ jt[SLOAD].constantGas = params.SloadGasEIP1884
+ jt[BALANCE].constantGas = params.BalanceGasEIP1884
+ jt[EXTCODEHASH].constantGas = params.ExtcodeHashGasEIP1884
+
+ // New opcode
+ jt[SELFBALANCE] = &operation{
+ execute: opSelfBalance,
+ constantGas: GasFastStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+}
+
+func opSelfBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ balance, _ := uint256.FromBig(interpreter.evm.StateDB.GetBalance(scope.Contract.Address()))
+ scope.Stack.Push(balance)
+ return nil, nil
+}
+
+// enable1344 applies EIP-1344 (ChainID Opcode)
+// - Adds an opcode that returns the current chain’s EIP-155 unique identifier
+func enable1344(jt *JumpTable) {
+ // New opcode
+ jt[CHAINID] = &operation{
+ execute: opChainID,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+}
+
+// opChainID implements CHAINID opcode
+func opChainID(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ chainId, _ := uint256.FromBig(interpreter.evm.chainConfig.ChainID)
+ scope.Stack.Push(chainId)
+ return nil, nil
+}
+
+// enable2200 applies EIP-2200 (Rebalance net-metered SSTORE)
+func enable2200(jt *JumpTable) {
+ jt[SLOAD].constantGas = params.SloadGasEIP2200
+ jt[SSTORE].dynamicGas = gasSStoreEIP2200
+}
+
+// enable2929 enables "EIP-2929: Gas cost increases for state access opcodes"
+// https://eips.ethereum.org/EIPS/eip-2929
+func enable2929(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP2929
+
+ jt[SLOAD].constantGas = 0
+ jt[SLOAD].dynamicGas = gasSLoadEIP2929
+
+ jt[EXTCODECOPY].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODECOPY].dynamicGas = gasExtCodeCopyEIP2929
+
+ jt[EXTCODESIZE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODESIZE].dynamicGas = gasEip2929AccountCheck
+
+ jt[EXTCODEHASH].constantGas = params.WarmStorageReadCostEIP2929
+ jt[EXTCODEHASH].dynamicGas = gasEip2929AccountCheck
+
+ jt[BALANCE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[BALANCE].dynamicGas = gasEip2929AccountCheck
+
+ jt[CALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[CALL].dynamicGas = gasCallEIP2929
+
+ jt[CALLCODE].constantGas = params.WarmStorageReadCostEIP2929
+ jt[CALLCODE].dynamicGas = gasCallCodeEIP2929
+
+ jt[STATICCALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[STATICCALL].dynamicGas = gasStaticCallEIP2929
+
+ jt[DELEGATECALL].constantGas = params.WarmStorageReadCostEIP2929
+ jt[DELEGATECALL].dynamicGas = gasDelegateCallEIP2929
+
+ // This was previously part of the dynamic cost, but we're using it as a constantGas
+ // factor here
+ jt[SELFDESTRUCT].constantGas = params.SelfdestructGasEIP150
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP2929
+}
+
+// enable3529 enabled "EIP-3529: Reduction in refunds":
+// - Removes refunds for selfdestructs
+// - Reduces refunds for SSTORE
+// - Reduces max refunds to 20% gas
+func enable3529(jt *JumpTable) {
+ jt[SSTORE].dynamicGas = gasSStoreEIP3529
+ jt[SELFDESTRUCT].dynamicGas = gasSelfdestructEIP3529
+}
+
+// enable3198 applies EIP-3198 (BASEFEE Opcode)
+// - Adds an opcode that returns the current block's base fee.
+func enable3198(jt *JumpTable) {
+ // New opcode
+ jt[BASEFEE] = &operation{
+ execute: opBaseFee,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+}
+
+// enable1153 applies EIP-1153 "Transient Storage"
+// - Adds TLOAD that reads from transient storage
+// - Adds TSTORE that writes to transient storage
+func enable1153(jt *JumpTable) {
+ jt[TLOAD] = &operation{
+ execute: opTload,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ }
+
+ jt[TSTORE] = &operation{
+ execute: opTstore,
+ constantGas: params.WarmStorageReadCostEIP2929,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ }
+}
+
+// opTload implements TLOAD opcode
+func opTload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ loc := scope.Stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetTransientState(scope.Contract.Address(), hash)
+ loc.SetBytes(val.Bytes())
+ return nil, nil
+}
+
+// opTstore implements TSTORE opcode
+func opTstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ loc := scope.Stack.pop()
+ val := scope.Stack.pop()
+ interpreter.evm.StateDB.SetTransientState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
+ return nil, nil
+}
+
+// opBaseFee implements BASEFEE opcode
+func opBaseFee(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ baseFee, _ := uint256.FromBig(interpreter.evm.Context.BaseFee)
+ scope.Stack.Push(baseFee)
+ return nil, nil
+}
+
+// enable3855 applies EIP-3855 (PUSH0 opcode)
+func enable3855(jt *JumpTable) {
+ // New opcode
+ jt[PUSH0] = &operation{
+ execute: opPush0,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+}
+
+// opPush0 implements the PUSH0 opcode
+func opPush0(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int))
+ return nil, nil
+}
+
+// ebnable3860 enables "EIP-3860: Limit and meter initcode"
+// https://eips.ethereum.org/EIPS/eip-3860
+func enable3860(jt *JumpTable) {
+ jt[CREATE].dynamicGas = gasCreateEip3860
+ jt[CREATE2].dynamicGas = gasCreate2Eip3860
+}
diff --git a/state/runtime/fakevm/errors.go b/state/runtime/fakevm/errors.go
new file mode 100644
index 0000000000..ef52b4e0a1
--- /dev/null
+++ b/state/runtime/fakevm/errors.go
@@ -0,0 +1,73 @@
+// 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 .
+
+package fakevm
+
+import (
+ "errors"
+ "fmt"
+)
+
+// List evm execution errors
+var (
+ ErrOutOfGas = errors.New("out of gas")
+ ErrCodeStoreOutOfGas = errors.New("contract creation code storage out of gas")
+ ErrDepth = errors.New("max call depth exceeded")
+ ErrInsufficientBalance = errors.New("insufficient balance for transfer")
+ ErrContractAddressCollision = errors.New("contract address collision")
+ ErrExecutionReverted = errors.New("execution reverted")
+ ErrMaxInitCodeSizeExceeded = errors.New("max initcode size exceeded")
+ ErrMaxCodeSizeExceeded = errors.New("max code size exceeded")
+ ErrInvalidJump = errors.New("invalid jump destination")
+ ErrWriteProtection = errors.New("write protection")
+ ErrReturnDataOutOfBounds = errors.New("return data out of bounds")
+ ErrGasUintOverflow = errors.New("gas uint64 overflow")
+ ErrInvalidCode = errors.New("invalid code: must not begin with 0xef")
+ ErrNonceUintOverflow = errors.New("nonce uint64 overflow")
+
+ // errStopToken is an internal token indicating interpreter loop termination,
+ // never returned to outside callers.
+ errStopToken = errors.New("stop token")
+)
+
+// ErrStackUnderflow wraps an evm error when the items on the stack less
+// than the minimal requirement.
+type ErrStackUnderflow struct {
+ stackLen int
+ required int
+}
+
+func (e *ErrStackUnderflow) Error() string {
+ return fmt.Sprintf("stack underflow (%d <=> %d)", e.stackLen, e.required)
+}
+
+// ErrStackOverflow wraps an evm error when the items on the stack exceeds
+// the maximum allowance.
+type ErrStackOverflow struct {
+ stackLen int
+ limit int
+}
+
+func (e *ErrStackOverflow) Error() string {
+ return fmt.Sprintf("stack limit reached %d (%d)", e.stackLen, e.limit)
+}
+
+// ErrInvalidOpCode wraps an evm error when an invalid opcode is encountered.
+type ErrInvalidOpCode struct {
+ opcode OpCode
+}
+
+func (e *ErrInvalidOpCode) Error() string { return fmt.Sprintf("invalid opcode: %s", e.opcode) }
diff --git a/state/runtime/fakevm/fakedb.go b/state/runtime/fakevm/fakedb.go
index f6d521ee33..47d87815e8 100644
--- a/state/runtime/fakevm/fakedb.go
+++ b/state/runtime/fakevm/fakedb.go
@@ -4,15 +4,74 @@ import (
"math/big"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/params"
)
// FakeDB is the interface state access for the FakeEVM
type FakeDB interface {
SetStateRoot(stateRoot []byte)
- GetBalance(address common.Address) *big.Int
- GetNonce(address common.Address) uint64
- GetCode(address common.Address) []byte
- GetState(address common.Address, hash common.Hash) common.Hash
- Exist(address common.Address) bool
- GetCodeHash(address common.Address) common.Hash
+ CreateAccount(common.Address)
+
+ SubBalance(common.Address, *big.Int)
+ AddBalance(common.Address, *big.Int)
+ GetBalance(common.Address) *big.Int
+
+ GetNonce(common.Address) uint64
+ SetNonce(common.Address, uint64)
+
+ GetCodeHash(common.Address) common.Hash
+ GetCode(common.Address) []byte
+ SetCode(common.Address, []byte)
+ GetCodeSize(common.Address) int
+
+ AddRefund(uint64)
+ SubRefund(uint64)
+ GetRefund() uint64
+
+ GetCommittedState(common.Address, common.Hash) common.Hash
+ GetState(common.Address, common.Hash) common.Hash
+ SetState(common.Address, common.Hash, common.Hash)
+
+ GetTransientState(addr common.Address, key common.Hash) common.Hash
+ SetTransientState(addr common.Address, key, value common.Hash)
+
+ Suicide(common.Address) bool
+ HasSuicided(common.Address) bool
+
+ // Exist reports whether the given account exists in state.
+ // Notably this should also return true for suicided accounts.
+ Exist(common.Address) bool
+ // Empty returns whether the given account is empty. Empty
+ // is defined according to EIP161 (balance = nonce = code = 0).
+ Empty(common.Address) bool
+
+ AddressInAccessList(addr common.Address) bool
+ SlotInAccessList(addr common.Address, slot common.Hash) (addressOk bool, slotOk bool)
+ // AddAddressToAccessList adds the given address to the access list. This operation is safe to perform
+ // even if the feature/fork is not active yet
+ AddAddressToAccessList(addr common.Address)
+ // AddSlotToAccessList adds the given (address,slot) to the access list. This operation is safe to perform
+ // even if the feature/fork is not active yet
+ AddSlotToAccessList(addr common.Address, slot common.Hash)
+ Prepare(rules params.Rules, sender, coinbase common.Address, dest *common.Address, precompiles []common.Address, txAccesses types.AccessList)
+
+ RevertToSnapshot(int)
+ Snapshot() int
+
+ AddLog(*types.Log)
+ AddPreimage(common.Hash, []byte)
+}
+
+// CallContext provides a basic interface for the EVM calling conventions. The EVM
+// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
+type CallContext interface {
+ // Call calls another contract.
+ Call(env *FakeEVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // CallCode takes another contracts code and execute within our own context
+ CallCode(env *FakeEVM, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
+ // DelegateCall is same as CallCode except sender and value is propagated from parent to child scope
+ DelegateCall(env *FakeEVM, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
+ // Create creates a new contract
+ Create(env *FakeEVM, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
}
diff --git a/state/runtime/fakevm/fakevm.go b/state/runtime/fakevm/fakevm.go
index 9f1c3ee8d4..5cc0ab3049 100644
--- a/state/runtime/fakevm/fakevm.go
+++ b/state/runtime/fakevm/fakevm.go
@@ -1,22 +1,108 @@
+// 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 .
+
package fakevm
import (
+ "math/big"
"sync/atomic"
- "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
+)
+
+// emptyCodeHash is used by create to ensure deployment is disallowed to already
+// deployed contract addresses (relevant after the account abstraction).
+var emptyCodeHash = crypto.Keccak256Hash(nil)
+
+type (
+ // CanTransferFunc is the signature of a transfer guard function
+ CanTransferFunc func(FakeDB, common.Address, *big.Int) bool
+ // TransferFunc is the signature of a transfer function
+ TransferFunc func(FakeDB, common.Address, common.Address, *big.Int)
+ // GetHashFunc returns the n'th block hash in the blockchain
+ // and is used by the BLOCKHASH EVM op code.
+ GetHashFunc func(uint64) common.Hash
)
-// MemoryItemSize is the memory item size.
-const MemoryItemSize int = 32
+func (evm *FakeEVM) precompile(addr common.Address) (PrecompiledContract, bool) {
+ var precompiles map[common.Address]PrecompiledContract
+ switch {
+ case evm.chainRules.IsBerlin:
+ precompiles = PrecompiledContractsBerlin
+ case evm.chainRules.IsIstanbul:
+ precompiles = PrecompiledContractsIstanbul
+ case evm.chainRules.IsByzantium:
+ precompiles = PrecompiledContractsByzantium
+ default:
+ precompiles = PrecompiledContractsHomestead
+ }
+ p, ok := precompiles[addr]
+ return p, ok
+}
+
+// BlockContext provides the EVM with auxiliary information. Once provided
+// it shouldn't be modified.
+type BlockContext struct {
+ // CanTransfer returns whether the account contains
+ // sufficient ether to transfer the value
+ CanTransfer CanTransferFunc
+ // Transfer transfers ether from one account to the other
+ Transfer TransferFunc
+ // GetHash returns the hash corresponding to n
+ GetHash GetHashFunc
+
+ // Block information
+ Coinbase common.Address // Provides information for COINBASE
+ GasLimit uint64 // Provides information for GASLIMIT
+ BlockNumber *big.Int // Provides information for NUMBER
+ Time uint64 // Provides information for TIME
+ Difficulty *big.Int // Provides information for DIFFICULTY
+ BaseFee *big.Int // Provides information for BASEFEE
+ Random *common.Hash // Provides information for PREVRANDAO
+}
+
+// TxContext provides the EVM with information about a transaction.
+// All fields can change between transactions.
+type TxContext struct {
+ // Message information
+ Origin common.Address // Provides information for ORIGIN
+ GasPrice *big.Int // Provides information for GASPRICE
+}
-// FakeEVM represents the fake EVM.
+// FakeEVM is the Ethereum Virtual Machine base object and provides
+// the necessary tools to run a contract on the given state with
+// the provided context. It should be noted that any error
+// generated through any of the calls should be considered a
+// revert-state-and-consume-all-gas operation, no checks on
+// specific errors should ever be performed. The interpreter makes
+// sure that any errors generated are to be considered faulty code.
+//
+// The FakeEVM should never be reused and is not thread safe.
type FakeEVM struct {
// Context provides auxiliary blockchain related information
- Context vm.BlockContext
- vm.TxContext
+ Context BlockContext
+ TxContext
// StateDB gives access to the underlying state
StateDB FakeDB
+ // Depth is the current call stack
+ depth int
+
// chainConfig contains information about the current chain
chainConfig *params.ChainConfig
// chain rules contains the chain rules for the current epoch
@@ -24,28 +110,38 @@ type FakeEVM struct {
// virtual machine configuration options used to initialise the
// evm.
Config Config
+ // 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
// NOTE: must be set atomically
abort int32
+ // callGasTemp holds the gas available for the current call. This is needed because the
+ // available gas is calculated in gasCall* according to the 63/64 rule and later
+ // applied in opCall*.
+ callGasTemp uint64
}
// NewFakeEVM returns a new EVM. The returned EVM is not thread safe and should
// only ever be used *once*.
-// func NewFakeEVM(blockCtx vm.BlockContext, txCtx vm.TxContext, statedb runtime.FakeDB, chainConfig *params.ChainConfig, config Config) *FakeEVM {
-func NewFakeEVM(blockCtx vm.BlockContext, txCtx vm.TxContext, chainConfig *params.ChainConfig, config Config) *FakeEVM {
+func NewFakeEVM(blockCtx BlockContext, txCtx TxContext, statedb FakeDB, chainConfig *params.ChainConfig, config Config) *FakeEVM {
evm := &FakeEVM{
Context: blockCtx,
TxContext: txCtx,
+ StateDB: statedb,
Config: config,
chainConfig: chainConfig,
chainRules: chainConfig.Rules(blockCtx.BlockNumber, blockCtx.Random != nil, blockCtx.Time),
}
+ evm.interpreter = NewEVMInterpreter(evm)
return evm
}
-// SetStateDB is the StateDB setter.
-func (evm *FakeEVM) SetStateDB(stateDB FakeDB) {
- evm.StateDB = stateDB
+// Reset resets the EVM with a new transaction context.Reset
+// This is not threadsafe and should only be done very cautiously.
+func (evm *FakeEVM) Reset(txCtx TxContext, statedb FakeDB) {
+ evm.TxContext = txCtx
+ evm.StateDB = statedb
}
// Cancel cancels any running EVM operation. This may be called concurrently and
@@ -54,13 +150,375 @@ func (evm *FakeEVM) Cancel() {
atomic.StoreInt32(&evm.abort, 1)
}
-// ChainConfig returns the environment's chain configuration
-func (evm *FakeEVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
+// Cancelled returns true if Cancel has been called
+func (evm *FakeEVM) Cancelled() bool {
+ return atomic.LoadInt32(&evm.abort) == 1
+}
+
+// Interpreter returns the current interpreter
+func (evm *FakeEVM) Interpreter() *EVMInterpreter {
+ return evm.interpreter
+}
+
+// SetBlockContext updates the block context of the EVM.
+func (evm *FakeEVM) SetBlockContext(blockCtx BlockContext) {
+ evm.Context = blockCtx
+ num := blockCtx.BlockNumber
+ timestamp := blockCtx.Time
+ evm.chainRules = evm.chainConfig.Rules(num, blockCtx.Random != nil, timestamp)
+}
+
+// Call executes the contract associated with the addr with the given input as
+// parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
+func (evm *FakeEVM) Call(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
+ // Fail if we're trying to execute above the call depth limit
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
+ }
+ // Fail if we're trying to transfer more than the available balance
+ if value.Sign() != 0 && !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ return nil, gas, ErrInsufficientBalance
+ }
+ snapshot := evm.StateDB.Snapshot()
+ p, isPrecompile := evm.precompile(addr)
+
+ if !evm.StateDB.Exist(addr) {
+ if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
+ // Calling a non existing account, don't do anything, but ping the tracer
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureEnd(ret, 0, nil)
+ } else {
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureExit(ret, 0, nil)
+ }
+ }
+ return nil, gas, nil
+ }
+ evm.StateDB.CreateAccount(addr)
+ }
+ evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
+
+ // Capture the tracer start/end events in debug mode
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ defer func(startGas uint64) { // Lazy evaluation of the parameters
+ evm.Config.Tracer.CaptureEnd(ret, startGas-gas, err)
+ }(gas)
+ } else {
+ // Handle tracer events for entering and exiting a call frame
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+ }
+
+ if isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } 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.
+ code := evm.StateDB.GetCode(addr)
+ if len(code) == 0 {
+ ret, err = nil, nil // gas is unchanged
+ } else {
+ addrCopy := addr
+ // If the account has no code, we can abort here
+ // The depth-check is already done, and precompiles handled above
+ contract := NewContract(caller, AccountRef(addrCopy), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), code)
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
+ }
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ gas = 0
+ }
+ // TODO: consider clearing up unused snapshots:
+ //} else {
+ // evm.StateDB.DiscardSnapshot(snapshot)
+ }
+ return ret, gas, err
+}
+
+// CallCode executes the contract associated with the addr with the given input
+// as parameters. It also handles any necessary value transfer required and takes
+// the necessary steps to create accounts and reverses the state in case of an
+// execution error or failed value transfer.
+//
+// CallCode differs from Call in the sense that it executes the given address'
+// code with the caller as context.
+func (evm *FakeEVM) CallCode(caller ContractRef, addr common.Address, input []byte, gas uint64, value *big.Int) (ret []byte, leftOverGas uint64, err error) {
+ // Fail if we're trying to execute above the call depth limit
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
+ }
+ // Fail if we're trying to transfer more than the available balance
+ // Note although it's noop to transfer X ether to caller itself. But
+ // if caller doesn't have enough balance, it would be an error to allow
+ // over-charging itself. So the check here is necessary.
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ return nil, gas, ErrInsufficientBalance
+ }
+ var snapshot = evm.StateDB.Snapshot()
+
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ addrCopy := addr
+ // 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.
+ contract := NewContract(caller, AccountRef(caller.Address()), value, gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
+ if err != nil {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ gas = 0
+ }
+ }
+ return ret, gas, err
+}
+
+// DelegateCall executes the contract associated with the addr with the given input
+// as parameters. It reverses the state in case of an execution error.
+//
+// DelegateCall differs from CallCode in the sense that it executes the given address'
+// code with the caller as context and the caller is set to the caller of the caller.
+func (evm *FakeEVM) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
+ // Fail if we're trying to execute above the call depth limit
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
+ }
+ var snapshot = evm.StateDB.Snapshot()
+
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ // NOTE: caller must, at all times be a contract. It should never happen
+ // that caller is something other than a Contract.
+ parent := caller.(*Contract)
+ // DELEGATECALL inherits value from parent call
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, parent.value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
+ // It is allowed to call precompiles, even via delegatecall
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ addrCopy := addr
+ // Initialise a new contract and make initialise the delegate values
+ contract := NewContract(caller, AccountRef(caller.Address()), nil, gas).AsDelegate()
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ ret, err = evm.interpreter.Run(contract, input, false)
+ gas = contract.Gas
+ }
+ if err != nil {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ gas = 0
+ }
+ }
+ return ret, gas, err
+}
+
+// StaticCall executes the contract associated with the addr with the given input
+// as parameters while disallowing any modifications to the state during the call.
+// Opcodes that attempt to perform such modifications will result in exceptions
+// instead of performing the modifications.
+func (evm *FakeEVM) StaticCall(caller ContractRef, addr common.Address, input []byte, gas uint64) (ret []byte, leftOverGas uint64, err error) {
+ // Fail if we're trying to execute above the call depth limit
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, gas, ErrDepth
+ }
+ // We take a snapshot here. This is a bit counter-intuitive, and could probably be skipped.
+ // However, even a staticcall is considered a 'touch'. On mainnet, static calls were introduced
+ // after all empty accounts were deleted, so this is not required. However, if we omit this,
+ // then certain tests start failing; stRevertTest/RevertPrecompiledTouchExactOOG.json.
+ // We could change this, but for now it's left for legacy reasons
+ var snapshot = evm.StateDB.Snapshot()
+
+ // We do an AddBalance of zero here, just in order to trigger a touch.
+ // This doesn't matter on Mainnet, where all empties are gone at the time of Byzantium,
+ // but is the correct thing to do and matters on other networks, in tests, and potential
+ // future scenarios
+ evm.StateDB.AddBalance(addr, big0)
+
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
+ if p, isPrecompile := evm.precompile(addr); isPrecompile {
+ ret, gas, err = RunPrecompiledContract(p, input, gas)
+ } else {
+ // At this point, we use a copy of address. If we don't, the go compiler will
+ // leak the 'contract' to the outer scope, and make allocation for 'contract'
+ // even if the actual execution ends on RunPrecompiled above.
+ addrCopy := addr
+ // 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.
+ contract := NewContract(caller, AccountRef(addrCopy), new(big.Int), gas)
+ contract.SetCallCode(&addrCopy, evm.StateDB.GetCodeHash(addrCopy), evm.StateDB.GetCode(addrCopy))
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in Homestead this also counts for code storage gas errors.
+ ret, err = evm.interpreter.Run(contract, input, true)
+ gas = contract.Gas
+ }
+ if err != nil {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ gas = 0
+ }
+ }
+ return ret, gas, err
+}
+
+type codeAndHash struct {
+ code []byte
+ hash common.Hash
+}
+
+func (c *codeAndHash) Hash() common.Hash {
+ if c.hash == (common.Hash{}) {
+ c.hash = crypto.Keccak256Hash(c.code)
+ }
+ return c.hash
+}
+
+// create creates a new contract using code as deployment code.
+func (evm *FakeEVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
+ // Depth check execution. Fail if we're trying to execute above the
+ // limit.
+ if evm.depth > int(params.CallCreateDepth) {
+ return nil, common.Address{}, gas, ErrDepth
+ }
+ if !evm.Context.CanTransfer(evm.StateDB, caller.Address(), value) {
+ return nil, common.Address{}, gas, ErrInsufficientBalance
+ }
+ nonce := evm.StateDB.GetNonce(caller.Address())
+ if nonce+1 < nonce {
+ return nil, common.Address{}, gas, ErrNonceUintOverflow
+ }
+ evm.StateDB.SetNonce(caller.Address(), nonce+1)
+ // We add this to the access list _before_ taking a snapshot. Even if the creation fails,
+ // the access-list change should not be rolled back
+ if evm.chainRules.IsBerlin {
+ evm.StateDB.AddAddressToAccessList(address)
+ }
+ // Ensure there's no existing contract already at the designated address
+ contractHash := evm.StateDB.GetCodeHash(address)
+ if evm.StateDB.GetNonce(address) != 0 || (contractHash != (common.Hash{}) && contractHash != emptyCodeHash) {
+ return nil, common.Address{}, 0, ErrContractAddressCollision
+ }
+ // Create a new account on the state
+ snapshot := evm.StateDB.Snapshot()
+ evm.StateDB.CreateAccount(address)
+ if evm.chainRules.IsEIP158 {
+ evm.StateDB.SetNonce(address, 1)
+ }
+ evm.Context.Transfer(evm.StateDB, caller.Address(), address, value)
+
+ // 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.
+ contract := NewContract(caller, AccountRef(address), value, gas)
+ contract.SetCodeOptionalHash(&address, codeAndHash)
+
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ } else {
+ evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
+ }
+ }
+
+ ret, err := evm.interpreter.Run(contract, nil, false)
+
+ // Check whether the max code size has been exceeded, assign err if the case.
+ if err == nil && evm.chainRules.IsEIP158 && len(ret) > params.MaxCodeSize {
+ err = ErrMaxCodeSizeExceeded
+ }
+
+ // Reject code starting with 0xEF if EIP-3541 is enabled.
+ if err == nil && len(ret) >= 1 && ret[0] == 0xEF && evm.chainRules.IsLondon {
+ err = ErrInvalidCode
+ }
+
+ // if the contract creation ran successfully and no errors were returned
+ // calculate the gas required to store the code. If the code could not
+ // be stored due to not enough gas set an error and let it be handled
+ // by the error checking condition below.
+ if err == nil {
+ createDataGas := uint64(len(ret)) * params.CreateDataGas
+ if contract.UseGas(createDataGas) {
+ evm.StateDB.SetCode(address, ret)
+ } else {
+ err = ErrCodeStoreOutOfGas
+ }
+ }
+
+ // When an error was returned by the EVM or when setting the creation code
+ // above we revert to the snapshot and consume any gas remaining. Additionally
+ // when we're in homestead this also counts for code storage gas errors.
+ if err != nil && (evm.chainRules.IsHomestead || err != ErrCodeStoreOutOfGas) {
+ evm.StateDB.RevertToSnapshot(snapshot)
+ if err != ErrExecutionReverted {
+ contract.UseGas(contract.Gas)
+ }
+ }
+
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, err)
+ } else {
+ evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
+ }
+ }
+ return ret, address, contract.Gas, err
+}
-// ScopeContext contains the things that are per-call, such as stack and memory,
-// but not transients like pc and gas
-type ScopeContext struct {
- Memory *Memory
- Stack *Stack
- Contract *vm.Contract
+// Create creates a new contract using code as deployment code.
+func (evm *FakeEVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+ contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
+ return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
+
+// Create2 creates a new contract using code as deployment code.
+//
+// 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 *FakeEVM) Create2(caller ContractRef, code []byte, gas uint64, endowment *big.Int, salt *uint256.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
+ codeAndHash := &codeAndHash{code: code}
+ contractAddr = crypto.CreateAddress2(caller.Address(), salt.Bytes32(), codeAndHash.Hash().Bytes())
+ return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
+}
+
+// ChainConfig returns the environment's chain configuration
+func (evm *FakeEVM) ChainConfig() *params.ChainConfig { return evm.chainConfig }
diff --git a/state/runtime/fakevm/gas.go b/state/runtime/fakevm/gas.go
new file mode 100644
index 0000000000..4ccf3da204
--- /dev/null
+++ b/state/runtime/fakevm/gas.go
@@ -0,0 +1,53 @@
+// Copyright 2015 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 .
+
+package fakevm
+
+import (
+ "github.com/holiman/uint256"
+)
+
+// Gas costs
+const (
+ GasQuickStep uint64 = 2
+ GasFastestStep uint64 = 3
+ GasFastStep uint64 = 5
+ GasMidStep uint64 = 8
+ GasSlowStep uint64 = 10
+ GasExtStep uint64 = 20
+)
+
+// callGas returns the actual gas cost of the call.
+//
+// The cost of gas was changed during the homestead price change HF.
+// As part of EIP 150 (TangerineWhistle), the returned gas is gas - base * 63 / 64.
+func callGas(isEip150 bool, availableGas, base uint64, callCost *uint256.Int) (uint64, error) {
+ if isEip150 {
+ availableGas = availableGas - base
+ gas := availableGas - availableGas/64
+ // If the bit length exceeds 64 bit we know that the newly calculated "gas" for EIP150
+ // is smaller than the requested amount. Therefore we return the new gas instead
+ // of returning an error.
+ if !callCost.IsUint64() || gas < callCost.Uint64() {
+ return gas, nil
+ }
+ }
+ if !callCost.IsUint64() {
+ return 0, ErrGasUintOverflow
+ }
+
+ return callCost.Uint64(), nil
+}
diff --git a/state/runtime/fakevm/gas_table.go b/state/runtime/fakevm/gas_table.go
new file mode 100644
index 0000000000..31e4859dc2
--- /dev/null
+++ b/state/runtime/fakevm/gas_table.go
@@ -0,0 +1,477 @@
+// Copyright 2017 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 .
+
+package fakevm
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// memoryGasCost calculates the quadratic gas for memory expansion. It does so
+// only for the memory region that is expanded, not the total memory.
+func memoryGasCost(mem *Memory, newMemSize uint64) (uint64, error) {
+ if newMemSize == 0 {
+ return 0, nil
+ }
+ // The maximum that will fit in a uint64 is max_word_count - 1. Anything above
+ // that will result in an overflow. Additionally, a newMemSize which results in
+ // a newMemSizeWords larger than 0xFFFFFFFF will cause the square operation to
+ // overflow. The constant 0x1FFFFFFFE0 is the highest number that can be used
+ // without overflowing the gas calculation.
+ if newMemSize > 0x1FFFFFFFE0 {
+ return 0, ErrGasUintOverflow
+ }
+ newMemSizeWords := toWordSize(newMemSize)
+ newMemSize = newMemSizeWords * 32
+
+ if newMemSize > uint64(mem.Len()) {
+ square := newMemSizeWords * newMemSizeWords
+ linCoef := newMemSizeWords * params.MemoryGas
+ quadCoef := square / params.QuadCoeffDiv
+ newTotalFee := linCoef + quadCoef
+
+ fee := newTotalFee - mem.lastGasCost
+ mem.lastGasCost = newTotalFee
+
+ return fee, nil
+ }
+ return 0, nil
+}
+
+// memoryCopierGas creates the gas functions for the following opcodes, and takes
+// the stack position of the operand which determines the size of the data to copy
+// as argument:
+// CALLDATACOPY (stack position 2)
+// CODECOPY (stack position 2)
+// EXTCODECOPY (stack position 3)
+// RETURNDATACOPY (stack position 2)
+func memoryCopierGas(stackpos int) gasFunc {
+ return func(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // Gas for expanding the memory
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ // And gas for copying data, charged per word at param.CopyGas
+ words, overflow := stack.Back(stackpos).Uint64WithOverflow()
+ if overflow {
+ return 0, ErrGasUintOverflow
+ }
+
+ if words, overflow = math.SafeMul(toWordSize(words), params.CopyGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+
+ if gas, overflow = math.SafeAdd(gas, words); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+ }
+}
+
+var (
+ gasCallDataCopy = memoryCopierGas(2)
+ gasCodeCopy = memoryCopierGas(2)
+ gasExtCodeCopy = memoryCopierGas(3)
+ gasReturnDataCopy = memoryCopierGas(2)
+)
+
+func gasSStore(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ y, x = stack.Back(1), stack.Back(0)
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
+ )
+ // The legacy gas metering only takes into consideration the current state
+ // Legacy rules should be applied if we are in Petersburg (removal of EIP-1283)
+ // OR Constantinople is not active
+ if evm.chainRules.IsPetersburg || !evm.chainRules.IsConstantinople {
+ // This checks for 3 scenario's and calculates gas accordingly:
+ //
+ // 1. From a zero-value address to a non-zero value (NEW VALUE)
+ // 2. From a non-zero value address to a zero-value address (DELETE)
+ // 3. From a non-zero to a non-zero (CHANGE)
+ switch {
+ case current == (common.Hash{}) && y.Sign() != 0: // 0 => non 0
+ return params.SstoreSetGas, nil
+ case current != (common.Hash{}) && y.Sign() == 0: // non 0 => 0
+ evm.StateDB.AddRefund(params.SstoreRefundGas)
+ return params.SstoreClearGas, nil
+ default: // non 0 => non 0 (or 0 => 0)
+ return params.SstoreResetGas, nil
+ }
+ }
+
+ // The new gas metering is based on net gas costs (EIP-1283):
+ //
+ // (1.) If current value equals new value (this is a no-op), 200 gas is deducted.
+ // (2.) If current value does not equal new value
+ // (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context)
+ // (2.1.1.) If original value is 0, 20000 gas is deducted.
+ // (2.1.2.) Otherwise, 5000 gas is deducted. If new value is 0, add 15000 gas to refund counter.
+ // (2.2.) If original value does not equal current value (this storage slot is dirty), 200 gas is deducted. Apply both of the following clauses.
+ // (2.2.1.) If original value is not 0
+ // (2.2.1.1.) If current value is 0 (also means that new value is not 0), remove 15000 gas from refund counter. We can prove that refund counter will never go below 0.
+ // (2.2.1.2.) If new value is 0 (also means that current value is not 0), add 15000 gas to refund counter.
+ // (2.2.2.) If original value equals new value (this storage slot is reset)
+ // (2.2.2.1.) If original value is 0, add 19800 gas to refund counter.
+ // (2.2.2.2.) Otherwise, add 4800 gas to refund counter.
+ value := common.Hash(y.Bytes32())
+ if current == value { // noop (1)
+ return params.NetSstoreNoopGas, nil
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return params.NetSstoreInitGas, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(params.NetSstoreClearRefund)
+ }
+ return params.NetSstoreCleanGas, nil // write existing slot (2.1.2)
+ }
+ if original != (common.Hash{}) {
+ if current == (common.Hash{}) { // recreate slot (2.2.1.1)
+ evm.StateDB.SubRefund(params.NetSstoreClearRefund)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(params.NetSstoreClearRefund)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ evm.StateDB.AddRefund(params.NetSstoreResetClearRefund)
+ } else { // reset to original existing slot (2.2.2.2)
+ evm.StateDB.AddRefund(params.NetSstoreResetRefund)
+ }
+ }
+ return params.NetSstoreDirtyGas, nil
+}
+
+// Here come the EIP2200 rules:
+//
+// (0.) If *gasleft* is less than or equal to 2300, fail the current call.
+// (1.) If current value equals new value (this is a no-op), SLOAD_GAS is deducted.
+// (2.) If current value does not equal new value:
+// (2.1.) If original value equals current value (this storage slot has not been changed by the current execution context):
+// (2.1.1.) If original value is 0, SSTORE_SET_GAS (20K) gas is deducted.
+// (2.1.2.) Otherwise, SSTORE_RESET_GAS gas is deducted. If new value is 0, add SSTORE_CLEARS_SCHEDULE to refund counter.
+// (2.2.) If original value does not equal current value (this storage slot is dirty), SLOAD_GAS gas is deducted. Apply both of the following clauses:
+// (2.2.1.) If original value is not 0:
+// (2.2.1.1.) If current value is 0 (also means that new value is not 0), subtract SSTORE_CLEARS_SCHEDULE gas from refund counter.
+// (2.2.1.2.) If new value is 0 (also means that current value is not 0), add SSTORE_CLEARS_SCHEDULE gas to refund counter.
+// (2.2.2.) If original value equals new value (this storage slot is reset):
+// (2.2.2.1.) If original value is 0, add SSTORE_SET_GAS - SLOAD_GAS to refund counter.
+// (2.2.2.2.) Otherwise, add SSTORE_RESET_GAS - SLOAD_GAS gas to refund counter.
+func gasSStoreEIP2200(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // If we fail the minimum gas availability invariant, fail (0)
+ if contract.Gas <= params.SstoreSentryGasEIP2200 {
+ return 0, errors.New("not enough gas for reentrancy sentry")
+ }
+ // Gas sentry honoured, do the actual gas calculation based on the stored value
+ var (
+ y, x = stack.Back(1), stack.Back(0)
+ current = evm.StateDB.GetState(contract.Address(), x.Bytes32())
+ )
+ value := common.Hash(y.Bytes32())
+
+ if current == value { // noop (1)
+ return params.SloadGasEIP2200, nil
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return params.SstoreSetGasEIP2200, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
+ }
+ return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
+ }
+ if original != (common.Hash{}) {
+ if current == (common.Hash{}) { // recreate slot (2.2.1.1)
+ evm.StateDB.SubRefund(params.SstoreClearsScheduleRefundEIP2200)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(params.SstoreClearsScheduleRefundEIP2200)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
+ } else { // reset to original existing slot (2.2.2.2)
+ evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
+ }
+ }
+ return params.SloadGasEIP2200, nil // dirty update (2.2)
+}
+
+func makeGasLog(n uint64) gasFunc {
+ return func(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ requestedSize, overflow := stack.Back(1).Uint64WithOverflow()
+ if overflow {
+ return 0, ErrGasUintOverflow
+ }
+
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+
+ if gas, overflow = math.SafeAdd(gas, params.LogGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if gas, overflow = math.SafeAdd(gas, n*params.LogTopicGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+
+ var memorySizeGas uint64
+ if memorySizeGas, overflow = math.SafeMul(requestedSize, params.LogDataGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if gas, overflow = math.SafeAdd(gas, memorySizeGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+ }
+}
+
+func gasKeccak256(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ wordGas, overflow := stack.Back(1).Uint64WithOverflow()
+ if overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+// pureMemoryGascost is used by several operations, which aside from their
+// static cost have a dynamic cost which is solely based on the memory
+// expansion
+func pureMemoryGascost(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ return memoryGasCost(mem, memorySize)
+}
+
+var (
+ gasReturn = pureMemoryGascost
+ gasRevert = pureMemoryGascost
+ gasMLoad = pureMemoryGascost
+ gasMStore8 = pureMemoryGascost
+ gasMStore = pureMemoryGascost
+ gasCreate = pureMemoryGascost
+)
+
+func gasCreate2(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ wordGas, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if wordGas, overflow = math.SafeMul(toWordSize(wordGas), params.Keccak256WordGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ if gas, overflow = math.SafeAdd(gas, wordGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasCreateEip3860(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := params.InitCodeWordGas * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+func gasCreate2Eip3860(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ size, overflow := stack.Back(2).Uint64WithOverflow()
+ if overflow || size > params.MaxInitCodeSize {
+ return 0, ErrGasUintOverflow
+ }
+ // Since size <= params.MaxInitCodeSize, these multiplication cannot overflow
+ moreGas := (params.InitCodeWordGas + params.Keccak256WordGas) * ((size + 31) / 32)
+ if gas, overflow = math.SafeAdd(gas, moreGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasExpFrontier(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+
+ var (
+ gas = expByteLen * params.ExpByteFrontier // no overflow check required. Max is 256 * ExpByte gas
+ overflow bool
+ )
+ if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasExpEIP158(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ expByteLen := uint64((stack.data[stack.len()-2].BitLen() + 7) / 8)
+
+ var (
+ gas = expByteLen * params.ExpByteEIP158 // no overflow check required. Max is 256 * ExpByte gas
+ overflow bool
+ )
+ if gas, overflow = math.SafeAdd(gas, params.ExpGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasCall(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ transfersValue = !stack.Back(2).IsZero()
+ address = common.Address(stack.Back(1).Bytes20())
+ )
+ if evm.chainRules.IsEIP158 {
+ if transfersValue && evm.StateDB.Empty(address) {
+ gas += params.CallNewAccountGas
+ }
+ } else if !evm.StateDB.Exist(address) {
+ gas += params.CallNewAccountGas
+ }
+ if transfersValue {
+ gas += params.CallValueTransferGas
+ }
+ memoryGas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ var overflow bool
+ if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+
+ evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasCallCode(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ memoryGas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ var (
+ gas uint64
+ overflow bool
+ )
+ if stack.Back(2).Sign() != 0 {
+ gas += params.CallValueTransferGas
+ }
+ if gas, overflow = math.SafeAdd(gas, memoryGas); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasDelegateCall(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
+ var overflow bool
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasStaticCall(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ gas, err := memoryGasCost(mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ evm.callGasTemp, err = callGas(evm.chainRules.IsEIP150, contract.Gas, gas, stack.Back(0))
+ if err != nil {
+ return 0, err
+ }
+ var overflow bool
+ if gas, overflow = math.SafeAdd(gas, evm.callGasTemp); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+}
+
+func gasSelfdestruct(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var gas uint64
+ // EIP150 homestead gas reprice fork:
+ if evm.chainRules.IsEIP150 {
+ gas = params.SelfdestructGasEIP150
+ var address = common.Address(stack.Back(0).Bytes20())
+
+ if evm.chainRules.IsEIP158 {
+ // if empty and transfers value
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
+ gas += params.CreateBySelfdestructGas
+ }
+ } else if !evm.StateDB.Exist(address) {
+ gas += params.CreateBySelfdestructGas
+ }
+ }
+
+ if !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(params.SelfdestructRefundGas)
+ }
+ return gas, nil
+}
diff --git a/state/runtime/fakevm/instructions.go b/state/runtime/fakevm/instructions.go
new file mode 100644
index 0000000000..f87fd9d6b5
--- /dev/null
+++ b/state/runtime/fakevm/instructions.go
@@ -0,0 +1,919 @@
+// Copyright 2015 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 .
+
+package fakevm
+
+import (
+ "sync/atomic"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/holiman/uint256"
+)
+
+func opAdd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Add(&x, y)
+ return nil, nil
+}
+
+func opSub(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Sub(&x, y)
+ return nil, nil
+}
+
+func opMul(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Mul(&x, y)
+ return nil, nil
+}
+
+func opDiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Div(&x, y)
+ return nil, nil
+}
+
+func opSdiv(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.SDiv(&x, y)
+ return nil, nil
+}
+
+func opMod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Mod(&x, y)
+ return nil, nil
+}
+
+func opSmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.SMod(&x, y)
+ return nil, nil
+}
+
+func opExp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ base, exponent := scope.Stack.pop(), scope.Stack.peek()
+ exponent.Exp(&base, exponent)
+ return nil, nil
+}
+
+func opSignExtend(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ back, num := scope.Stack.pop(), scope.Stack.peek()
+ num.ExtendSign(num, &back)
+ return nil, nil
+}
+
+func opNot(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x := scope.Stack.peek()
+ x.Not(x)
+ return nil, nil
+}
+
+func opLt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ if x.Lt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
+ }
+ return nil, nil
+}
+
+func opGt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ if x.Gt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
+ }
+ return nil, nil
+}
+
+func opSlt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ if x.Slt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
+ }
+ return nil, nil
+}
+
+func opSgt(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ if x.Sgt(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
+ }
+ return nil, nil
+}
+
+func opEq(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ if x.Eq(y) {
+ y.SetOne()
+ } else {
+ y.Clear()
+ }
+ return nil, nil
+}
+
+func opIszero(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x := scope.Stack.peek()
+ if x.IsZero() {
+ x.SetOne()
+ } else {
+ x.Clear()
+ }
+ return nil, nil
+}
+
+func opAnd(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.And(&x, y)
+ return nil, nil
+}
+
+func opOr(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Or(&x, y)
+ return nil, nil
+}
+
+func opXor(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y := scope.Stack.pop(), scope.Stack.peek()
+ y.Xor(&x, y)
+ return nil, nil
+}
+
+func opByte(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ th, val := scope.Stack.pop(), scope.Stack.peek()
+ val.Byte(&th)
+ return nil, nil
+}
+
+func opAddmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
+ if z.IsZero() {
+ z.Clear()
+ } else {
+ z.AddMod(&x, &y, z)
+ }
+ return nil, nil
+}
+
+func opMulmod(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x, y, z := scope.Stack.pop(), scope.Stack.pop(), scope.Stack.peek()
+ z.MulMod(&x, &y, z)
+ return nil, nil
+}
+
+// opSHL implements Shift Left
+// The SHL instruction (shift left) pops 2 values from the stack, first arg1 and then arg2,
+// and pushes on the stack arg2 shifted to the left by arg1 number of bits.
+func opSHL(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
+ shift, value := scope.Stack.pop(), scope.Stack.peek()
+ if shift.LtUint64(256) {
+ value.Lsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
+ }
+ return nil, nil
+}
+
+// opSHR implements Logical Shift Right
+// The SHR instruction (logical shift right) pops 2 values from the stack, first arg1 and then arg2,
+// and pushes on the stack arg2 shifted to the right by arg1 number of bits with zero fill.
+func opSHR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // Note, second operand is left in the stack; accumulate result into it, and no need to push it afterwards
+ shift, value := scope.Stack.pop(), scope.Stack.peek()
+ if shift.LtUint64(256) {
+ value.Rsh(value, uint(shift.Uint64()))
+ } else {
+ value.Clear()
+ }
+ return nil, nil
+}
+
+// opSAR implements Arithmetic Shift Right
+// The SAR instruction (arithmetic shift right) pops 2 values from the stack, first arg1 and then arg2,
+// and pushes on the stack arg2 shifted to the right by arg1 number of bits with sign extension.
+func opSAR(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ shift, value := scope.Stack.pop(), scope.Stack.peek()
+ if shift.GtUint64(256) {
+ if value.Sign() >= 0 {
+ value.Clear()
+ } else {
+ // Max negative shift: all bits set
+ value.SetAllOne()
+ }
+ return nil, nil
+ }
+ n := uint(shift.Uint64())
+ value.SRsh(value, n)
+ return nil, nil
+}
+
+func opKeccak256(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ offset, size := scope.Stack.pop(), scope.Stack.peek()
+ data := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
+
+ if interpreter.hasher == nil {
+ interpreter.hasher = crypto.NewKeccakState()
+ } else {
+ interpreter.hasher.Reset()
+ }
+ interpreter.hasher.Write(data)
+ interpreter.hasher.Read(interpreter.hasherBuf[:])
+
+ evm := interpreter.evm
+ if evm.Config.EnablePreimageRecording {
+ evm.StateDB.AddPreimage(interpreter.hasherBuf, data)
+ }
+
+ size.SetBytes(interpreter.hasherBuf[:])
+ return nil, nil
+}
+func opAddress(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Address().Bytes()))
+ return nil, nil
+}
+
+func opBalance(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ slot := scope.Stack.peek()
+ address := common.Address(slot.Bytes20())
+ slot.SetFromBig(interpreter.evm.StateDB.GetBalance(address))
+ return nil, nil
+}
+
+func opOrigin(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Origin.Bytes()))
+ return nil, nil
+}
+func opCaller(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetBytes(scope.Contract.Caller().Bytes()))
+ return nil, nil
+}
+
+func opCallValue(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v, _ := uint256.FromBig(scope.Contract.value)
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opCallDataLoad(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ x := scope.Stack.peek()
+ if offset, overflow := x.Uint64WithOverflow(); !overflow {
+ data := getData(scope.Contract.Input, offset, 32)
+ x.SetBytes(data)
+ } else {
+ x.Clear()
+ }
+ return nil, nil
+}
+
+func opCallDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(scope.Contract.Input))))
+ return nil, nil
+}
+
+func opCallDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ memOffset = scope.Stack.pop()
+ dataOffset = scope.Stack.pop()
+ length = scope.Stack.pop()
+ )
+ dataOffset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ dataOffset64 = 0xffffffffffffffff
+ }
+ // These values are checked for overflow during gas cost calculation
+ memOffset64 := memOffset.Uint64()
+ length64 := length.Uint64()
+ scope.Memory.Set(memOffset64, length64, getData(scope.Contract.Input, dataOffset64, length64))
+
+ return nil, nil
+}
+
+func opReturnDataSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(len(interpreter.returnData))))
+ return nil, nil
+}
+
+func opReturnDataCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ memOffset = scope.Stack.pop()
+ dataOffset = scope.Stack.pop()
+ length = scope.Stack.pop()
+ )
+
+ offset64, overflow := dataOffset.Uint64WithOverflow()
+ if overflow {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ // we can reuse dataOffset now (aliasing it for clarity)
+ var end = dataOffset
+ end.Add(&dataOffset, &length)
+ end64, overflow := end.Uint64WithOverflow()
+ if overflow || uint64(len(interpreter.returnData)) < end64 {
+ return nil, ErrReturnDataOutOfBounds
+ }
+ scope.Memory.Set(memOffset.Uint64(), length.Uint64(), interpreter.returnData[offset64:end64])
+ return nil, nil
+}
+
+func opExtCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ slot := scope.Stack.peek()
+ slot.SetUint64(uint64(interpreter.evm.StateDB.GetCodeSize(slot.Bytes20())))
+ return nil, nil
+}
+
+func opCodeSize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ l := new(uint256.Int)
+ l.SetUint64(uint64(len(scope.Contract.Code)))
+ scope.Stack.Push(l)
+ return nil, nil
+}
+
+func opCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ memOffset = scope.Stack.pop()
+ codeOffset = scope.Stack.pop()
+ length = scope.Stack.pop()
+ )
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ codeCopy := getData(scope.Contract.Code, uint64CodeOffset, length.Uint64())
+ scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+
+ return nil, nil
+}
+
+func opExtCodeCopy(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ stack = scope.Stack
+ a = stack.pop()
+ memOffset = stack.pop()
+ codeOffset = stack.pop()
+ length = stack.pop()
+ )
+ uint64CodeOffset, overflow := codeOffset.Uint64WithOverflow()
+ if overflow {
+ uint64CodeOffset = 0xffffffffffffffff
+ }
+ addr := common.Address(a.Bytes20())
+ codeCopy := getData(interpreter.evm.StateDB.GetCode(addr), uint64CodeOffset, length.Uint64())
+ scope.Memory.Set(memOffset.Uint64(), length.Uint64(), codeCopy)
+
+ return nil, nil
+}
+
+// opExtCodeHash returns the code hash of a specified account.
+// There are several cases when the function is called, while we can relay everything
+// to `state.GetCodeHash` function to ensure the correctness.
+//
+// 1. Caller tries to get the code hash of a normal contract account, state
+// should return the relative code hash and set it as the result.
+//
+// 2. Caller tries to get the code hash of a non-existent account, state should
+// return common.Hash{} and zero will be set as the result.
+//
+// 3. Caller tries to get the code hash for an account without contract code, state
+// should return emptyCodeHash(0xc5d246...) as the result.
+//
+// 4. Caller tries to get the code hash of a precompiled account, the result should be
+// zero or emptyCodeHash.
+//
+// It is worth noting that in order to avoid unnecessary create and clean, all precompile
+// accounts on mainnet have been transferred 1 wei, so the return here should be
+// emptyCodeHash. If the precompile account is not transferred any amount on a private or
+// customized chain, the return value will be zero.
+//
+// 5. Caller tries to get the code hash for an account which is marked as suicided
+// in the current transaction, the code hash of this account should be returned.
+//
+// 6. Caller tries to get the code hash for an account which is marked as deleted, this
+// account should be regarded as a non-existent account and zero should be returned.
+func opExtCodeHash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ slot := scope.Stack.peek()
+ address := common.Address(slot.Bytes20())
+ if interpreter.evm.StateDB.Empty(address) {
+ slot.Clear()
+ } else {
+ slot.SetBytes(interpreter.evm.StateDB.GetCodeHash(address).Bytes())
+ }
+ return nil, nil
+}
+
+func opGasprice(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.GasPrice)
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opBlockhash(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ num := scope.Stack.peek()
+ num64, overflow := num.Uint64WithOverflow()
+ if overflow {
+ num.Clear()
+ return nil, nil
+ }
+ var upper, lower uint64
+ upper = interpreter.evm.Context.BlockNumber.Uint64()
+ if upper < 257 {
+ lower = 0
+ } else {
+ lower = upper - 256
+ }
+ if num64 >= lower && num64 < upper {
+ num.SetBytes(interpreter.evm.Context.GetHash(num64).Bytes())
+ } else {
+ num.Clear()
+ }
+ return nil, nil
+}
+
+func opCoinbase(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetBytes(interpreter.evm.Context.Coinbase.Bytes()))
+ return nil, nil
+}
+
+func opTimestamp(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.Time))
+ return nil, nil
+}
+
+func opNumber(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Context.BlockNumber)
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opDifficulty(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v, _ := uint256.FromBig(interpreter.evm.Context.Difficulty)
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opRandom(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v := new(uint256.Int).SetBytes(interpreter.evm.Context.Random.Bytes())
+ scope.Stack.Push(v)
+ return nil, nil
+}
+
+func opGasLimit(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(interpreter.evm.Context.GasLimit))
+ return nil, nil
+}
+
+func opPop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.pop()
+ return nil, nil
+}
+
+func opMload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ v := scope.Stack.peek()
+ offset := int64(v.Uint64())
+ v.SetBytes(scope.Memory.GetPtr(offset, 32))
+ return nil, nil
+}
+
+func opMstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // pop value of the stack
+ mStart, val := scope.Stack.pop(), scope.Stack.pop()
+ scope.Memory.Set32(mStart.Uint64(), &val)
+ return nil, nil
+}
+
+func opMstore8(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ off, val := scope.Stack.pop(), scope.Stack.pop()
+ scope.Memory.store[off.Uint64()] = byte(val.Uint64())
+ return nil, nil
+}
+
+func opSload(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ loc := scope.Stack.peek()
+ hash := common.Hash(loc.Bytes32())
+ val := interpreter.evm.StateDB.GetState(scope.Contract.Address(), hash)
+ loc.SetBytes(val.Bytes())
+ return nil, nil
+}
+
+func opSstore(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ loc := scope.Stack.pop()
+ val := scope.Stack.pop()
+ interpreter.evm.StateDB.SetState(scope.Contract.Address(), loc.Bytes32(), val.Bytes32())
+ return nil, nil
+}
+
+func opJump(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
+ return nil, errStopToken
+ }
+ pos := scope.Stack.pop()
+ if !scope.Contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
+ }
+ *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
+ return nil, nil
+}
+
+func opJumpi(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if atomic.LoadInt32(&interpreter.evm.abort) != 0 {
+ return nil, errStopToken
+ }
+ pos, cond := scope.Stack.pop(), scope.Stack.pop()
+ if !cond.IsZero() {
+ if !scope.Contract.validJumpdest(&pos) {
+ return nil, ErrInvalidJump
+ }
+ *pc = pos.Uint64() - 1 // pc will be increased by the interpreter loop
+ }
+ return nil, nil
+}
+
+func opJumpdest(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ return nil, nil
+}
+
+func opPc(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(*pc))
+ return nil, nil
+}
+
+func opMsize(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(uint64(scope.Memory.Len())))
+ return nil, nil
+}
+
+func opGas(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.Push(new(uint256.Int).SetUint64(scope.Contract.Gas))
+ return nil, nil
+}
+
+func opCreate(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ var (
+ value = scope.Stack.pop()
+ offset, size = scope.Stack.pop(), scope.Stack.pop()
+ input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = scope.Contract.Gas
+ )
+ if interpreter.evm.chainRules.IsEIP150 {
+ gas -= gas / 64
+ }
+ // reuse size int for stackvalue
+ stackvalue := size
+
+ scope.Contract.UseGas(gas)
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
+ bigVal = value.ToBig()
+ }
+
+ res, addr, returnGas, suberr := interpreter.evm.Create(scope.Contract, input, gas, bigVal)
+ // Push item on the stack based on the returned error. If the ruleset is
+ // homestead we must check for CodeStoreOutOfGasError (homestead only
+ // rule) and treat as an error, if the ruleset is frontier we must
+ // ignore this error and pretend the operation was successful.
+ if interpreter.evm.chainRules.IsHomestead && suberr == ErrCodeStoreOutOfGas {
+ stackvalue.Clear()
+ } else if suberr != nil && suberr != ErrCodeStoreOutOfGas {
+ stackvalue.Clear()
+ } else {
+ stackvalue.SetBytes(addr.Bytes())
+ }
+ scope.Stack.Push(&stackvalue)
+ scope.Contract.Gas += returnGas
+
+ if suberr == ErrExecutionReverted {
+ interpreter.returnData = res // set REVERT data to return data buffer
+ return res, nil
+ }
+ interpreter.returnData = nil // clear dirty return data buffer
+ return nil, nil
+}
+
+func opCreate2(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ var (
+ endowment = scope.Stack.pop()
+ offset, size = scope.Stack.pop(), scope.Stack.pop()
+ salt = scope.Stack.pop()
+ input = scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ gas = scope.Contract.Gas
+ )
+ // Apply EIP150
+ gas -= gas / 64
+ scope.Contract.UseGas(gas)
+ // reuse size int for stackvalue
+ stackvalue := size
+ //TODO: use uint256.Int instead of converting with toBig()
+ bigEndowment := big0
+ if !endowment.IsZero() {
+ bigEndowment = endowment.ToBig()
+ }
+ res, addr, returnGas, suberr := interpreter.evm.Create2(scope.Contract, input, gas,
+ bigEndowment, &salt)
+ // Push item on the stack based on the returned error.
+ if suberr != nil {
+ stackvalue.Clear()
+ } else {
+ stackvalue.SetBytes(addr.Bytes())
+ }
+ scope.Stack.Push(&stackvalue)
+ scope.Contract.Gas += returnGas
+
+ if suberr == ErrExecutionReverted {
+ interpreter.returnData = res // set REVERT data to return data buffer
+ return res, nil
+ }
+ interpreter.returnData = nil // clear dirty return data buffer
+ return nil, nil
+}
+
+func opCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ stack := scope.Stack
+ // Pop gas. The actual gas in interpreter.evm.callGasTemp.
+ // We can use this as a temporary value
+ temp := stack.pop()
+ gas := interpreter.evm.callGasTemp
+ // Pop other call parameters.
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
+ // Get the arguments from the memory.
+ args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+
+ if interpreter.readOnly && !value.IsZero() {
+ return nil, ErrWriteProtection
+ }
+ var bigVal = big0
+ //TODO: use uint256.Int instead of converting with toBig()
+ // By using big0 here, we save an alloc for the most common case (non-ether-transferring contract calls),
+ // but it would make more sense to extend the usage of uint256.Int
+ if !value.IsZero() {
+ gas += params.CallStipend
+ bigVal = value.ToBig()
+ }
+
+ ret, returnGas, err := interpreter.evm.Call(scope.Contract, toAddr, args, gas, bigVal)
+
+ if err != nil {
+ temp.Clear()
+ } else {
+ temp.SetOne()
+ }
+ stack.Push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ }
+ scope.Contract.Gas += returnGas
+
+ interpreter.returnData = ret
+ return ret, nil
+}
+
+func opCallCode(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
+ stack := scope.Stack
+ // We use it as a temporary value
+ temp := stack.pop()
+ gas := interpreter.evm.callGasTemp
+ // Pop other call parameters.
+ addr, value, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
+ // Get arguments from the memory.
+ args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+
+ //TODO: use uint256.Int instead of converting with toBig()
+ var bigVal = big0
+ if !value.IsZero() {
+ gas += params.CallStipend
+ bigVal = value.ToBig()
+ }
+
+ ret, returnGas, err := interpreter.evm.CallCode(scope.Contract, toAddr, args, gas, bigVal)
+ if err != nil {
+ temp.Clear()
+ } else {
+ temp.SetOne()
+ }
+ stack.Push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ }
+ scope.Contract.Gas += returnGas
+
+ interpreter.returnData = ret
+ return ret, nil
+}
+
+func opDelegateCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ stack := scope.Stack
+ // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
+ // We use it as a temporary value
+ temp := stack.pop()
+ gas := interpreter.evm.callGasTemp
+ // Pop other call parameters.
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
+ // Get arguments from the memory.
+ args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+
+ ret, returnGas, err := interpreter.evm.DelegateCall(scope.Contract, toAddr, args, gas)
+ if err != nil {
+ temp.Clear()
+ } else {
+ temp.SetOne()
+ }
+ stack.Push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ }
+ scope.Contract.Gas += returnGas
+
+ interpreter.returnData = ret
+ return ret, nil
+}
+
+func opStaticCall(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ // Pop gas. The actual gas is in interpreter.evm.callGasTemp.
+ stack := scope.Stack
+ // We use it as a temporary value
+ temp := stack.pop()
+ gas := interpreter.evm.callGasTemp
+ // Pop other call parameters.
+ addr, inOffset, inSize, retOffset, retSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
+ toAddr := common.Address(addr.Bytes20())
+ // Get arguments from the memory.
+ args := scope.Memory.GetPtr(int64(inOffset.Uint64()), int64(inSize.Uint64()))
+
+ ret, returnGas, err := interpreter.evm.StaticCall(scope.Contract, toAddr, args, gas)
+ if err != nil {
+ temp.Clear()
+ } else {
+ temp.SetOne()
+ }
+ stack.Push(&temp)
+ if err == nil || err == ErrExecutionReverted {
+ scope.Memory.Set(retOffset.Uint64(), retSize.Uint64(), ret)
+ }
+ scope.Contract.Gas += returnGas
+
+ interpreter.returnData = ret
+ return ret, nil
+}
+
+func opReturn(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ offset, size := scope.Stack.pop(), scope.Stack.pop()
+ ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
+
+ return ret, errStopToken
+}
+
+func opRevert(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ offset, size := scope.Stack.pop(), scope.Stack.pop()
+ ret := scope.Memory.GetPtr(int64(offset.Uint64()), int64(size.Uint64()))
+
+ interpreter.returnData = ret
+ return ret, ErrExecutionReverted
+}
+
+func opUndefined(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ return nil, &ErrInvalidOpCode{opcode: OpCode(scope.Contract.Code[*pc])}
+}
+
+func opStop(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ return nil, errStopToken
+}
+
+func opSelfdestruct(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ beneficiary := scope.Stack.pop()
+ balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
+ interpreter.evm.StateDB.AddBalance(beneficiary.Bytes20(), balance)
+ interpreter.evm.StateDB.Suicide(scope.Contract.Address())
+ if interpreter.evm.Config.Debug {
+ interpreter.evm.Config.Tracer.CaptureEnter(SELFDESTRUCT, scope.Contract.Address(), beneficiary.Bytes20(), []byte{}, 0, balance)
+ interpreter.evm.Config.Tracer.CaptureExit([]byte{}, 0, nil)
+ }
+ return nil, errStopToken
+}
+
+// following functions are used by the instruction jump table
+
+// make log instruction function
+func makeLog(size int) executionFunc {
+ return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ if interpreter.readOnly {
+ return nil, ErrWriteProtection
+ }
+ topics := make([]common.Hash, size)
+ stack := scope.Stack
+ mStart, mSize := stack.pop(), stack.pop()
+ for i := 0; i < size; i++ {
+ addr := stack.pop()
+ topics[i] = addr.Bytes32()
+ }
+
+ d := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
+ interpreter.evm.StateDB.AddLog(&types.Log{
+ Address: scope.Contract.Address(),
+ Topics: topics,
+ Data: d,
+ // This is a non-consensus field, but assigned here because
+ // core/state doesn't know the current block number.
+ BlockNumber: interpreter.evm.Context.BlockNumber.Uint64(),
+ })
+
+ return nil, nil
+ }
+}
+
+// opPush1 is a specialized version of pushN
+func opPush1(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ var (
+ codeLen = uint64(len(scope.Contract.Code))
+ integer = new(uint256.Int)
+ )
+ *pc += 1
+ if *pc < codeLen {
+ scope.Stack.Push(integer.SetUint64(uint64(scope.Contract.Code[*pc])))
+ } else {
+ scope.Stack.Push(integer.Clear())
+ }
+ return nil, nil
+}
+
+// make push instruction function
+func makePush(size uint64, pushByteSize int) executionFunc {
+ return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ codeLen := len(scope.Contract.Code)
+
+ startMin := codeLen
+ if int(*pc+1) < startMin {
+ startMin = int(*pc + 1)
+ }
+
+ endMin := codeLen
+ if startMin+pushByteSize < endMin {
+ endMin = startMin + pushByteSize
+ }
+
+ integer := new(uint256.Int)
+ scope.Stack.Push(integer.SetBytes(common.RightPadBytes(
+ scope.Contract.Code[startMin:endMin], pushByteSize)))
+
+ *pc += size
+ return nil, nil
+ }
+}
+
+// make dup instruction function
+func makeDup(size int64) executionFunc {
+ return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.dup(int(size))
+ return nil, nil
+ }
+}
+
+// make swap instruction function
+func makeSwap(size int64) executionFunc {
+ // switch n + 1 otherwise n would be swapped with n
+ size++
+ return func(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]byte, error) {
+ scope.Stack.swap(int(size))
+ return nil, nil
+ }
+}
diff --git a/state/runtime/fakevm/interpreter.go b/state/runtime/fakevm/interpreter.go
index cd1984066a..a291b15411 100644
--- a/state/runtime/fakevm/interpreter.go
+++ b/state/runtime/fakevm/interpreter.go
@@ -1,6 +1,27 @@
+// 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 .
+
package fakevm
-import "github.com/ethereum/go-ethereum/core/vm"
+import (
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/log"
+)
// Config are the configuration options for the Interpreter
type Config struct {
@@ -8,8 +29,212 @@ type Config struct {
Tracer EVMLogger // Opcode logger
NoBaseFee bool // Forces the EIP-1559 baseFee to 0 (needed for 0 price calls)
EnablePreimageRecording bool // Enables recording of SHA3/keccak preimages
+ ExtraEips []int // Additional EIPS that are to be enabled
+}
+
+// ScopeContext contains the things that are per-call, such as stack and memory,
+// but not transients like pc and gas
+type ScopeContext struct {
+ Memory *Memory
+ Stack *Stack
+ Contract *Contract
+}
+
+// EVMInterpreter represents an EVM interpreter
+type EVMInterpreter struct {
+ evm *FakeEVM
+ table *JumpTable
+
+ hasher crypto.KeccakState // Keccak256 hasher instance shared across opcodes
+ hasherBuf common.Hash // Keccak256 hasher result array shared aross opcodes
+
+ readOnly bool // Whether to throw on stateful modifications
+ returnData []byte // Last CALL's return data for subsequent reuse
+}
+
+// NewEVMInterpreter returns a new instance of the Interpreter.
+func NewEVMInterpreter(evm *FakeEVM) *EVMInterpreter {
+ // If jump table was not initialised we set the default one.
+ var table *JumpTable
+ switch {
+ case evm.chainRules.IsShanghai:
+ table = &shanghaiInstructionSet
+ case evm.chainRules.IsMerge:
+ table = &mergeInstructionSet
+ case evm.chainRules.IsLondon:
+ table = &londonInstructionSet
+ case evm.chainRules.IsBerlin:
+ table = &berlinInstructionSet
+ case evm.chainRules.IsIstanbul:
+ table = &istanbulInstructionSet
+ case evm.chainRules.IsConstantinople:
+ table = &constantinopleInstructionSet
+ case evm.chainRules.IsByzantium:
+ table = &byzantiumInstructionSet
+ case evm.chainRules.IsEIP158:
+ table = &spuriousDragonInstructionSet
+ case evm.chainRules.IsEIP150:
+ table = &tangerineWhistleInstructionSet
+ case evm.chainRules.IsHomestead:
+ table = &homesteadInstructionSet
+ default:
+ table = &frontierInstructionSet
+ }
+ var extraEips []int
+ if len(evm.Config.ExtraEips) > 0 {
+ // Deep-copy jumptable to prevent modification of opcodes in other tables
+ table = copyJumpTable(table)
+ }
+ for _, eip := range evm.Config.ExtraEips {
+ if err := EnableEIP(eip, table); err != nil {
+ // Disable it, so caller can check if it's activated or not
+ log.Error("EIP activation failed", "eip", eip, "error", err)
+ } else {
+ extraEips = append(extraEips, eip)
+ }
+ }
+ evm.Config.ExtraEips = extraEips
+ return &EVMInterpreter{evm: evm, table: table}
+}
+
+// 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.
+//
+// It's important to note that any errors returned by the interpreter should be
+// considered a revert-and-consume-all-gas operation except for
+// ErrExecutionReverted which means revert-and-keep-gas-left.
+func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (ret []byte, err error) {
+ // Increment the call depth which is restricted to 1024
+ in.evm.depth++
+ defer func() { in.evm.depth-- }()
+
+ // Make sure the readOnly is only set if we aren't in readOnly yet.
+ // This also makes sure that the readOnly flag isn't removed for child calls.
+ if readOnly && !in.readOnly {
+ in.readOnly = true
+ defer func() { in.readOnly = false }()
+ }
+
+ // Reset the previous call's return data. It's unimportant to preserve the old buffer
+ // as every returning call will return new data anyway.
+ in.returnData = nil
+
+ // Don't bother with the execution if there's no code.
+ if len(contract.Code) == 0 {
+ return nil, nil
+ }
+
+ var (
+ op OpCode // current opcode
+ mem = NewMemory() // bound memory
+ stack = NewStack() // local stack
+ callContext = &ScopeContext{
+ Memory: mem,
+ Stack: stack,
+ Contract: contract,
+ }
+ // For optimisation reason we're using uint64 as the program counter.
+ // It's theoretically possible to go above 2^64. The YP defines the PC
+ // to be uint256. Practically much less so feasible.
+ pc = uint64(0) // program counter
+ cost uint64
+ // copies used by tracer
+ pcCopy uint64 // needed for the deferred EVMLogger
+ gasCopy uint64 // for EVMLogger to log gas remaining before execution
+ logged bool // deferred EVMLogger should ignore already logged steps
+ res []byte // result of the opcode execution function
+ )
+ // Don't move this deferred function, it's placed before the capturestate-deferred method,
+ // so that it get's executed _after_: the capturestate needs the stacks before
+ // they are returned to the pools
+ defer func() {
+ returnStack(stack)
+ }()
+ contract.Input = input
+
+ if in.evm.Config.Debug {
+ defer func() {
+ if err != nil {
+ if !logged {
+ in.evm.Config.Tracer.CaptureState(pcCopy, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ } else {
+ in.evm.Config.Tracer.CaptureFault(pcCopy, op, gasCopy, cost, callContext, in.evm.depth, err)
+ }
+ }
+ }()
+ }
+ // The Interpreter main run loop (contextual). This loop runs until either an
+ // explicit STOP, RETURN or SELFDESTRUCT is executed, an error occurred during
+ // the execution of one of the operations or until the done flag is set by the
+ // parent context.
+ for {
+ if in.evm.Config.Debug {
+ // Capture pre-execution values for tracing.
+ logged, pcCopy, gasCopy = false, pc, contract.Gas
+ }
+ // Get the operation from the jump table and validate the stack to ensure there are
+ // enough stack items available to perform the operation.
+ op = contract.GetOp(pc)
+ operation := in.table[op]
+ cost = operation.constantGas // For tracing
+ // Validate stack
+ if sLen := stack.len(); sLen < operation.minStack {
+ return nil, &ErrStackUnderflow{stackLen: sLen, required: operation.minStack}
+ } else if sLen > operation.maxStack {
+ return nil, &ErrStackOverflow{stackLen: sLen, limit: operation.maxStack}
+ }
+ if !contract.UseGas(cost) {
+ return nil, ErrOutOfGas
+ }
+ if operation.dynamicGas != nil {
+ // All ops with a dynamic memory usage also has a dynamic gas cost.
+ var memorySize uint64
+ // calculate the new memory size and expand the memory to fit
+ // the operation
+ // Memory check needs to be done prior to evaluating the dynamic gas portion,
+ // to detect calculation overflows
+ if operation.memorySize != nil {
+ memSize, overflow := operation.memorySize(stack)
+ if overflow {
+ return nil, ErrGasUintOverflow
+ }
+ // memory is expanded in words of 32 bytes. Gas
+ // is also calculated in words.
+ if memorySize, overflow = math.SafeMul(toWordSize(memSize), 32); overflow {
+ return nil, ErrGasUintOverflow
+ }
+ }
+ // Consume the gas and return an error if not enough gas is available.
+ // cost is explicitly set so that the capture state defer method can get the proper cost
+ var dynamicCost uint64
+ dynamicCost, err = operation.dynamicGas(in.evm, contract, stack, mem, memorySize)
+ cost += dynamicCost // for tracing
+ if err != nil || !contract.UseGas(dynamicCost) {
+ return nil, ErrOutOfGas
+ }
+ // Do tracing before memory expansion
+ if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ logged = true
+ }
+ if memorySize > 0 {
+ mem.Resize(memorySize)
+ }
+ } else if in.evm.Config.Debug {
+ in.evm.Config.Tracer.CaptureState(pc, op, gasCopy, cost, callContext, in.returnData, in.evm.depth, err)
+ logged = true
+ }
+ // execute the operation
+ res, err = operation.execute(&pc, in, callContext)
+ if err != nil {
+ break
+ }
+ pc++
+ }
- JumpTable *vm.JumpTable // EVM instruction table, automatically populated if unset
+ if err == errStopToken {
+ err = nil // clear stop token error
+ }
- ExtraEips []int // Additional EIPS that are to be enabled
+ return res, err
}
diff --git a/state/runtime/fakevm/jump_table.go b/state/runtime/fakevm/jump_table.go
new file mode 100644
index 0000000000..ba3aa98a89
--- /dev/null
+++ b/state/runtime/fakevm/jump_table.go
@@ -0,0 +1,1064 @@
+// Copyright 2015 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 .
+
+package fakevm
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/params"
+)
+
+type (
+ executionFunc func(pc *uint64, interpreter *EVMInterpreter, callContext *ScopeContext) ([]byte, error)
+ gasFunc func(*FakeEVM, *Contract, *Stack, *Memory, uint64) (uint64, error) // last parameter is the requested memory size as a uint64
+ // memorySizeFunc returns the required size, and whether the operation overflowed a uint64
+ memorySizeFunc func(*Stack) (size uint64, overflow bool)
+)
+
+type operation struct {
+ // execute is the operation function
+ execute executionFunc
+ constantGas uint64
+ dynamicGas gasFunc
+ // minStack tells how many stack items are required
+ minStack int
+ // maxStack specifies the max length the stack can have for this operation
+ // to not overflow the stack.
+ maxStack int
+
+ // memorySize returns the memory size required for the operation
+ memorySize memorySizeFunc
+}
+
+var (
+ frontierInstructionSet = newFrontierInstructionSet()
+ homesteadInstructionSet = newHomesteadInstructionSet()
+ tangerineWhistleInstructionSet = newTangerineWhistleInstructionSet()
+ spuriousDragonInstructionSet = newSpuriousDragonInstructionSet()
+ byzantiumInstructionSet = newByzantiumInstructionSet()
+ constantinopleInstructionSet = newConstantinopleInstructionSet()
+ istanbulInstructionSet = newIstanbulInstructionSet()
+ berlinInstructionSet = newBerlinInstructionSet()
+ londonInstructionSet = newLondonInstructionSet()
+ mergeInstructionSet = newMergeInstructionSet()
+ shanghaiInstructionSet = newShanghaiInstructionSet()
+)
+
+// JumpTable contains the EVM opcodes supported at a given fork.
+type JumpTable [256]*operation
+
+func validate(jt JumpTable) JumpTable {
+ for i, op := range jt {
+ if op == nil {
+ panic(fmt.Sprintf("op %#x is not set", i))
+ }
+ // The interpreter has an assumption that if the memorySize function is
+ // set, then the dynamicGas function is also set. This is a somewhat
+ // arbitrary assumption, and can be removed if we need to -- but it
+ // allows us to avoid a condition check. As long as we have that assumption
+ // in there, this little sanity check prevents us from merging in a
+ // change which violates it.
+ if op.memorySize != nil && op.dynamicGas == nil {
+ panic(fmt.Sprintf("op %v has dynamic memory but not dynamic gas", OpCode(i).String()))
+ }
+ }
+ return jt
+}
+
+func newShanghaiInstructionSet() JumpTable {
+ instructionSet := newMergeInstructionSet()
+ enable3855(&instructionSet) // PUSH0 instruction
+ enable3860(&instructionSet) // Limit and meter initcode
+ return validate(instructionSet)
+}
+
+func newMergeInstructionSet() JumpTable {
+ instructionSet := newLondonInstructionSet()
+ instructionSet[PREVRANDAO] = &operation{
+ execute: opRandom,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+ return validate(instructionSet)
+}
+
+// newLondonInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg, berlin and london instructions.
+func newLondonInstructionSet() JumpTable {
+ instructionSet := newBerlinInstructionSet()
+ enable3529(&instructionSet) // EIP-3529: Reduction in refunds https://eips.ethereum.org/EIPS/eip-3529
+ enable3198(&instructionSet) // Base fee opcode https://eips.ethereum.org/EIPS/eip-3198
+ return validate(instructionSet)
+}
+
+// newBerlinInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul, petersburg and berlin instructions.
+func newBerlinInstructionSet() JumpTable {
+ instructionSet := newIstanbulInstructionSet()
+ enable2929(&instructionSet) // Access lists for trie accesses https://eips.ethereum.org/EIPS/eip-2929
+ return validate(instructionSet)
+}
+
+// newIstanbulInstructionSet returns the frontier, homestead, byzantium,
+// constantinople, istanbul and petersburg instructions.
+func newIstanbulInstructionSet() JumpTable {
+ instructionSet := newConstantinopleInstructionSet()
+
+ enable1344(&instructionSet) // ChainID opcode - https://eips.ethereum.org/EIPS/eip-1344
+ enable1884(&instructionSet) // Reprice reader opcodes - https://eips.ethereum.org/EIPS/eip-1884
+ enable2200(&instructionSet) // Net metered SSTORE - https://eips.ethereum.org/EIPS/eip-2200
+
+ return validate(instructionSet)
+}
+
+// newConstantinopleInstructionSet returns the frontier, homestead,
+// byzantium and constantinople instructions.
+func newConstantinopleInstructionSet() JumpTable {
+ instructionSet := newByzantiumInstructionSet()
+ instructionSet[SHL] = &operation{
+ execute: opSHL,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ }
+ instructionSet[SHR] = &operation{
+ execute: opSHR,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ }
+ instructionSet[SAR] = &operation{
+ execute: opSAR,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ }
+ instructionSet[EXTCODEHASH] = &operation{
+ execute: opExtCodeHash,
+ constantGas: params.ExtcodeHashGasConstantinople,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ }
+ instructionSet[CREATE2] = &operation{
+ execute: opCreate2,
+ constantGas: params.Create2Gas,
+ dynamicGas: gasCreate2,
+ minStack: minStack(4, 1),
+ maxStack: maxStack(4, 1),
+ memorySize: memoryCreate2,
+ }
+ return validate(instructionSet)
+}
+
+// newByzantiumInstructionSet returns the frontier, homestead and
+// byzantium instructions.
+func newByzantiumInstructionSet() JumpTable {
+ instructionSet := newSpuriousDragonInstructionSet()
+ instructionSet[STATICCALL] = &operation{
+ execute: opStaticCall,
+ constantGas: params.CallGasEIP150,
+ dynamicGas: gasStaticCall,
+ minStack: minStack(6, 1),
+ maxStack: maxStack(6, 1),
+ memorySize: memoryStaticCall,
+ }
+ instructionSet[RETURNDATASIZE] = &operation{
+ execute: opReturnDataSize,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ }
+ instructionSet[RETURNDATACOPY] = &operation{
+ execute: opReturnDataCopy,
+ constantGas: GasFastestStep,
+ dynamicGas: gasReturnDataCopy,
+ minStack: minStack(3, 0),
+ maxStack: maxStack(3, 0),
+ memorySize: memoryReturnDataCopy,
+ }
+ instructionSet[REVERT] = &operation{
+ execute: opRevert,
+ dynamicGas: gasRevert,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ memorySize: memoryRevert,
+ }
+ return validate(instructionSet)
+}
+
+// EIP 158 a.k.a Spurious Dragon
+func newSpuriousDragonInstructionSet() JumpTable {
+ instructionSet := newTangerineWhistleInstructionSet()
+ instructionSet[EXP].dynamicGas = gasExpEIP158
+ return validate(instructionSet)
+}
+
+// EIP 150 a.k.a Tangerine Whistle
+func newTangerineWhistleInstructionSet() JumpTable {
+ instructionSet := newHomesteadInstructionSet()
+ instructionSet[BALANCE].constantGas = params.BalanceGasEIP150
+ instructionSet[EXTCODESIZE].constantGas = params.ExtcodeSizeGasEIP150
+ instructionSet[SLOAD].constantGas = params.SloadGasEIP150
+ instructionSet[EXTCODECOPY].constantGas = params.ExtcodeCopyBaseEIP150
+ instructionSet[CALL].constantGas = params.CallGasEIP150
+ instructionSet[CALLCODE].constantGas = params.CallGasEIP150
+ instructionSet[DELEGATECALL].constantGas = params.CallGasEIP150
+ return validate(instructionSet)
+}
+
+// newHomesteadInstructionSet returns the frontier and homestead
+// instructions that can be executed during the homestead phase.
+func newHomesteadInstructionSet() JumpTable {
+ instructionSet := newFrontierInstructionSet()
+ instructionSet[DELEGATECALL] = &operation{
+ execute: opDelegateCall,
+ dynamicGas: gasDelegateCall,
+ constantGas: params.CallGasFrontier,
+ minStack: minStack(6, 1),
+ maxStack: maxStack(6, 1),
+ memorySize: memoryDelegateCall,
+ }
+ return validate(instructionSet)
+}
+
+// newFrontierInstructionSet returns the frontier instructions
+// that can be executed during the frontier phase.
+func newFrontierInstructionSet() JumpTable {
+ tbl := JumpTable{
+ STOP: {
+ execute: opStop,
+ constantGas: 0,
+ minStack: minStack(0, 0),
+ maxStack: maxStack(0, 0),
+ },
+ ADD: {
+ execute: opAdd,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ MUL: {
+ execute: opMul,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SUB: {
+ execute: opSub,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ DIV: {
+ execute: opDiv,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SDIV: {
+ execute: opSdiv,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ MOD: {
+ execute: opMod,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SMOD: {
+ execute: opSmod,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ ADDMOD: {
+ execute: opAddmod,
+ constantGas: GasMidStep,
+ minStack: minStack(3, 1),
+ maxStack: maxStack(3, 1),
+ },
+ MULMOD: {
+ execute: opMulmod,
+ constantGas: GasMidStep,
+ minStack: minStack(3, 1),
+ maxStack: maxStack(3, 1),
+ },
+ EXP: {
+ execute: opExp,
+ dynamicGas: gasExpFrontier,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SIGNEXTEND: {
+ execute: opSignExtend,
+ constantGas: GasFastStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ LT: {
+ execute: opLt,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ GT: {
+ execute: opGt,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SLT: {
+ execute: opSlt,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ SGT: {
+ execute: opSgt,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ EQ: {
+ execute: opEq,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ ISZERO: {
+ execute: opIszero,
+ constantGas: GasFastestStep,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ AND: {
+ execute: opAnd,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ XOR: {
+ execute: opXor,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ OR: {
+ execute: opOr,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ NOT: {
+ execute: opNot,
+ constantGas: GasFastestStep,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ BYTE: {
+ execute: opByte,
+ constantGas: GasFastestStep,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ },
+ KECCAK256: {
+ execute: opKeccak256,
+ constantGas: params.Keccak256Gas,
+ dynamicGas: gasKeccak256,
+ minStack: minStack(2, 1),
+ maxStack: maxStack(2, 1),
+ memorySize: memoryKeccak256,
+ },
+ ADDRESS: {
+ execute: opAddress,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ BALANCE: {
+ execute: opBalance,
+ constantGas: params.BalanceGasFrontier,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ ORIGIN: {
+ execute: opOrigin,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ CALLER: {
+ execute: opCaller,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ CALLVALUE: {
+ execute: opCallValue,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ CALLDATALOAD: {
+ execute: opCallDataLoad,
+ constantGas: GasFastestStep,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ CALLDATASIZE: {
+ execute: opCallDataSize,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ CALLDATACOPY: {
+ execute: opCallDataCopy,
+ constantGas: GasFastestStep,
+ dynamicGas: gasCallDataCopy,
+ minStack: minStack(3, 0),
+ maxStack: maxStack(3, 0),
+ memorySize: memoryCallDataCopy,
+ },
+ CODESIZE: {
+ execute: opCodeSize,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ CODECOPY: {
+ execute: opCodeCopy,
+ constantGas: GasFastestStep,
+ dynamicGas: gasCodeCopy,
+ minStack: minStack(3, 0),
+ maxStack: maxStack(3, 0),
+ memorySize: memoryCodeCopy,
+ },
+ GASPRICE: {
+ execute: opGasprice,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ EXTCODESIZE: {
+ execute: opExtCodeSize,
+ constantGas: params.ExtcodeSizeGasFrontier,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ EXTCODECOPY: {
+ execute: opExtCodeCopy,
+ constantGas: params.ExtcodeCopyBaseFrontier,
+ dynamicGas: gasExtCodeCopy,
+ minStack: minStack(4, 0),
+ maxStack: maxStack(4, 0),
+ memorySize: memoryExtCodeCopy,
+ },
+ BLOCKHASH: {
+ execute: opBlockhash,
+ constantGas: GasExtStep,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ COINBASE: {
+ execute: opCoinbase,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ TIMESTAMP: {
+ execute: opTimestamp,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ NUMBER: {
+ execute: opNumber,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ DIFFICULTY: {
+ execute: opDifficulty,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ GASLIMIT: {
+ execute: opGasLimit,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ POP: {
+ execute: opPop,
+ constantGas: GasQuickStep,
+ minStack: minStack(1, 0),
+ maxStack: maxStack(1, 0),
+ },
+ MLOAD: {
+ execute: opMload,
+ constantGas: GasFastestStep,
+ dynamicGas: gasMLoad,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ memorySize: memoryMLoad,
+ },
+ MSTORE: {
+ execute: opMstore,
+ constantGas: GasFastestStep,
+ dynamicGas: gasMStore,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ memorySize: memoryMStore,
+ },
+ MSTORE8: {
+ execute: opMstore8,
+ constantGas: GasFastestStep,
+ dynamicGas: gasMStore8,
+ memorySize: memoryMStore8,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ },
+ SLOAD: {
+ execute: opSload,
+ constantGas: params.SloadGasFrontier,
+ minStack: minStack(1, 1),
+ maxStack: maxStack(1, 1),
+ },
+ SSTORE: {
+ execute: opSstore,
+ dynamicGas: gasSStore,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ },
+ JUMP: {
+ execute: opJump,
+ constantGas: GasMidStep,
+ minStack: minStack(1, 0),
+ maxStack: maxStack(1, 0),
+ },
+ JUMPI: {
+ execute: opJumpi,
+ constantGas: GasSlowStep,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ },
+ PC: {
+ execute: opPc,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ MSIZE: {
+ execute: opMsize,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ GAS: {
+ execute: opGas,
+ constantGas: GasQuickStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ JUMPDEST: {
+ execute: opJumpdest,
+ constantGas: params.JumpdestGas,
+ minStack: minStack(0, 0),
+ maxStack: maxStack(0, 0),
+ },
+ PUSH1: {
+ execute: opPush1,
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH2: {
+ execute: makePush(2, 2),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH3: {
+ execute: makePush(3, 3),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH4: {
+ execute: makePush(4, 4),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH5: {
+ execute: makePush(5, 5),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH6: {
+ execute: makePush(6, 6),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH7: {
+ execute: makePush(7, 7),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH8: {
+ execute: makePush(8, 8),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH9: {
+ execute: makePush(9, 9),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH10: {
+ execute: makePush(10, 10),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH11: {
+ execute: makePush(11, 11),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH12: {
+ execute: makePush(12, 12),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH13: {
+ execute: makePush(13, 13),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH14: {
+ execute: makePush(14, 14),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH15: {
+ execute: makePush(15, 15),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH16: {
+ execute: makePush(16, 16),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH17: {
+ execute: makePush(17, 17),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH18: {
+ execute: makePush(18, 18),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH19: {
+ execute: makePush(19, 19),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH20: {
+ execute: makePush(20, 20),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH21: {
+ execute: makePush(21, 21),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH22: {
+ execute: makePush(22, 22),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH23: {
+ execute: makePush(23, 23),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH24: {
+ execute: makePush(24, 24),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH25: {
+ execute: makePush(25, 25),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH26: {
+ execute: makePush(26, 26),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH27: {
+ execute: makePush(27, 27),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH28: {
+ execute: makePush(28, 28),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH29: {
+ execute: makePush(29, 29),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH30: {
+ execute: makePush(30, 30),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH31: {
+ execute: makePush(31, 31),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ PUSH32: {
+ execute: makePush(32, 32),
+ constantGas: GasFastestStep,
+ minStack: minStack(0, 1),
+ maxStack: maxStack(0, 1),
+ },
+ DUP1: {
+ execute: makeDup(1),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(1),
+ maxStack: maxDupStack(1),
+ },
+ DUP2: {
+ execute: makeDup(2),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(2),
+ maxStack: maxDupStack(2),
+ },
+ DUP3: {
+ execute: makeDup(3),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(3),
+ maxStack: maxDupStack(3),
+ },
+ DUP4: {
+ execute: makeDup(4),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(4),
+ maxStack: maxDupStack(4),
+ },
+ DUP5: {
+ execute: makeDup(5),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(5),
+ maxStack: maxDupStack(5),
+ },
+ DUP6: {
+ execute: makeDup(6),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(6),
+ maxStack: maxDupStack(6),
+ },
+ DUP7: {
+ execute: makeDup(7),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(7),
+ maxStack: maxDupStack(7),
+ },
+ DUP8: {
+ execute: makeDup(8),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(8),
+ maxStack: maxDupStack(8),
+ },
+ DUP9: {
+ execute: makeDup(9),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(9),
+ maxStack: maxDupStack(9),
+ },
+ DUP10: {
+ execute: makeDup(10),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(10),
+ maxStack: maxDupStack(10),
+ },
+ DUP11: {
+ execute: makeDup(11),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(11),
+ maxStack: maxDupStack(11),
+ },
+ DUP12: {
+ execute: makeDup(12),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(12),
+ maxStack: maxDupStack(12),
+ },
+ DUP13: {
+ execute: makeDup(13),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(13),
+ maxStack: maxDupStack(13),
+ },
+ DUP14: {
+ execute: makeDup(14),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(14),
+ maxStack: maxDupStack(14),
+ },
+ DUP15: {
+ execute: makeDup(15),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(15),
+ maxStack: maxDupStack(15),
+ },
+ DUP16: {
+ execute: makeDup(16),
+ constantGas: GasFastestStep,
+ minStack: minDupStack(16),
+ maxStack: maxDupStack(16),
+ },
+ SWAP1: {
+ execute: makeSwap(1),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(2),
+ maxStack: maxSwapStack(2),
+ },
+ SWAP2: {
+ execute: makeSwap(2),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(3),
+ maxStack: maxSwapStack(3),
+ },
+ SWAP3: {
+ execute: makeSwap(3),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(4),
+ maxStack: maxSwapStack(4),
+ },
+ SWAP4: {
+ execute: makeSwap(4),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(5),
+ maxStack: maxSwapStack(5),
+ },
+ SWAP5: {
+ execute: makeSwap(5),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(6),
+ maxStack: maxSwapStack(6),
+ },
+ SWAP6: {
+ execute: makeSwap(6),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(7),
+ maxStack: maxSwapStack(7),
+ },
+ SWAP7: {
+ execute: makeSwap(7),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(8),
+ maxStack: maxSwapStack(8),
+ },
+ SWAP8: {
+ execute: makeSwap(8),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(9),
+ maxStack: maxSwapStack(9),
+ },
+ SWAP9: {
+ execute: makeSwap(9),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(10),
+ maxStack: maxSwapStack(10),
+ },
+ SWAP10: {
+ execute: makeSwap(10),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(11),
+ maxStack: maxSwapStack(11),
+ },
+ SWAP11: {
+ execute: makeSwap(11),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(12),
+ maxStack: maxSwapStack(12),
+ },
+ SWAP12: {
+ execute: makeSwap(12),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(13),
+ maxStack: maxSwapStack(13),
+ },
+ SWAP13: {
+ execute: makeSwap(13),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(14),
+ maxStack: maxSwapStack(14),
+ },
+ SWAP14: {
+ execute: makeSwap(14),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(15),
+ maxStack: maxSwapStack(15),
+ },
+ SWAP15: {
+ execute: makeSwap(15),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(16),
+ maxStack: maxSwapStack(16),
+ },
+ SWAP16: {
+ execute: makeSwap(16),
+ constantGas: GasFastestStep,
+ minStack: minSwapStack(17),
+ maxStack: maxSwapStack(17),
+ },
+ LOG0: {
+ execute: makeLog(0),
+ dynamicGas: makeGasLog(0),
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ memorySize: memoryLog,
+ },
+ LOG1: {
+ execute: makeLog(1),
+ dynamicGas: makeGasLog(1),
+ minStack: minStack(3, 0),
+ maxStack: maxStack(3, 0),
+ memorySize: memoryLog,
+ },
+ LOG2: {
+ execute: makeLog(2),
+ dynamicGas: makeGasLog(2),
+ minStack: minStack(4, 0),
+ maxStack: maxStack(4, 0),
+ memorySize: memoryLog,
+ },
+ LOG3: {
+ execute: makeLog(3),
+ dynamicGas: makeGasLog(3),
+ minStack: minStack(5, 0),
+ maxStack: maxStack(5, 0),
+ memorySize: memoryLog,
+ },
+ LOG4: {
+ execute: makeLog(4),
+ dynamicGas: makeGasLog(4),
+ minStack: minStack(6, 0),
+ maxStack: maxStack(6, 0),
+ memorySize: memoryLog,
+ },
+ CREATE: {
+ execute: opCreate,
+ constantGas: params.CreateGas,
+ dynamicGas: gasCreate,
+ minStack: minStack(3, 1),
+ maxStack: maxStack(3, 1),
+ memorySize: memoryCreate,
+ },
+ CALL: {
+ execute: opCall,
+ constantGas: params.CallGasFrontier,
+ dynamicGas: gasCall,
+ minStack: minStack(7, 1),
+ maxStack: maxStack(7, 1),
+ memorySize: memoryCall,
+ },
+ CALLCODE: {
+ execute: opCallCode,
+ constantGas: params.CallGasFrontier,
+ dynamicGas: gasCallCode,
+ minStack: minStack(7, 1),
+ maxStack: maxStack(7, 1),
+ memorySize: memoryCall,
+ },
+ RETURN: {
+ execute: opReturn,
+ dynamicGas: gasReturn,
+ minStack: minStack(2, 0),
+ maxStack: maxStack(2, 0),
+ memorySize: memoryReturn,
+ },
+ SELFDESTRUCT: {
+ execute: opSelfdestruct,
+ dynamicGas: gasSelfdestruct,
+ minStack: minStack(1, 0),
+ maxStack: maxStack(1, 0),
+ },
+ }
+
+ // Fill all unassigned slots with opUndefined.
+ for i, entry := range tbl {
+ if entry == nil {
+ tbl[i] = &operation{execute: opUndefined, maxStack: maxStack(0, 0)}
+ }
+ }
+
+ return validate(tbl)
+}
+
+func copyJumpTable(source *JumpTable) *JumpTable {
+ dest := *source
+ for i, op := range source {
+ if op != nil {
+ opCopy := *op
+ dest[i] = &opCopy
+ }
+ }
+ return &dest
+}
diff --git a/state/runtime/fakevm/logger.go b/state/runtime/fakevm/logger.go
index cfd316d6dc..3595c003ad 100644
--- a/state/runtime/fakevm/logger.go
+++ b/state/runtime/fakevm/logger.go
@@ -1,4 +1,4 @@
-// Copyright 2021 The go-ethereum Authors
+// Copyright 2015 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
@@ -18,10 +18,8 @@ package fakevm
import (
"math/big"
- "time"
"github.com/ethereum/go-ethereum/common"
- "github.com/ethereum/go-ethereum/core/vm"
)
// EVMLogger is used to collect execution traces from an EVM transaction
@@ -35,11 +33,11 @@ type EVMLogger interface {
CaptureTxEnd(restGas uint64)
// Top call frame
CaptureStart(env *FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
- CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
+ CaptureEnd(output []byte, gasUsed uint64, err error)
// Rest of call frames
- CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
+ CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
CaptureExit(output []byte, gasUsed uint64, err error)
// Opcode level
- CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
- CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
+ CaptureState(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
+ CaptureFault(pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
}
diff --git a/state/runtime/fakevm/memory.go b/state/runtime/fakevm/memory.go
index e7453e4ef5..e88ef80152 100644
--- a/state/runtime/fakevm/memory.go
+++ b/state/runtime/fakevm/memory.go
@@ -17,14 +17,13 @@
package fakevm
import (
- "fmt"
-
"github.com/holiman/uint256"
)
// Memory implements a simple memory model for the ethereum virtual machine.
type Memory struct {
- store []byte
+ store []byte
+ lastGasCost uint64
}
// NewMemory returns a new memory model.
@@ -54,10 +53,9 @@ func (m *Memory) Set32(offset uint64, val *uint256.Int) {
if offset+32 > uint64(len(m.store)) {
panic("invalid memory: store empty")
}
- // Zero the memory area
- copy(m.store[offset:offset+32], []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})
// Fill in relevant bits
- val.WriteToSlice(m.store[offset:])
+ b32 := val.Bytes32()
+ copy(m.store[offset:], b32[:])
}
// Resize resizes the memory to size
@@ -105,18 +103,3 @@ func (m *Memory) Len() int {
func (m *Memory) Data() []byte {
return m.store
}
-
-// Print dumps the content of the memory.
-func (m *Memory) Print() {
- fmt.Printf("### mem %d bytes ###\n", len(m.store))
- if len(m.store) > 0 {
- addr := 0
- for i := 0; i+32 <= len(m.store); i += 32 {
- fmt.Printf("%03d: % x\n", addr, m.store[i:i+32])
- addr++
- }
- } else {
- fmt.Println("-- empty --")
- }
- fmt.Println("####################")
-}
diff --git a/state/runtime/fakevm/memory_table.go b/state/runtime/fakevm/memory_table.go
new file mode 100644
index 0000000000..7f9221b1c3
--- /dev/null
+++ b/state/runtime/fakevm/memory_table.go
@@ -0,0 +1,113 @@
+// Copyright 2017 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 .
+
+package fakevm
+
+func memoryKeccak256(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(1))
+}
+
+func memoryCallDataCopy(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(2))
+}
+
+func memoryReturnDataCopy(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(2))
+}
+
+func memoryCodeCopy(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(2))
+}
+
+func memoryExtCodeCopy(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(1), stack.Back(3))
+}
+
+func memoryMLoad(stack *Stack) (uint64, bool) {
+ return calcMemSize64WithUint(stack.Back(0), 32)
+}
+
+func memoryMStore8(stack *Stack) (uint64, bool) {
+ return calcMemSize64WithUint(stack.Back(0), 1)
+}
+
+func memoryMStore(stack *Stack) (uint64, bool) {
+ return calcMemSize64WithUint(stack.Back(0), 32)
+}
+
+func memoryCreate(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(1), stack.Back(2))
+}
+
+func memoryCreate2(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(1), stack.Back(2))
+}
+
+func memoryCall(stack *Stack) (uint64, bool) {
+ x, overflow := calcMemSize64(stack.Back(5), stack.Back(6))
+ if overflow {
+ return 0, true
+ }
+ y, overflow := calcMemSize64(stack.Back(3), stack.Back(4))
+ if overflow {
+ return 0, true
+ }
+ if x > y {
+ return x, false
+ }
+ return y, false
+}
+func memoryDelegateCall(stack *Stack) (uint64, bool) {
+ x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
+ if overflow {
+ return 0, true
+ }
+ y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
+ if overflow {
+ return 0, true
+ }
+ if x > y {
+ return x, false
+ }
+ return y, false
+}
+
+func memoryStaticCall(stack *Stack) (uint64, bool) {
+ x, overflow := calcMemSize64(stack.Back(4), stack.Back(5))
+ if overflow {
+ return 0, true
+ }
+ y, overflow := calcMemSize64(stack.Back(2), stack.Back(3))
+ if overflow {
+ return 0, true
+ }
+ if x > y {
+ return x, false
+ }
+ return y, false
+}
+
+func memoryReturn(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(1))
+}
+
+func memoryRevert(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(1))
+}
+
+func memoryLog(stack *Stack) (uint64, bool) {
+ return calcMemSize64(stack.Back(0), stack.Back(1))
+}
diff --git a/state/runtime/fakevm/opcodes.go b/state/runtime/fakevm/opcodes.go
index 844f397216..a1ba2c7271 100644
--- a/state/runtime/fakevm/opcodes.go
+++ b/state/runtime/fakevm/opcodes.go
@@ -1,465 +1,266 @@
+// 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 .
+
package fakevm
-// Ethereum Virtual Machine OpCode s
-// https://ethervm.io/#opcodes
+import (
+ "fmt"
+)
-// OpCode is the EVM opcode
+// OpCode is an EVM opcode
type OpCode byte
-const (
-
- // STOP halts execution of the contract
- STOP = 0x00
-
- // ADD performs (u)int256 addition modulo 2**256
- ADD = 0x01
-
- // MUL performs (u)int256 multiplication modulo 2**256
- MUL = 0x02
-
- // SUB performs (u)int256 subtraction modulo 2**256
- SUB = 0x03
-
- // DIV performs uint256 division
- DIV = 0x04
-
- // SDIV performs int256 division
- SDIV = 0x05
-
- // MOD performs uint256 modulus
- MOD = 0x06
-
- // SMOD performs int256 modulus
- SMOD = 0x07
-
- // ADDMOD performs (u)int256 addition modulo N
- ADDMOD = 0x08
-
- // MULMOD performs (u)int256 multiplication modulo N
- MULMOD = 0x09
-
- // EXP performs uint256 exponentiation modulo 2**256
- EXP = 0x0A
-
- // SIGNEXTEND performs sign extends x from (b + 1) * 8 bits to 256 bits.
- SIGNEXTEND = 0x0B
-
- // LT performs int256 comparison
- LT = 0x10
-
- // GT performs int256 comparison
- GT = 0x11
-
- // SLT performs int256 comparison
- SLT = 0x12
-
- // SGT performs int256 comparison
- SGT = 0x13
-
- // EQ performs (u)int256 equality
- EQ = 0x14
-
- // ISZERO checks if (u)int256 is zero
- ISZERO = 0x15
-
- // AND performs 256-bit bitwise and
- AND = 0x16
-
- // OR performs 256-bit bitwise or
- OR = 0x17
-
- // XOR performs 256-bit bitwise xor
- XOR = 0x18
-
- // NOT performs 256-bit bitwise not
- NOT = 0x19
-
- // BYTE returns the ith byte of (u)int256 x counting from most significant byte
- BYTE = 0x1A
-
- // SHL performs a shift left
- SHL = 0x1B
-
- // SHR performs a logical shift right
- SHR = 0x1C
-
- // SAR performs an arithmetic shift right
- SAR = 0x1D
-
- // SHA3 performs the keccak256 hash function
- SHA3 = 0x20
-
- // ADDRESS returns the address of the executing contract
- ADDRESS = 0x30
-
- // BALANCE returns the address balance in wei
- BALANCE = 0x31
-
- // ORIGIN returns the transaction origin address
- ORIGIN = 0x32
-
- // CALLER returns the message caller address
- CALLER = 0x33
-
- // CALLVALUE returns the message funds in wei
- CALLVALUE = 0x34
-
- // CALLDATALOAD reads a (u)int256 from message data
- CALLDATALOAD = 0x35
-
- // CALLDATASIZE returns the message data length in bytes
- CALLDATASIZE = 0x36
-
- // CALLDATACOPY copies the message data
- CALLDATACOPY = 0x37
-
- // CODESIZE returns the length of the executing contract's code in bytes
- CODESIZE = 0x38
-
- // CODECOPY copies the executing contract bytecode
- CODECOPY = 0x39
-
- // GASPRICE returns the gas price of the executing transaction, in wei per unit of gas
- GASPRICE = 0x3A
-
- // EXTCODESIZE returns the length of the contract bytecode at addr
- EXTCODESIZE = 0x3B
-
- // EXTCODECOPY copies the contract bytecode
- EXTCODECOPY = 0x3C
-
- // RETURNDATASIZE returns the size of the returned data from the last external call in bytes
- RETURNDATASIZE = 0x3D
-
- // RETURNDATACOPY copies the returned data
- RETURNDATACOPY = 0x3E
-
- // EXTCODEHASH returns the hash of the specified contract bytecode
- EXTCODEHASH = 0x3F
-
- // BLOCKHASH returns the hash of the specific block. Only valid for the last 256 most recent blocks
- BLOCKHASH = 0x40
-
- // COINBASE returns the address of the current block's miner
- COINBASE = 0x41
-
- // TIMESTAMP returns the current block's Unix timestamp in seconds
- TIMESTAMP = 0x42
-
- // NUMBER returns the current block's number
- NUMBER = 0x43
-
- // DIFFICULTY returns the current block's difficulty
- DIFFICULTY = 0x44
-
- // GASLIMIT returns the current block's gas limit
- GASLIMIT = 0x45
-
- // CHAINID returns the id of the chain
- CHAINID = 0x46
-
- // SELFBALANCE returns the balance of the current account
- SELFBALANCE = 0x47
-
- // POP pops a (u)int256 off the stack and discards it
- POP = 0x50
-
- // MLOAD reads a (u)int256 from memory
- MLOAD = 0x51
-
- // MSTORE writes a (u)int256 to memory
- MSTORE = 0x52
-
- // MSTORE8 writes a uint8 to memory
- MSTORE8 = 0x53
-
- // SLOAD reads a (u)int256 from storage
- SLOAD = 0x54
-
- // SSTORE writes a (u)int256 to storage
- SSTORE = 0x55
-
- // JUMP performs an unconditional jump
- JUMP = 0x56
-
- // JUMPI performs a conditional jump if condition is truthy
- JUMPI = 0x57
-
- // PC returns the program counter
- PC = 0x58
-
- // MSIZE returns the size of memory for this contract execution, in bytes
- MSIZE = 0x59
-
- // GAS returns the remaining gas
- GAS = 0x5A
-
- // JUMPDEST corresponds to a possible jump destination
- JUMPDEST = 0x5B
-
- // PUSH1 pushes a 1-byte value onto the stack
- PUSH1 = 0x60
-
- // PUSH2 pushes a 2-bytes value onto the stack
- PUSH2 = 0x61
-
- // PUSH3 pushes a 3-bytes value onto the stack
- PUSH3 = 0x62
-
- // PUSH4 pushes a 4-bytes value onto the stack
- PUSH4 = 0x63
-
- // PUSH5 pushes a 5-bytes value onto the stack
- PUSH5 = 0x64
-
- // PUSH6 pushes a 6-bytes value onto the stack
- PUSH6 = 0x65
-
- // PUSH7 pushes a 7-bytes value onto the stack
- PUSH7 = 0x66
-
- // PUSH8 pushes a 8-bytes value onto the stack
- PUSH8 = 0x67
-
- // PUSH9 pushes a 9-bytes value onto the stack
- PUSH9 = 0x68
-
- // PUSH10 pushes a 10-bytes value onto the stack
- PUSH10 = 0x69
-
- // PUSH11 pushes a 11-bytes value onto the stack
- PUSH11 = 0x6A
-
- // PUSH12 pushes a 12-bytes value onto the stack
- PUSH12 = 0x6B
-
- // PUSH13 pushes a 13-bytes value onto the stack
- PUSH13 = 0x6C
-
- // PUSH14 pushes a 14-bytes value onto the stack
- PUSH14 = 0x6D
-
- // PUSH15 pushes a 15-bytes value onto the stack
- PUSH15 = 0x6E
-
- // PUSH16 pushes a 16-bytes value onto the stack
- PUSH16 = 0x6F
-
- // PUSH17 pushes a 17-bytes value onto the stack
- PUSH17 = 0x70
-
- // PUSH18 pushes a 18-bytes value onto the stack
- PUSH18 = 0x71
-
- // PUSH19 pushes a 19-bytes value onto the stack
- PUSH19 = 0x72
-
- // PUSH20 pushes a 20-bytes value onto the stack
- PUSH20 = 0x73
-
- // PUSH21 pushes a 21-bytes value onto the stack
- PUSH21 = 0x74
-
- // PUSH22 pushes a 22-bytes value onto the stack
- PUSH22 = 0x75
-
- // PUSH23 pushes a 23-bytes value onto the stack
- PUSH23 = 0x76
-
- // PUSH24 pushes a 24-bytes value onto the stack
- PUSH24 = 0x77
-
- // PUSH25 pushes a 25-bytes value onto the stack
- PUSH25 = 0x78
-
- // PUSH26 pushes a 26-bytes value onto the stack
- PUSH26 = 0x79
-
- // PUSH27 pushes a 27-bytes value onto the stack
- PUSH27 = 0x7A
-
- // PUSH28 pushes a 28-bytes value onto the stack
- PUSH28 = 0x7B
-
- // PUSH29 pushes a 29-bytes value onto the stack
- PUSH29 = 0x7C
-
- // PUSH30 pushes a 30-bytes value onto the stack
- PUSH30 = 0x7D
-
- // PUSH31 pushes a 31-bytes value onto the stack
- PUSH31 = 0x7E
-
- // PUSH32 pushes a 32-byte value onto the stack
- PUSH32 = 0x7F
-
- // DUP1 clones the last value on the stack
- DUP1 = 0x80
-
- // DUP2 clones the 2nd last value on the stack
- DUP2 = 0x81
-
- // DUP3 clones the 3rd last value on the stack
- DUP3 = 0x82
-
- // DUP4 clones the 4th last value on the stack
- DUP4 = 0x83
-
- // DUP5 clones the 5th last value on the stack
- DUP5 = 0x84
-
- // DUP6 clones the 6th last value on the stack
- DUP6 = 0x85
-
- // DUP7 clones the 7th last value on the stack
- DUP7 = 0x86
-
- // DUP8 clones the 8th last value on the stack
- DUP8 = 0x87
-
- // DUP9 clones the 9th last value on the stack
- DUP9 = 0x88
-
- // DUP10 clones the 10th last value on the stack
- DUP10 = 0x89
-
- // DUP11 clones the 11th last value on the stack
- DUP11 = 0x8A
-
- // DUP12 clones the 12th last value on the stack
- DUP12 = 0x8B
-
- // DUP13 clones the 13th last value on the stack
- DUP13 = 0x8C
-
- // DUP14 clones the 14th last value on the stack
- DUP14 = 0x8D
-
- // DUP15 clones the 15th last value on the stack
- DUP15 = 0x8E
-
- // DUP16 clones the 16th last value on the stack
- DUP16 = 0x8F
-
- // SWAP1 swaps the last two values on the stack
- SWAP1 = 0x90
-
- // SWAP2 swaps the top of the stack with the 3rd last element
- SWAP2 = 0x91
-
- // SWAP3 swaps the top of the stack with the 4th last element
- SWAP3 = 0x92
-
- // SWAP4 swaps the top of the stack with the 5th last element
- SWAP4 = 0x93
-
- // SWAP5 swaps the top of the stack with the 6th last element
- SWAP5 = 0x94
-
- // SWAP6 swaps the top of the stack with the 7th last element
- SWAP6 = 0x95
-
- // SWAP7 swaps the top of the stack with the 8th last element
- SWAP7 = 0x96
-
- // SWAP8 swaps the top of the stack with the 9th last element
- SWAP8 = 0x97
-
- // SWAP9 swaps the top of the stack with the 10th last element
- SWAP9 = 0x98
-
- // SWAP10 swaps the top of the stack with the 11th last element
- SWAP10 = 0x99
-
- // SWAP11 swaps the top of the stack with the 12th last element
- SWAP11 = 0x9A
-
- // SWAP12 swaps the top of the stack with the 13th last element
- SWAP12 = 0x9B
-
- // SWAP13 swaps the top of the stack with the 14th last element
- SWAP13 = 0x9C
-
- // SWAP14 swaps the top of the stack with the 15th last element
- SWAP14 = 0x9D
-
- // SWAP15 swaps the top of the stack with the 16th last element
- SWAP15 = 0x9E
-
- // SWAP16 swaps the top of the stack with the 17th last element
- SWAP16 = 0x9F
-
- // LOG0 fires an event without topics
- LOG0 = 0xA0
-
- // LOG1 fires an event with one topic
- LOG1 = 0xA1
+// IsPush specifies if an opcode is a PUSH opcode.
+func (op OpCode) IsPush() bool {
+ return PUSH1 <= op && op <= PUSH32
+}
- // LOG2 fires an event with two topics
- LOG2 = 0xA2
+// 0x0 range - arithmetic ops.
+const (
+ STOP OpCode = 0x0
+ ADD OpCode = 0x1
+ MUL OpCode = 0x2
+ SUB OpCode = 0x3
+ DIV OpCode = 0x4
+ SDIV OpCode = 0x5
+ MOD OpCode = 0x6
+ SMOD OpCode = 0x7
+ ADDMOD OpCode = 0x8
+ MULMOD OpCode = 0x9
+ EXP OpCode = 0xa
+ SIGNEXTEND OpCode = 0xb
+)
- // LOG3 fires an event with three topics
- LOG3 = 0xA3
+// 0x10 range - comparison ops.
+const (
+ LT OpCode = 0x10
+ GT OpCode = 0x11
+ SLT OpCode = 0x12
+ SGT OpCode = 0x13
+ EQ OpCode = 0x14
+ ISZERO OpCode = 0x15
+ AND OpCode = 0x16
+ OR OpCode = 0x17
+ XOR OpCode = 0x18
+ NOT OpCode = 0x19
+ BYTE OpCode = 0x1a
+ SHL OpCode = 0x1b
+ SHR OpCode = 0x1c
+ SAR OpCode = 0x1d
+)
- // LOG4 fires an event with four topics
- LOG4 = 0xA4
+// 0x20 range - crypto.
+const (
+ KECCAK256 OpCode = 0x20
+)
- // CREATE creates a child contract
- CREATE = 0xF0
+// 0x30 range - closure state.
+const (
+ ADDRESS OpCode = 0x30
+ BALANCE OpCode = 0x31
+ ORIGIN OpCode = 0x32
+ CALLER OpCode = 0x33
+ CALLVALUE OpCode = 0x34
+ CALLDATALOAD OpCode = 0x35
+ CALLDATASIZE OpCode = 0x36
+ CALLDATACOPY OpCode = 0x37
+ CODESIZE OpCode = 0x38
+ CODECOPY OpCode = 0x39
+ GASPRICE OpCode = 0x3a
+ EXTCODESIZE OpCode = 0x3b
+ EXTCODECOPY OpCode = 0x3c
+ RETURNDATASIZE OpCode = 0x3d
+ RETURNDATACOPY OpCode = 0x3e
+ EXTCODEHASH OpCode = 0x3f
+)
- // CALL calls a method in another contract
- CALL = 0xF1
+// 0x40 range - block operations.
+const (
+ BLOCKHASH OpCode = 0x40
+ COINBASE OpCode = 0x41
+ TIMESTAMP OpCode = 0x42
+ NUMBER OpCode = 0x43
+ DIFFICULTY OpCode = 0x44
+ RANDOM OpCode = 0x44 // Same as DIFFICULTY
+ PREVRANDAO OpCode = 0x44 // Same as DIFFICULTY
+ GASLIMIT OpCode = 0x45
+ CHAINID OpCode = 0x46
+ SELFBALANCE OpCode = 0x47
+ BASEFEE OpCode = 0x48
+)
- // CALLCODE calls a method in another contract
- CALLCODE = 0xF2
+// 0x50 range - 'storage' and execution.
+const (
+ POP OpCode = 0x50
+ MLOAD OpCode = 0x51
+ MSTORE OpCode = 0x52
+ MSTORE8 OpCode = 0x53
+ SLOAD OpCode = 0x54
+ SSTORE OpCode = 0x55
+ JUMP OpCode = 0x56
+ JUMPI OpCode = 0x57
+ PC OpCode = 0x58
+ MSIZE OpCode = 0x59
+ GAS OpCode = 0x5a
+ JUMPDEST OpCode = 0x5b
+ PUSH0 OpCode = 0x5f
+)
- // RETURN returns from this contract call
- RETURN = 0xF3
+// 0x60 range - pushes.
+const (
+ PUSH1 OpCode = 0x60 + iota
+ PUSH2
+ PUSH3
+ PUSH4
+ PUSH5
+ PUSH6
+ PUSH7
+ PUSH8
+ PUSH9
+ PUSH10
+ PUSH11
+ PUSH12
+ PUSH13
+ PUSH14
+ PUSH15
+ PUSH16
+ PUSH17
+ PUSH18
+ PUSH19
+ PUSH20
+ PUSH21
+ PUSH22
+ PUSH23
+ PUSH24
+ PUSH25
+ PUSH26
+ PUSH27
+ PUSH28
+ PUSH29
+ PUSH30
+ PUSH31
+ PUSH32
+)
- // DELEGATECALL calls a method in another contract using the storage of the current contract
- DELEGATECALL = 0xF4
+// 0x80 range - dups.
+const (
+ DUP1 = 0x80 + iota
+ DUP2
+ DUP3
+ DUP4
+ DUP5
+ DUP6
+ DUP7
+ DUP8
+ DUP9
+ DUP10
+ DUP11
+ DUP12
+ DUP13
+ DUP14
+ DUP15
+ DUP16
+)
- // CREATE2 creates a child contract with a salt
- CREATE2 = 0xF5
+// 0x90 range - swaps.
+const (
+ SWAP1 = 0x90 + iota
+ SWAP2
+ SWAP3
+ SWAP4
+ SWAP5
+ SWAP6
+ SWAP7
+ SWAP8
+ SWAP9
+ SWAP10
+ SWAP11
+ SWAP12
+ SWAP13
+ SWAP14
+ SWAP15
+ SWAP16
+)
- // STATICCALL calls a method in another contract
- STATICCALL = 0xFA
+// 0xa0 range - logging ops.
+const (
+ LOG0 OpCode = 0xa0 + iota
+ LOG1
+ LOG2
+ LOG3
+ LOG4
+)
- // REVERT reverts with return data
- REVERT = 0xFD
+// 0xf0 range - closures.
+const (
+ CREATE OpCode = 0xf0
+ CALL OpCode = 0xf1
+ CALLCODE OpCode = 0xf2
+ RETURN OpCode = 0xf3
+ DELEGATECALL OpCode = 0xf4
+ CREATE2 OpCode = 0xf5
+
+ STATICCALL OpCode = 0xfa
+ REVERT OpCode = 0xfd
+ INVALID OpCode = 0xfe
+ SELFDESTRUCT OpCode = 0xff
+)
- // SELFDESTRUCT destroys the contract and sends all funds to addr
- SELFDESTRUCT = 0xFF
+// 0xb0 range.
+const (
+ TLOAD OpCode = 0xb3
+ TSTORE OpCode = 0xb4
)
+// Since the opcodes aren't all in order we can't use a regular slice.
var opCodeToString = map[OpCode]string{
- STOP: "STOP",
- ADD: "ADD",
- MUL: "MUL",
- SUB: "SUB",
- DIV: "DIV",
- SDIV: "SDIV",
- MOD: "MOD",
- SMOD: "SMOD",
- EXP: "EXP",
- NOT: "NOT",
- LT: "LT",
- GT: "GT",
- SLT: "SLT",
- SGT: "SGT",
- EQ: "EQ",
- ISZERO: "ISZERO",
- SIGNEXTEND: "SIGNEXTEND",
- AND: "AND",
- OR: "OR",
- XOR: "XOR",
- BYTE: "BYTE",
- SHL: "SHL",
- SHR: "SHR",
- SAR: "SAR",
- ADDMOD: "ADDMOD",
- MULMOD: "MULMOD",
- SHA3: "SHA3",
+ // 0x0 range - arithmetic ops.
+ STOP: "STOP",
+ ADD: "ADD",
+ MUL: "MUL",
+ SUB: "SUB",
+ DIV: "DIV",
+ SDIV: "SDIV",
+ MOD: "MOD",
+ SMOD: "SMOD",
+ EXP: "EXP",
+ NOT: "NOT",
+ LT: "LT",
+ GT: "GT",
+ SLT: "SLT",
+ SGT: "SGT",
+ EQ: "EQ",
+ ISZERO: "ISZERO",
+ SIGNEXTEND: "SIGNEXTEND",
+
+ // 0x10 range - bit ops.
+ AND: "AND",
+ OR: "OR",
+ XOR: "XOR",
+ BYTE: "BYTE",
+ SHL: "SHL",
+ SHR: "SHR",
+ SAR: "SAR",
+ ADDMOD: "ADDMOD",
+ MULMOD: "MULMOD",
+
+ // 0x20 range - crypto.
+ KECCAK256: "KECCAK256",
+
+ // 0x30 range - closure state.
ADDRESS: "ADDRESS",
BALANCE: "BALANCE",
ORIGIN: "ORIGIN",
@@ -476,106 +277,284 @@ var opCodeToString = map[OpCode]string{
RETURNDATASIZE: "RETURNDATASIZE",
RETURNDATACOPY: "RETURNDATACOPY",
EXTCODEHASH: "EXTCODEHASH",
- BLOCKHASH: "BLOCKHASH",
- COINBASE: "COINBASE",
- TIMESTAMP: "TIMESTAMP",
- NUMBER: "NUMBER",
- DIFFICULTY: "DIFFICULTY",
- GASLIMIT: "GASLIMIT",
- POP: "POP",
- MLOAD: "MLOAD",
- MSTORE: "MSTORE",
- MSTORE8: "MSTORE8",
- SLOAD: "SLOAD",
- SSTORE: "SSTORE",
- JUMP: "JUMP",
- JUMPI: "JUMPI",
- PC: "PC",
- MSIZE: "MSIZE",
- GAS: "GAS",
- JUMPDEST: "JUMPDEST",
- PUSH1: "PUSH1",
- PUSH2: "PUSH2",
- PUSH3: "PUSH3",
- PUSH4: "PUSH4",
- PUSH5: "PUSH5",
- PUSH6: "PUSH6",
- PUSH7: "PUSH7",
- PUSH8: "PUSH8",
- PUSH9: "PUSH9",
- PUSH10: "PUSH10",
- PUSH11: "PUSH11",
- PUSH12: "PUSH12",
- PUSH13: "PUSH13",
- PUSH14: "PUSH14",
- PUSH15: "PUSH15",
- PUSH16: "PUSH16",
- PUSH17: "PUSH17",
- PUSH18: "PUSH18",
- PUSH19: "PUSH19",
- PUSH20: "PUSH20",
- PUSH21: "PUSH21",
- PUSH22: "PUSH22",
- PUSH23: "PUSH23",
- PUSH24: "PUSH24",
- PUSH25: "PUSH25",
- PUSH26: "PUSH26",
- PUSH27: "PUSH27",
- PUSH28: "PUSH28",
- PUSH29: "PUSH29",
- PUSH30: "PUSH30",
- PUSH31: "PUSH31",
- PUSH32: "PUSH32",
- DUP1: "DUP1",
- DUP2: "DUP2",
- DUP3: "DUP3",
- DUP4: "DUP4",
- DUP5: "DUP5",
- DUP6: "DUP6",
- DUP7: "DUP7",
- DUP8: "DUP8",
- DUP9: "DUP9",
- DUP10: "DUP10",
- DUP11: "DUP11",
- DUP12: "DUP12",
- DUP13: "DUP13",
- DUP14: "DUP14",
- DUP15: "DUP15",
- DUP16: "DUP16",
- SWAP1: "SWAP1",
- SWAP2: "SWAP2",
- SWAP3: "SWAP3",
- SWAP4: "SWAP4",
- SWAP5: "SWAP5",
- SWAP6: "SWAP6",
- SWAP7: "SWAP7",
- SWAP8: "SWAP8",
- SWAP9: "SWAP9",
- SWAP10: "SWAP10",
- SWAP11: "SWAP11",
- SWAP12: "SWAP12",
- SWAP13: "SWAP13",
- SWAP14: "SWAP14",
- SWAP15: "SWAP15",
- SWAP16: "SWAP16",
- LOG0: "LOG0",
- LOG1: "LOG1",
- LOG2: "LOG2",
- LOG3: "LOG3",
- LOG4: "LOG4",
- CREATE: "CREATE",
- CALL: "CALL",
- RETURN: "RETURN",
- CALLCODE: "CALLCODE",
- DELEGATECALL: "DELEGATECALL",
- CREATE2: "CREATE2",
- STATICCALL: "STATICCALL",
- REVERT: "REVERT",
- SELFDESTRUCT: "SELFDESTRUCT",
- CHAINID: "CHAINID",
- SELFBALANCE: "SELFBALANCE",
+
+ // 0x40 range - block operations.
+ BLOCKHASH: "BLOCKHASH",
+ COINBASE: "COINBASE",
+ TIMESTAMP: "TIMESTAMP",
+ NUMBER: "NUMBER",
+ DIFFICULTY: "DIFFICULTY", // TODO (MariusVanDerWijden) rename to PREVRANDAO post merge
+ GASLIMIT: "GASLIMIT",
+ CHAINID: "CHAINID",
+ SELFBALANCE: "SELFBALANCE",
+ BASEFEE: "BASEFEE",
+
+ // 0x50 range - 'storage' and execution.
+ POP: "POP",
+ //DUP: "DUP",
+ //SWAP: "SWAP",
+ MLOAD: "MLOAD",
+ MSTORE: "MSTORE",
+ MSTORE8: "MSTORE8",
+ SLOAD: "SLOAD",
+ SSTORE: "SSTORE",
+ JUMP: "JUMP",
+ JUMPI: "JUMPI",
+ PC: "PC",
+ MSIZE: "MSIZE",
+ GAS: "GAS",
+ JUMPDEST: "JUMPDEST",
+ PUSH0: "PUSH0",
+
+ // 0x60 range - push.
+ PUSH1: "PUSH1",
+ PUSH2: "PUSH2",
+ PUSH3: "PUSH3",
+ PUSH4: "PUSH4",
+ PUSH5: "PUSH5",
+ PUSH6: "PUSH6",
+ PUSH7: "PUSH7",
+ PUSH8: "PUSH8",
+ PUSH9: "PUSH9",
+ PUSH10: "PUSH10",
+ PUSH11: "PUSH11",
+ PUSH12: "PUSH12",
+ PUSH13: "PUSH13",
+ PUSH14: "PUSH14",
+ PUSH15: "PUSH15",
+ PUSH16: "PUSH16",
+ PUSH17: "PUSH17",
+ PUSH18: "PUSH18",
+ PUSH19: "PUSH19",
+ PUSH20: "PUSH20",
+ PUSH21: "PUSH21",
+ PUSH22: "PUSH22",
+ PUSH23: "PUSH23",
+ PUSH24: "PUSH24",
+ PUSH25: "PUSH25",
+ PUSH26: "PUSH26",
+ PUSH27: "PUSH27",
+ PUSH28: "PUSH28",
+ PUSH29: "PUSH29",
+ PUSH30: "PUSH30",
+ PUSH31: "PUSH31",
+ PUSH32: "PUSH32",
+
+ DUP1: "DUP1",
+ DUP2: "DUP2",
+ DUP3: "DUP3",
+ DUP4: "DUP4",
+ DUP5: "DUP5",
+ DUP6: "DUP6",
+ DUP7: "DUP7",
+ DUP8: "DUP8",
+ DUP9: "DUP9",
+ DUP10: "DUP10",
+ DUP11: "DUP11",
+ DUP12: "DUP12",
+ DUP13: "DUP13",
+ DUP14: "DUP14",
+ DUP15: "DUP15",
+ DUP16: "DUP16",
+
+ SWAP1: "SWAP1",
+ SWAP2: "SWAP2",
+ SWAP3: "SWAP3",
+ SWAP4: "SWAP4",
+ SWAP5: "SWAP5",
+ SWAP6: "SWAP6",
+ SWAP7: "SWAP7",
+ SWAP8: "SWAP8",
+ SWAP9: "SWAP9",
+ SWAP10: "SWAP10",
+ SWAP11: "SWAP11",
+ SWAP12: "SWAP12",
+ SWAP13: "SWAP13",
+ SWAP14: "SWAP14",
+ SWAP15: "SWAP15",
+ SWAP16: "SWAP16",
+ LOG0: "LOG0",
+ LOG1: "LOG1",
+ LOG2: "LOG2",
+ LOG3: "LOG3",
+ LOG4: "LOG4",
+
+ // 0xb0 range.
+ TLOAD: "TLOAD",
+ TSTORE: "TSTORE",
+
+ // 0xf0 range.
+ CREATE: "CREATE",
+ CALL: "CALL",
+ RETURN: "RETURN",
+ CALLCODE: "CALLCODE",
+ DELEGATECALL: "DELEGATECALL",
+ CREATE2: "CREATE2",
+ STATICCALL: "STATICCALL",
+ REVERT: "REVERT",
+ INVALID: "INVALID",
+ SELFDESTRUCT: "SELFDESTRUCT",
}
func (op OpCode) String() string {
- return opCodeToString[op]
+ str := opCodeToString[op]
+ if len(str) == 0 {
+ return fmt.Sprintf("opcode %#x not defined", int(op))
+ }
+
+ return str
+}
+
+var stringToOp = map[string]OpCode{
+ "STOP": STOP,
+ "ADD": ADD,
+ "MUL": MUL,
+ "SUB": SUB,
+ "DIV": DIV,
+ "SDIV": SDIV,
+ "MOD": MOD,
+ "SMOD": SMOD,
+ "EXP": EXP,
+ "NOT": NOT,
+ "LT": LT,
+ "GT": GT,
+ "SLT": SLT,
+ "SGT": SGT,
+ "EQ": EQ,
+ "ISZERO": ISZERO,
+ "SIGNEXTEND": SIGNEXTEND,
+ "AND": AND,
+ "OR": OR,
+ "XOR": XOR,
+ "BYTE": BYTE,
+ "SHL": SHL,
+ "SHR": SHR,
+ "SAR": SAR,
+ "ADDMOD": ADDMOD,
+ "MULMOD": MULMOD,
+ "KECCAK256": KECCAK256,
+ "ADDRESS": ADDRESS,
+ "BALANCE": BALANCE,
+ "ORIGIN": ORIGIN,
+ "CALLER": CALLER,
+ "CALLVALUE": CALLVALUE,
+ "CALLDATALOAD": CALLDATALOAD,
+ "CALLDATASIZE": CALLDATASIZE,
+ "CALLDATACOPY": CALLDATACOPY,
+ "CHAINID": CHAINID,
+ "BASEFEE": BASEFEE,
+ "DELEGATECALL": DELEGATECALL,
+ "STATICCALL": STATICCALL,
+ "CODESIZE": CODESIZE,
+ "CODECOPY": CODECOPY,
+ "GASPRICE": GASPRICE,
+ "EXTCODESIZE": EXTCODESIZE,
+ "EXTCODECOPY": EXTCODECOPY,
+ "RETURNDATASIZE": RETURNDATASIZE,
+ "RETURNDATACOPY": RETURNDATACOPY,
+ "EXTCODEHASH": EXTCODEHASH,
+ "BLOCKHASH": BLOCKHASH,
+ "COINBASE": COINBASE,
+ "TIMESTAMP": TIMESTAMP,
+ "NUMBER": NUMBER,
+ "DIFFICULTY": DIFFICULTY,
+ "GASLIMIT": GASLIMIT,
+ "SELFBALANCE": SELFBALANCE,
+ "POP": POP,
+ "MLOAD": MLOAD,
+ "MSTORE": MSTORE,
+ "MSTORE8": MSTORE8,
+ "SLOAD": SLOAD,
+ "SSTORE": SSTORE,
+ "JUMP": JUMP,
+ "JUMPI": JUMPI,
+ "PC": PC,
+ "MSIZE": MSIZE,
+ "GAS": GAS,
+ "JUMPDEST": JUMPDEST,
+ "PUSH0": PUSH0,
+ "TLOAD": TLOAD,
+ "TSTORE": TSTORE,
+ "PUSH1": PUSH1,
+ "PUSH2": PUSH2,
+ "PUSH3": PUSH3,
+ "PUSH4": PUSH4,
+ "PUSH5": PUSH5,
+ "PUSH6": PUSH6,
+ "PUSH7": PUSH7,
+ "PUSH8": PUSH8,
+ "PUSH9": PUSH9,
+ "PUSH10": PUSH10,
+ "PUSH11": PUSH11,
+ "PUSH12": PUSH12,
+ "PUSH13": PUSH13,
+ "PUSH14": PUSH14,
+ "PUSH15": PUSH15,
+ "PUSH16": PUSH16,
+ "PUSH17": PUSH17,
+ "PUSH18": PUSH18,
+ "PUSH19": PUSH19,
+ "PUSH20": PUSH20,
+ "PUSH21": PUSH21,
+ "PUSH22": PUSH22,
+ "PUSH23": PUSH23,
+ "PUSH24": PUSH24,
+ "PUSH25": PUSH25,
+ "PUSH26": PUSH26,
+ "PUSH27": PUSH27,
+ "PUSH28": PUSH28,
+ "PUSH29": PUSH29,
+ "PUSH30": PUSH30,
+ "PUSH31": PUSH31,
+ "PUSH32": PUSH32,
+ "DUP1": DUP1,
+ "DUP2": DUP2,
+ "DUP3": DUP3,
+ "DUP4": DUP4,
+ "DUP5": DUP5,
+ "DUP6": DUP6,
+ "DUP7": DUP7,
+ "DUP8": DUP8,
+ "DUP9": DUP9,
+ "DUP10": DUP10,
+ "DUP11": DUP11,
+ "DUP12": DUP12,
+ "DUP13": DUP13,
+ "DUP14": DUP14,
+ "DUP15": DUP15,
+ "DUP16": DUP16,
+ "SWAP1": SWAP1,
+ "SWAP2": SWAP2,
+ "SWAP3": SWAP3,
+ "SWAP4": SWAP4,
+ "SWAP5": SWAP5,
+ "SWAP6": SWAP6,
+ "SWAP7": SWAP7,
+ "SWAP8": SWAP8,
+ "SWAP9": SWAP9,
+ "SWAP10": SWAP10,
+ "SWAP11": SWAP11,
+ "SWAP12": SWAP12,
+ "SWAP13": SWAP13,
+ "SWAP14": SWAP14,
+ "SWAP15": SWAP15,
+ "SWAP16": SWAP16,
+ "LOG0": LOG0,
+ "LOG1": LOG1,
+ "LOG2": LOG2,
+ "LOG3": LOG3,
+ "LOG4": LOG4,
+ "CREATE": CREATE,
+ "CREATE2": CREATE2,
+ "CALL": CALL,
+ "RETURN": RETURN,
+ "CALLCODE": CALLCODE,
+ "REVERT": REVERT,
+ "INVALID": INVALID,
+ "SELFDESTRUCT": SELFDESTRUCT,
+}
+
+// StringToOp finds the opcode whose name is stored in `str`.
+func StringToOp(str string) OpCode {
+ return stringToOp[str]
}
diff --git a/state/runtime/fakevm/operations_acl.go b/state/runtime/fakevm/operations_acl.go
new file mode 100644
index 0000000000..09ca31d3b6
--- /dev/null
+++ b/state/runtime/fakevm/operations_acl.go
@@ -0,0 +1,244 @@
+// Copyright 2020 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 .
+
+package fakevm
+
+import (
+ "errors"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func makeGasSStoreFunc(clearingRefund uint64) gasFunc {
+ return func(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // If we fail the minimum gas availability invariant, fail (0)
+ if contract.Gas <= params.SstoreSentryGasEIP2200 {
+ return 0, errors.New("not enough gas for reentrancy sentry")
+ }
+ // Gas sentry honoured, do the actual gas calculation based on the stored value
+ var (
+ y, x = stack.Back(1), stack.peek()
+ slot = common.Hash(x.Bytes32())
+ current = evm.StateDB.GetState(contract.Address(), slot)
+ cost = uint64(0)
+ )
+ // Check slot presence in the access list
+ if addrPresent, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ cost = params.ColdSloadCostEIP2929
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ if !addrPresent {
+ // Once we're done with YOLOv2 and schedule this for mainnet, might
+ // be good to remove this panic here, which is just really a
+ // canary to have during testing
+ panic("impossible case: address was not present in access list during sstore op")
+ }
+ }
+ value := common.Hash(y.Bytes32())
+
+ if current == value { // noop (1)
+ // EIP 2200 original clause:
+ // return params.SloadGasEIP2200, nil
+ return cost + params.WarmStorageReadCostEIP2929, nil // SLOAD_GAS
+ }
+ original := evm.StateDB.GetCommittedState(contract.Address(), x.Bytes32())
+ if original == current {
+ if original == (common.Hash{}) { // create slot (2.1.1)
+ return cost + params.SstoreSetGasEIP2200, nil
+ }
+ if value == (common.Hash{}) { // delete slot (2.1.2b)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ // EIP-2200 original clause:
+ // return params.SstoreResetGasEIP2200, nil // write existing slot (2.1.2)
+ return cost + (params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929), nil // write existing slot (2.1.2)
+ }
+ if original != (common.Hash{}) {
+ if current == (common.Hash{}) { // recreate slot (2.2.1.1)
+ evm.StateDB.SubRefund(clearingRefund)
+ } else if value == (common.Hash{}) { // delete slot (2.2.1.2)
+ evm.StateDB.AddRefund(clearingRefund)
+ }
+ }
+ if original == value {
+ if original == (common.Hash{}) { // reset to original inexistent slot (2.2.2.1)
+ // EIP 2200 Original clause:
+ //evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.SloadGasEIP2200)
+ evm.StateDB.AddRefund(params.SstoreSetGasEIP2200 - params.WarmStorageReadCostEIP2929)
+ } else { // reset to original existing slot (2.2.2.2)
+ // EIP 2200 Original clause:
+ // evm.StateDB.AddRefund(params.SstoreResetGasEIP2200 - params.SloadGasEIP2200)
+ // - SSTORE_RESET_GAS redefined as (5000 - COLD_SLOAD_COST)
+ // - SLOAD_GAS redefined as WARM_STORAGE_READ_COST
+ // Final: (5000 - COLD_SLOAD_COST) - WARM_STORAGE_READ_COST
+ evm.StateDB.AddRefund((params.SstoreResetGasEIP2200 - params.ColdSloadCostEIP2929) - params.WarmStorageReadCostEIP2929)
+ }
+ }
+ // EIP-2200 original clause:
+ //return params.SloadGasEIP2200, nil // dirty update (2.2)
+ return cost + params.WarmStorageReadCostEIP2929, nil // dirty update (2.2)
+ }
+}
+
+// gasSLoadEIP2929 calculates dynamic gas for SLOAD according to EIP-2929
+// For SLOAD, if the (address, storage_key) pair (where address is the address of the contract
+// whose storage is being read) is not yet in accessed_storage_keys,
+// charge 2100 gas and add the pair to accessed_storage_keys.
+// If the pair is already in accessed_storage_keys, charge 100 gas.
+func gasSLoadEIP2929(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ loc := stack.peek()
+ slot := common.Hash(loc.Bytes32())
+ // Check slot presence in the access list
+ if _, slotPresent := evm.StateDB.SlotInAccessList(contract.Address(), slot); !slotPresent {
+ // If the caller cannot afford the cost, this change will be rolled back
+ // If he does afford it, we can skip checking the same thing later on, during execution
+ evm.StateDB.AddSlotToAccessList(contract.Address(), slot)
+ return params.ColdSloadCostEIP2929, nil
+ }
+ return params.WarmStorageReadCostEIP2929, nil
+}
+
+// gasExtCodeCopyEIP2929 implements extcodecopy according to EIP-2929
+// EIP spec:
+// > If the target is not in accessed_addresses,
+// > charge COLD_ACCOUNT_ACCESS_COST gas, and add the address to accessed_addresses.
+// > Otherwise, charge WARM_STORAGE_READ_COST gas.
+func gasExtCodeCopyEIP2929(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ // memory expansion first (dynamic part of pre-2929 implementation)
+ gas, err := gasExtCodeCopy(evm, contract, stack, mem, memorySize)
+ if err != nil {
+ return 0, err
+ }
+ addr := common.Address(stack.peek().Bytes20())
+ // Check slot presence in the access list
+ if !evm.StateDB.AddressInAccessList(addr) {
+ evm.StateDB.AddAddressToAccessList(addr)
+ var overflow bool
+ // We charge (cold-warm), since 'warm' is already charged as constantGas
+ if gas, overflow = math.SafeAdd(gas, params.ColdAccountAccessCostEIP2929-params.WarmStorageReadCostEIP2929); overflow {
+ return 0, ErrGasUintOverflow
+ }
+ return gas, nil
+ }
+ return gas, nil
+}
+
+// gasEip2929AccountCheck checks whether the first stack item (as address) is present in the access list.
+// If it is, this method returns '0', otherwise 'cold-warm' gas, presuming that the opcode using it
+// is also using 'warm' as constant factor.
+// This method is used by:
+// - extcodehash,
+// - extcodesize,
+// - (ext) balance
+func gasEip2929AccountCheck(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ addr := common.Address(stack.peek().Bytes20())
+ // Check slot presence in the access list
+ if !evm.StateDB.AddressInAccessList(addr) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(addr)
+ // The warm storage read cost is already charged as constantGas
+ return params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929, nil
+ }
+ return 0, nil
+}
+
+func makeCallVariantGasCallEIP2929(oldCalculator gasFunc) gasFunc {
+ return func(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ addr := common.Address(stack.Back(1).Bytes20())
+ // Check slot presence in the access list
+ warmAccess := evm.StateDB.AddressInAccessList(addr)
+ // The WarmStorageReadCostEIP2929 (100) is already deducted in the form of a constant cost, so
+ // the cost to charge for cold access, if any, is Cold - Warm
+ coldCost := params.ColdAccountAccessCostEIP2929 - params.WarmStorageReadCostEIP2929
+ if !warmAccess {
+ evm.StateDB.AddAddressToAccessList(addr)
+ // Charge the remaining difference here already, to correctly calculate available
+ // gas for call
+ if !contract.UseGas(coldCost) {
+ return 0, ErrOutOfGas
+ }
+ }
+ // Now call the old calculator, which takes into account
+ // - create new account
+ // - transfer value
+ // - memory expansion
+ // - 63/64ths rule
+ gas, err := oldCalculator(evm, contract, stack, mem, memorySize)
+ if warmAccess || err != nil {
+ return gas, err
+ }
+ // In case of a cold access, we temporarily add the cold charge back, and also
+ // add it to the returned gas. By adding it to the return, it will be charged
+ // outside of this function, as part of the dynamic gas, and that will make it
+ // also become correctly reported to tracers.
+ contract.Gas += coldCost
+ return gas + coldCost, nil
+ }
+}
+
+var (
+ gasCallEIP2929 = makeCallVariantGasCallEIP2929(gasCall)
+ gasDelegateCallEIP2929 = makeCallVariantGasCallEIP2929(gasDelegateCall)
+ gasStaticCallEIP2929 = makeCallVariantGasCallEIP2929(gasStaticCall)
+ gasCallCodeEIP2929 = makeCallVariantGasCallEIP2929(gasCallCode)
+ gasSelfdestructEIP2929 = makeSelfdestructGasFn(true)
+ // gasSelfdestructEIP3529 implements the changes in EIP-2539 (no refunds)
+ gasSelfdestructEIP3529 = makeSelfdestructGasFn(false)
+
+ // gasSStoreEIP2929 implements gas cost for SSTORE according to EIP-2929
+ //
+ // When calling SSTORE, check if the (address, storage_key) pair is in accessed_storage_keys.
+ // If it is not, charge an additional COLD_SLOAD_COST gas, and add the pair to accessed_storage_keys.
+ // Additionally, modify the parameters defined in EIP 2200 as follows:
+ //
+ // Parameter Old value New value
+ // SLOAD_GAS 800 = WARM_STORAGE_READ_COST
+ // SSTORE_RESET_GAS 5000 5000 - COLD_SLOAD_COST
+ //
+ //The other parameters defined in EIP 2200 are unchanged.
+ // see gasSStoreEIP2200(...) in core/vm/gas_table.go for more info about how EIP 2200 is specified
+ gasSStoreEIP2929 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP2200)
+
+ // gasSStoreEIP2539 implements gas cost for SSTORE according to EIP-2539
+ // Replace `SSTORE_CLEARS_SCHEDULE` with `SSTORE_RESET_GAS + ACCESS_LIST_STORAGE_KEY_COST` (4,800)
+ gasSStoreEIP3529 = makeGasSStoreFunc(params.SstoreClearsScheduleRefundEIP3529)
+)
+
+// makeSelfdestructGasFn can create the selfdestruct dynamic gas function for EIP-2929 and EIP-2539
+func makeSelfdestructGasFn(refundsEnabled bool) gasFunc {
+ gasFunc := func(evm *FakeEVM, contract *Contract, stack *Stack, mem *Memory, memorySize uint64) (uint64, error) {
+ var (
+ gas uint64
+ address = common.Address(stack.peek().Bytes20())
+ )
+ if !evm.StateDB.AddressInAccessList(address) {
+ // If the caller cannot afford the cost, this change will be rolled back
+ evm.StateDB.AddAddressToAccessList(address)
+ gas = params.ColdAccountAccessCostEIP2929
+ }
+ // if empty and transfers value
+ if evm.StateDB.Empty(address) && evm.StateDB.GetBalance(contract.Address()).Sign() != 0 {
+ gas += params.CreateBySelfdestructGas
+ }
+ if refundsEnabled && !evm.StateDB.HasSuicided(contract.Address()) {
+ evm.StateDB.AddRefund(params.SelfdestructRefundGas)
+ }
+ return gas, nil
+ }
+ return gasFunc
+}
diff --git a/state/runtime/fakevm/stack.go b/state/runtime/fakevm/stack.go
index f778f610d0..6c9e105206 100644
--- a/state/runtime/fakevm/stack.go
+++ b/state/runtime/fakevm/stack.go
@@ -17,18 +17,14 @@
package fakevm
import (
- "fmt"
"sync"
"github.com/holiman/uint256"
)
-// InitialStackSize represents the initial stack size.
-const InitialStackSize int = 16
-
var stackPool = sync.Pool{
New: func() interface{} {
- return &Stack{data: make([]uint256.Int, 0, InitialStackSize)}
+ return &Stack{data: make([]uint256.Int, 0, 16)}
},
}
@@ -39,40 +35,48 @@ type Stack struct {
data []uint256.Int
}
-// Newstack gets a stack from the pool.
-func Newstack() *Stack {
+func NewStack() *Stack {
return stackPool.Get().(*Stack)
}
+func returnStack(s *Stack) {
+ s.data = s.data[:0]
+ stackPool.Put(s)
+}
+
// Data returns the underlying uint256.Int array.
func (st *Stack) Data() []uint256.Int {
return st.data
}
-// Push adds an item to the data.
func (st *Stack) Push(d *uint256.Int) {
// NOTE push limit (1024) is checked in baseCheck
st.data = append(st.data, *d)
}
+func (st *Stack) pop() (ret uint256.Int) {
+ ret = st.data[len(st.data)-1]
+ st.data = st.data[:len(st.data)-1]
+ return
+}
+
func (st *Stack) len() int {
return len(st.data)
}
+func (st *Stack) swap(n int) {
+ st.data[st.len()-n], st.data[st.len()-1] = st.data[st.len()-1], st.data[st.len()-n]
+}
+
+func (st *Stack) dup(n int) {
+ st.Push(&st.data[st.len()-n])
+}
+
+func (st *Stack) peek() *uint256.Int {
+ return &st.data[st.len()-1]
+}
+
// Back returns the n'th item in stack
func (st *Stack) Back(n int) *uint256.Int {
return &st.data[st.len()-n-1]
}
-
-// Print dumps the content of the stack
-func (st *Stack) Print() {
- fmt.Println("### stack ###")
- if len(st.data) > 0 {
- for i, val := range st.data {
- fmt.Printf("%-3d %s\n", i, val.String())
- }
- } else {
- fmt.Println("-- empty --")
- }
- fmt.Println("#############")
-}
diff --git a/state/runtime/fakevm/stack_table.go b/state/runtime/fakevm/stack_table.go
new file mode 100644
index 0000000000..b5bf72a42a
--- /dev/null
+++ b/state/runtime/fakevm/stack_table.go
@@ -0,0 +1,42 @@
+// Copyright 2017 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 .
+
+package fakevm
+
+import (
+ "github.com/ethereum/go-ethereum/params"
+)
+
+func minSwapStack(n int) int {
+ return minStack(n, n)
+}
+func maxSwapStack(n int) int {
+ return maxStack(n, n)
+}
+
+func minDupStack(n int) int {
+ return minStack(n, n+1)
+}
+func maxDupStack(n int) int {
+ return maxStack(n, n+1)
+}
+
+func maxStack(pop, push int) int {
+ return int(params.StackLimit) + pop - push
+}
+func minStack(pops, push int) int {
+ return pops
+}
diff --git a/state/runtime/instrumentation/executortrace.go b/state/runtime/instrumentation/executortrace.go
index 237ba044ed..8a45bbd30a 100644
--- a/state/runtime/instrumentation/executortrace.go
+++ b/state/runtime/instrumentation/executortrace.go
@@ -36,8 +36,8 @@ type Step struct {
Contract Contract `json:"contract"`
GasCost string `json:"gasCost"`
Stack []string `json:"stack"`
- Memory []string `json:"memory"`
- ReturnData string `json:"returnData"`
+ Memory []byte `json:"memory"`
+ ReturnData []byte `json:"returnData"`
}
// Contract represents a contract in the trace.
diff --git a/state/runtime/instrumentation/js/goja.go b/state/runtime/instrumentation/js/goja.go
index 7b9853c22a..6e418eaf12 100644
--- a/state/runtime/instrumentation/js/goja.go
+++ b/state/runtime/instrumentation/js/goja.go
@@ -1,3 +1,19 @@
+// Copyright 2022 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 .
+
package js
import (
@@ -5,19 +21,20 @@ import (
"errors"
"fmt"
"math/big"
- "time"
- "github.com/0xPolygonHermez/zkevm-node/log"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
jsassets "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/js/internal/tracers"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
- "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
)
+const (
+ memoryPadLimit = 1024 * 1024
+)
+
var assetTracers = make(map[string]string)
// init retrieves the JavaScript transaction tracers included in go-ethereum.
@@ -27,7 +44,16 @@ func init() {
if err != nil {
panic(err)
}
- tracers.RegisterLookup(true, NewJsTracer)
+ type ctorFn = func(*tracers.Context, json.RawMessage) (tracers.Tracer, error)
+ lookup := func(code string) ctorFn {
+ return func(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ return NewJsTracer(code, ctx, cfg)
+ }
+ }
+ for name, code := range assetTracers {
+ tracers.DefaultDirectory.Register(name, lookup(code), true)
+ }
+ tracers.DefaultDirectory.RegisterJSEval(NewJsTracer)
}
// bigIntProgram is compiled once and the exported function mostly invoked to convert
@@ -40,11 +66,7 @@ type fromBufFn = func(vm *goja.Runtime, buf goja.Value, allowString bool) ([]byt
func toBuf(vm *goja.Runtime, bufType goja.Value, val []byte) (goja.Value, error) {
// bufType is usually Uint8Array. This is equivalent to `new Uint8Array(val)` in JS.
- res, err := vm.New(bufType, vm.ToValue(val))
- if err != nil {
- return nil, err
- }
- return vm.ToValue(res), nil
+ return vm.New(bufType, vm.ToValue(vm.NewArrayBuffer(val)))
}
func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString bool) ([]byte, error) {
@@ -55,6 +77,7 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
break
}
return common.FromHex(obj.String()), nil
+
case "Array":
var b []byte
if err := vm.ExportTo(buf, &b); err != nil {
@@ -66,15 +89,14 @@ func fromBuf(vm *goja.Runtime, bufType goja.Value, buf goja.Value, allowString b
if !obj.Get("constructor").SameAs(bufType) {
break
}
- var b []byte
- if err := vm.ExportTo(buf, &b); err != nil {
- return nil, err
- }
+ b := obj.Get("buffer").Export().(goja.ArrayBuffer).Bytes()
return b, nil
}
return nil, fmt.Errorf("invalid buffer type")
}
+// jsTracer is an implementation of the Tracer interface which evaluates
+// JS functions on the relevant EVM hooks. It uses Goja as its JS engine.
type jsTracer struct {
vm *goja.Runtime
env *fakevm.FakeEVM
@@ -108,11 +130,14 @@ type jsTracer struct {
frameResultValue goja.Value
}
-// NewJsTracer is the JS tracer constructor.
-func NewJsTracer(code string, ctx *tracers.Context) (tracers.Tracer, error) {
- if c, ok := assetTracers[code]; ok {
- code = c
- }
+// NewJsTracer instantiates a new JS tracer instance. code is a
+// Javascript snippet which evaluates to an expression returning
+// an object with certain methods:
+//
+// The methods `result` and `fault` are required to be present.
+// The methods `step`, `enter`, and `exit` are optional, but note that
+// `enter` and `exit` always go together.
+func NewJsTracer(code string, ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
vm := goja.New()
// By default field names are exported to JS as is, i.e. capitalized.
vm.SetFieldNameMapper(goja.UncapFieldNameMapper())
@@ -131,10 +156,7 @@ func NewJsTracer(code string, ctx *tracers.Context) (tracers.Tracer, error) {
}
}
- err := t.setTypeConverters()
- if err != nil {
- return nil, err
- }
+ t.setTypeConverters()
t.setBuiltinFunctions()
ret, err := vm.RunString("(" + code + ")")
if err != nil {
@@ -164,6 +186,17 @@ func NewJsTracer(code string, ctx *tracers.Context) (tracers.Tracer, error) {
t.exit = exit
t.result = result
t.fault = fault
+
+ // Pass in config
+ if setup, ok := goja.AssertFunction(obj.Get("setup")); ok {
+ cfgStr := "{}"
+ if cfg != nil {
+ cfgStr = string(cfg)
+ }
+ if _, err := setup(obj, vm.ToValue(cfgStr)); err != nil {
+ return nil, err
+ }
+ }
// Setup objects carrying data to JS. These are created once and re-used.
t.log = &steplog{
vm: vm,
@@ -188,7 +221,9 @@ func (t *jsTracer) CaptureTxStart(gasLimit uint64) {
// CaptureTxEnd implements the Tracer interface and is invoked at the end of
// transaction processing.
-func (t *jsTracer) CaptureTxEnd(restGas uint64) {}
+func (t *jsTracer) CaptureTxEnd(restGas uint64) {
+ t.ctx["gasUsed"] = t.vm.ToValue(t.gasLimit - restGas)
+}
// CaptureStart implements the Tracer interface to initialize the tracing operation.
func (t *jsTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
@@ -214,12 +249,11 @@ func (t *jsTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to com
t.ctx["block"] = t.vm.ToValue(env.Context.BlockNumber.Uint64())
// Update list of precompiles based on current block
rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
- t.activePrecompiles = vm.ActivePrecompiles(rules)
- t.ctx["intrinsicGas"] = t.vm.ToValue(t.gasLimit - gas)
+ t.activePrecompiles = fakevm.ActivePrecompiles(rules)
}
// CaptureState implements the Tracer interface to trace a single step of VM execution.
-func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
+func (t *jsTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
if !t.traceStep {
return
}
@@ -232,10 +266,11 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
log.memory.memory = scope.Memory
log.stack.stack = scope.Stack
log.contract.contract = scope.Contract
- log.pc = uint(pc)
- log.gas = uint(gas)
- log.cost = uint(cost)
- log.depth = uint(depth)
+ log.pc = pc
+ log.gas = gas
+ log.cost = cost
+ log.refund = t.env.StateDB.GetRefund()
+ log.depth = depth
log.err = err
if _, err := t.step(t.obj, t.logValue, t.dbValue); err != nil {
t.onError("step", err)
@@ -243,7 +278,7 @@ func (t *jsTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
// CaptureFault implements the Tracer interface to trace an execution fault
-func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, depth int, err error) {
+func (t *jsTracer) CaptureFault(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, depth int, err error) {
if t.err != nil {
return
}
@@ -255,17 +290,15 @@ func (t *jsTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope
}
// CaptureEnd is called after the call finishes to finalize the tracing.
-func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, duration time.Duration, err error) {
+func (t *jsTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
t.ctx["output"] = t.vm.ToValue(output)
- t.ctx["time"] = t.vm.ToValue(duration.String())
- t.ctx["gasUsed"] = t.vm.ToValue(gasUsed)
if err != nil {
t.ctx["error"] = t.vm.ToValue(err.Error())
}
}
// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
-func (t *jsTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+func (t *jsTracer) CaptureEnter(typ fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
if !t.traceFrame {
return
}
@@ -342,7 +375,7 @@ func wrapError(context string, err error) error {
func (t *jsTracer) setBuiltinFunctions() {
vm := t.vm
// TODO: load console from goja-nodejs
- err := vm.Set("toHex", func(v goja.Value) string {
+ vm.Set("toHex", func(v goja.Value) string {
b, err := t.fromBuf(vm, v, false)
if err != nil {
vm.Interrupt(err)
@@ -350,10 +383,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return hexutil.Encode(b)
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("toWord", func(v goja.Value) goja.Value {
+ vm.Set("toWord", func(v goja.Value) goja.Value {
// TODO: add test with []byte len < 32 or > 32
b, err := t.fromBuf(vm, v, true)
if err != nil {
@@ -368,10 +398,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return res
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("toAddress", func(v goja.Value) goja.Value {
+ vm.Set("toAddress", func(v goja.Value) goja.Value {
a, err := t.fromBuf(vm, v, true)
if err != nil {
vm.Interrupt(err)
@@ -385,10 +412,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return res
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("toContract", func(from goja.Value, nonce uint) goja.Value {
+ vm.Set("toContract", func(from goja.Value, nonce uint) goja.Value {
a, err := t.fromBuf(vm, from, true)
if err != nil {
vm.Interrupt(err)
@@ -403,10 +427,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return res
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("toContract2", func(from goja.Value, salt string, initcode goja.Value) goja.Value {
+ vm.Set("toContract2", func(from goja.Value, salt string, initcode goja.Value) goja.Value {
a, err := t.fromBuf(vm, from, true)
if err != nil {
vm.Interrupt(err)
@@ -428,10 +449,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return res
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("isPrecompiled", func(v goja.Value) bool {
+ vm.Set("isPrecompiled", func(v goja.Value) bool {
a, err := t.fromBuf(vm, v, true)
if err != nil {
vm.Interrupt(err)
@@ -445,10 +463,7 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return false
})
- if err != nil {
- log.Error(err)
- }
- err = vm.Set("slice", func(slice goja.Value, start, end int) goja.Value {
+ vm.Set("slice", func(slice goja.Value, start, end int) goja.Value {
b, err := t.fromBuf(vm, slice, false)
if err != nil {
vm.Interrupt(err)
@@ -465,9 +480,6 @@ func (t *jsTracer) setBuiltinFunctions() {
}
return res
})
- if err != nil {
- log.Error(err)
- }
}
// setTypeConverters sets up utilities for converting Go types into those
@@ -506,7 +518,7 @@ func (t *jsTracer) setTypeConverters() error {
type opObj struct {
vm *goja.Runtime
- op vm.OpCode
+ op fakevm.OpCode
}
func (o *opObj) ToNumber() int {
@@ -523,18 +535,9 @@ func (o *opObj) IsPush() bool {
func (o *opObj) setupObject() *goja.Object {
obj := o.vm.NewObject()
- err := obj.Set("toNumber", o.vm.ToValue(o.ToNumber))
- if err != nil {
- log.Error(err)
- }
- err = obj.Set("toString", o.vm.ToValue(o.ToString))
- if err != nil {
- log.Error(err)
- }
- err = obj.Set("isPush", o.vm.ToValue(o.IsPush))
- if err != nil {
- log.Error(err)
- }
+ obj.Set("toNumber", o.vm.ToValue(o.ToNumber))
+ obj.Set("toString", o.vm.ToValue(o.ToString))
+ obj.Set("isPush", o.vm.ToValue(o.IsPush))
return obj
}
@@ -565,12 +568,17 @@ func (mo *memoryObj) slice(begin, end int64) ([]byte, error) {
return []byte{}, nil
}
if end < begin || begin < 0 {
- return nil, fmt.Errorf("Tracer accessed out of bound memory: offset %d, end %d", begin, end)
+ return nil, fmt.Errorf("tracer accessed out of bound memory: offset %d, end %d", begin, end)
}
- if mo.memory.Len() < int(end) {
- return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), begin, end-begin)
+ mlen := mo.memory.Len()
+ if end-int64(mlen) > memoryPadLimit {
+ return nil, fmt.Errorf("tracer reached limit for padding memory slice: end %d, memorySize %d", end, mlen)
}
- return mo.memory.GetCopy(begin, end-begin), nil
+ slice := make([]byte, end-begin)
+ end = min(end, int64(mo.memory.Len()))
+ ptr := mo.memory.GetPtr(begin, end-begin)
+ copy(slice[:], ptr[:])
+ return slice, nil
}
func (mo *memoryObj) GetUint(addr int64) goja.Value {
@@ -590,29 +598,20 @@ func (mo *memoryObj) GetUint(addr int64) goja.Value {
// getUint returns the 32 bytes at the specified address interpreted as a uint.
func (mo *memoryObj) getUint(addr int64) (*big.Int, error) {
if mo.memory.Len() < int(addr)+32 || addr < 0 {
- return nil, fmt.Errorf("Tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, fakevm.MemoryItemSize)
+ return nil, fmt.Errorf("tracer accessed out of bound memory: available %d, offset %d, size %d", mo.memory.Len(), addr, 32)
}
- return new(big.Int).SetBytes(mo.memory.GetPtr(addr, int64(fakevm.MemoryItemSize))), nil
+ return new(big.Int).SetBytes(mo.memory.GetPtr(addr, 32)), nil
}
func (mo *memoryObj) Length() int {
return mo.memory.Len()
}
-func (mo *memoryObj) setupObject() *goja.Object {
- o := mo.vm.NewObject()
- err := o.Set("slice", mo.vm.ToValue(mo.Slice))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getUint", mo.vm.ToValue(mo.GetUint))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("length", mo.vm.ToValue(mo.Length))
- if err != nil {
- log.Error(err)
- }
+func (m *memoryObj) setupObject() *goja.Object {
+ o := m.vm.NewObject()
+ o.Set("slice", m.vm.ToValue(m.Slice))
+ o.Set("getUint", m.vm.ToValue(m.GetUint))
+ o.Set("length", m.vm.ToValue(m.Length))
return o
}
@@ -639,7 +638,7 @@ func (s *stackObj) Peek(idx int) goja.Value {
// peek returns the nth-from-the-top element of the stack.
func (s *stackObj) peek(idx int) (*big.Int, error) {
if len(s.stack.Data()) <= idx || idx < 0 {
- return nil, fmt.Errorf("Tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
+ return nil, fmt.Errorf("tracer accessed out of bound stack: size %d, index %d", len(s.stack.Data()), idx)
}
return s.stack.Back(idx).ToBig(), nil
}
@@ -650,14 +649,8 @@ func (s *stackObj) Length() int {
func (s *stackObj) setupObject() *goja.Object {
o := s.vm.NewObject()
- err := o.Set("peek", s.vm.ToValue(s.Peek))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("length", s.vm.ToValue(s.Length))
- if err != nil {
- log.Error(err)
- }
+ o.Set("peek", s.vm.ToValue(s.Peek))
+ o.Set("length", s.vm.ToValue(s.Length))
return o
}
@@ -745,31 +738,16 @@ func (do *dbObj) Exists(addrSlice goja.Value) bool {
func (do *dbObj) setupObject() *goja.Object {
o := do.vm.NewObject()
- err := o.Set("getBalance", do.vm.ToValue(do.GetBalance))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getNonce", do.vm.ToValue(do.GetNonce))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getCode", do.vm.ToValue(do.GetCode))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getState", do.vm.ToValue(do.GetState))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("exists", do.vm.ToValue(do.Exists))
- if err != nil {
- log.Error(err)
- }
+ o.Set("getBalance", do.vm.ToValue(do.GetBalance))
+ o.Set("getNonce", do.vm.ToValue(do.GetNonce))
+ o.Set("getCode", do.vm.ToValue(do.GetCode))
+ o.Set("getState", do.vm.ToValue(do.GetState))
+ o.Set("exists", do.vm.ToValue(do.Exists))
return o
}
type contractObj struct {
- contract *vm.Contract
+ contract *fakevm.Contract
vm *goja.Runtime
toBig toBigFn
toBuf toBufFn
@@ -806,7 +784,7 @@ func (co *contractObj) GetValue() goja.Value {
}
func (co *contractObj) GetInput() goja.Value {
- input := co.contract.Input
+ input := common.CopyBytes(co.contract.Input)
res, err := co.toBuf(co.vm, input)
if err != nil {
co.vm.Interrupt(err)
@@ -815,24 +793,12 @@ func (co *contractObj) GetInput() goja.Value {
return res
}
-func (co *contractObj) setupObject() *goja.Object {
- o := co.vm.NewObject()
- err := o.Set("getCaller", co.vm.ToValue(co.GetCaller))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getAddress", co.vm.ToValue(co.GetAddress))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getValue", co.vm.ToValue(co.GetValue))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getInput", co.vm.ToValue(co.GetInput))
- if err != nil {
- log.Error(err)
- }
+func (c *contractObj) setupObject() *goja.Object {
+ o := c.vm.NewObject()
+ o.Set("getCaller", c.vm.ToValue(c.GetCaller))
+ o.Set("getAddress", c.vm.ToValue(c.GetAddress))
+ o.Set("getValue", c.vm.ToValue(c.GetValue))
+ o.Set("getInput", c.vm.ToValue(c.GetInput))
return o
}
@@ -901,30 +867,12 @@ func (f *callframe) GetValue() goja.Value {
func (f *callframe) setupObject() *goja.Object {
o := f.vm.NewObject()
- err := o.Set("getType", f.vm.ToValue(f.GetType))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getFrom", f.vm.ToValue(f.GetFrom))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getTo", f.vm.ToValue(f.GetTo))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getInput", f.vm.ToValue(f.GetInput))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getGas", f.vm.ToValue(f.GetGas))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getValue", f.vm.ToValue(f.GetValue))
- if err != nil {
- log.Error(err)
- }
+ o.Set("getType", f.vm.ToValue(f.GetType))
+ o.Set("getFrom", f.vm.ToValue(f.GetFrom))
+ o.Set("getTo", f.vm.ToValue(f.GetTo))
+ o.Set("getInput", f.vm.ToValue(f.GetInput))
+ o.Set("getGas", f.vm.ToValue(f.GetGas))
+ o.Set("getValue", f.vm.ToValue(f.GetValue))
return o
}
@@ -959,18 +907,9 @@ func (r *callframeResult) GetError() goja.Value {
func (r *callframeResult) setupObject() *goja.Object {
o := r.vm.NewObject()
- err := o.Set("getGasUsed", r.vm.ToValue(r.GetGasUsed))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getOutput", r.vm.ToValue(r.GetOutput))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getError", r.vm.ToValue(r.GetError))
- if err != nil {
- log.Error(err)
- }
+ o.Set("getGasUsed", r.vm.ToValue(r.GetGasUsed))
+ o.Set("getOutput", r.vm.ToValue(r.GetOutput))
+ o.Set("getError", r.vm.ToValue(r.GetError))
return o
}
@@ -982,33 +921,19 @@ type steplog struct {
stack *stackObj
contract *contractObj
- pc uint
- gas uint
- cost uint
- depth uint
- refund uint
+ pc uint64
+ gas uint64
+ cost uint64
+ depth int
+ refund uint64
err error
}
-func (l *steplog) GetPC() uint {
- return l.pc
-}
-
-func (l *steplog) GetGas() uint {
- return l.gas
-}
-
-func (l *steplog) GetCost() uint {
- return l.cost
-}
-
-func (l *steplog) GetDepth() uint {
- return l.depth
-}
-
-func (l *steplog) GetRefund() uint {
- return l.refund
-}
+func (l *steplog) GetPC() uint64 { return l.pc }
+func (l *steplog) GetGas() uint64 { return l.gas }
+func (l *steplog) GetCost() uint64 { return l.cost }
+func (l *steplog) GetDepth() int { return l.depth }
+func (l *steplog) GetRefund() uint64 { return l.refund }
func (l *steplog) GetError() goja.Value {
if l.err != nil {
@@ -1020,46 +945,23 @@ func (l *steplog) GetError() goja.Value {
func (l *steplog) setupObject() *goja.Object {
o := l.vm.NewObject()
// Setup basic fields.
- err := o.Set("getPC", l.vm.ToValue(l.GetPC))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getGas", l.vm.ToValue(l.GetGas))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getCost", l.vm.ToValue(l.GetCost))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getDepth", l.vm.ToValue(l.GetDepth))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getRefund", l.vm.ToValue(l.GetRefund))
- if err != nil {
- log.Error(err)
- }
- err = o.Set("getError", l.vm.ToValue(l.GetError))
- if err != nil {
- log.Error(err)
- }
+ o.Set("getPC", l.vm.ToValue(l.GetPC))
+ o.Set("getGas", l.vm.ToValue(l.GetGas))
+ o.Set("getCost", l.vm.ToValue(l.GetCost))
+ o.Set("getDepth", l.vm.ToValue(l.GetDepth))
+ o.Set("getRefund", l.vm.ToValue(l.GetRefund))
+ o.Set("getError", l.vm.ToValue(l.GetError))
// Setup nested objects.
- err = o.Set("op", l.op.setupObject())
- if err != nil {
- log.Error(err)
- }
- err = o.Set("stack", l.stack.setupObject())
- if err != nil {
- log.Error(err)
- }
- err = o.Set("memory", l.memory.setupObject())
- if err != nil {
- log.Error(err)
- }
- err = o.Set("contract", l.contract.setupObject())
- if err != nil {
- log.Error(err)
- }
+ o.Set("op", l.op.setupObject())
+ o.Set("stack", l.stack.setupObject())
+ o.Set("memory", l.memory.setupObject())
+ o.Set("contract", l.contract.setupObject())
return o
}
+
+func min(a, b int64) int64 {
+ if a < b {
+ return a
+ }
+ return b
+}
diff --git a/state/runtime/instrumentation/tracers/native/4byte.go b/state/runtime/instrumentation/tracers/native/4byte.go
new file mode 100644
index 0000000000..524f26f7fc
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/4byte.go
@@ -0,0 +1,132 @@
+// Copyright 2021 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 .
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+ "strconv"
+ "sync/atomic"
+
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func init() {
+ tracers.DefaultDirectory.Register("4byteTracer", NewFourByteTracer, false)
+}
+
+// fourByteTracer searches for 4byte-identifiers, and collects them for post-processing.
+// It collects the methods identifiers along with the size of the supplied data, so
+// a reversed signature can be matched against the size of the data.
+//
+// Example:
+//
+// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
+// {
+// 0x27dc297e-128: 1,
+// 0x38cc4831-0: 2,
+// 0x524f3889-96: 1,
+// 0xadf59f99-288: 1,
+// 0xc281d19e-0: 1
+// }
+type fourByteTracer struct {
+ noopTracer
+ ids map[string]int // ids aggregates the 4byte ids found
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+}
+
+// NewFourByteTracer returns a native go tracer which collects
+// 4 byte-identifiers of a tx, and implements vm.EVMLogger.
+func NewFourByteTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
+ t := &fourByteTracer{
+ ids: make(map[string]int),
+ }
+ return t, nil
+}
+
+// isPrecompiled returns whether the addr is a precompile. Logic borrowed from newJsTracer in eth/tracers/js/tracer.go
+func (t *fourByteTracer) isPrecompiled(addr common.Address) bool {
+ for _, p := range t.activePrecompiles {
+ if p == addr {
+ return true
+ }
+ }
+ return false
+}
+
+// store saves the given identifier and datasize.
+func (t *fourByteTracer) store(id []byte, size int) {
+ key := bytesToHex(id) + "-" + strconv.Itoa(size)
+ t.ids[key] += 1
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *fourByteTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ // Update list of precompiles based on current block
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
+ t.activePrecompiles = fakevm.ActivePrecompiles(rules)
+
+ // Save the outer calldata also
+ if len(input) >= 4 {
+ t.store(input[0:4], len(input)-4)
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *fourByteTracer) CaptureEnter(op fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ // Skip if tracing was interrupted
+ if atomic.LoadUint32(&t.interrupt) > 0 {
+ return
+ }
+ if len(input) < 4 {
+ return
+ }
+ // primarily we want to avoid CREATE/CREATE2/SELFDESTRUCT
+ if op != fakevm.DELEGATECALL && op != fakevm.STATICCALL &&
+ op != fakevm.CALL && op != fakevm.CALLCODE {
+ return
+ }
+ // Skip any pre-compile invocations, those are just fancy opcodes
+ if t.isPrecompiled(to) {
+ return
+ }
+ t.store(input[0:4], len(input)-4)
+}
+
+// GetResult returns the json-encoded nested list of call traces, and any
+// error arising from the encoding or forceful termination (via `Stop`).
+func (t *fourByteTracer) GetResult() (json.RawMessage, error) {
+ res, err := json.Marshal(t.ids)
+ if err != nil {
+ return nil, err
+ }
+ return res, t.reason
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *fourByteTracer) Stop(err error) {
+ t.reason = err
+ atomic.StoreUint32(&t.interrupt, 1)
+}
+
+func bytesToHex(s []byte) string {
+ return "0x" + common.Bytes2Hex(s)
+}
diff --git a/state/runtime/instrumentation/tracers/native/call.go b/state/runtime/instrumentation/tracers/native/call.go
new file mode 100644
index 0000000000..a4975ed114
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/call.go
@@ -0,0 +1,270 @@
+// Copyright 2021 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 .
+
+package native
+
+import (
+ "encoding/json"
+ "errors"
+ "math/big"
+ "sync/atomic"
+
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+//go:generate go run github.com/fjl/gencodec -type callFrame -field-override callFrameMarshaling -out gen_callframe_json.go
+
+func init() {
+ tracers.DefaultDirectory.Register("callTracer", NewCallTracer, false)
+}
+
+type callLog struct {
+ Address common.Address `json:"address"`
+ Topics []common.Hash `json:"topics"`
+ Data hexutil.Bytes `json:"data"`
+}
+
+type callFrame struct {
+ Type fakevm.OpCode `json:"-"`
+ From common.Address `json:"from"`
+ Gas uint64 `json:"gas"`
+ GasUsed uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty" rlp:"optional"`
+ Input []byte `json:"input" rlp:"optional"`
+ Output []byte `json:"output,omitempty" rlp:"optional"`
+ Error string `json:"error,omitempty" rlp:"optional"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ // Placed at end on purpose. The RLP will be decoded to 0 instead of
+ // nil if there are non-empty elements after in the struct.
+ Value *big.Int `json:"value,omitempty" rlp:"optional"`
+}
+
+func (f callFrame) TypeString() string {
+ return f.Type.String()
+}
+
+func (f callFrame) failed() bool {
+ return len(f.Error) > 0
+}
+
+func (f *callFrame) processOutput(output []byte, err error) {
+ output = common.CopyBytes(output)
+ if err == nil {
+ f.Output = output
+ return
+ }
+ f.Error = err.Error()
+ if f.Type == fakevm.CREATE || f.Type == fakevm.CREATE2 {
+ f.To = nil
+ }
+ if !errors.Is(err, fakevm.ErrExecutionReverted) || len(output) == 0 {
+ return
+ }
+ f.Output = output
+ if len(output) < 4 {
+ return
+ }
+ if unpacked, err := abi.UnpackRevert(output); err == nil {
+ f.RevertReason = unpacked
+ }
+}
+
+type callFrameMarshaling struct {
+ TypeString string `json:"type"`
+ Gas hexutil.Uint64
+ GasUsed hexutil.Uint64
+ Value *hexutil.Big
+ Input hexutil.Bytes
+ Output hexutil.Bytes
+}
+
+type callTracer struct {
+ noopTracer
+ callstack []callFrame
+ config callTracerConfig
+ gasLimit uint64
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+}
+
+type callTracerConfig struct {
+ OnlyTopCall bool `json:"onlyTopCall"` // If true, call tracer won't collect any subcalls
+ WithLog bool `json:"withLog"` // If true, call tracer will collect event logs
+}
+
+// NewCallTracer returns a native go tracer which tracks
+// call frames of a tx, and implements fakevm.EVMLogger.
+func NewCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config callTracerConfig
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+ // First callframe contains tx context info
+ // and is populated on start and end.
+ return &callTracer{callstack: make([]callFrame, 1), config: config}, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *callTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ toCopy := to
+ t.callstack[0] = callFrame{
+ Type: fakevm.CALL,
+ From: from,
+ To: &toCopy,
+ Input: common.CopyBytes(input),
+ Gas: gas,
+ Value: value,
+ }
+ if create {
+ t.callstack[0].Type = fakevm.CREATE
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *callTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ t.callstack[0].processOutput(output, err)
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *callTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
+ // Only logs need to be captured via opcode processing
+ if !t.config.WithLog {
+ return
+ }
+ // Avoid processing nested calls when only caring about top call
+ if t.config.OnlyTopCall && depth > 0 {
+ return
+ }
+ // Skip if tracing was interrupted
+ if atomic.LoadUint32(&t.interrupt) > 0 {
+ return
+ }
+ switch op {
+ case fakevm.LOG0, fakevm.LOG1, fakevm.LOG2, fakevm.LOG3, fakevm.LOG4:
+ size := int(op - fakevm.LOG0)
+
+ stack := scope.Stack
+ stackData := stack.Data()
+
+ // Don't modify the stack
+ mStart := stackData[len(stackData)-1]
+ mSize := stackData[len(stackData)-2]
+ topics := make([]common.Hash, size)
+ for i := 0; i < size; i++ {
+ topic := stackData[len(stackData)-2-(i+1)]
+ topics[i] = common.Hash(topic.Bytes32())
+ }
+
+ data := scope.Memory.GetCopy(int64(mStart.Uint64()), int64(mSize.Uint64()))
+ log := callLog{Address: scope.Contract.Address(), Topics: topics, Data: hexutil.Bytes(data)}
+ t.callstack[len(t.callstack)-1].Logs = append(t.callstack[len(t.callstack)-1].Logs, log)
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *callTracer) CaptureEnter(typ fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ if t.config.OnlyTopCall {
+ return
+ }
+ // Skip if tracing was interrupted
+ if atomic.LoadUint32(&t.interrupt) > 0 {
+ return
+ }
+
+ toCopy := to
+ call := callFrame{
+ Type: typ,
+ From: from,
+ To: &toCopy,
+ Input: common.CopyBytes(input),
+ Gas: gas,
+ Value: value,
+ }
+ t.callstack = append(t.callstack, call)
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *callTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ if t.config.OnlyTopCall {
+ return
+ }
+ size := len(t.callstack)
+ if size <= 1 {
+ return
+ }
+ // pop call
+ call := t.callstack[size-1]
+ t.callstack = t.callstack[:size-1]
+ size -= 1
+
+ call.GasUsed = gasUsed
+ call.processOutput(output, err)
+ t.callstack[size-1].Calls = append(t.callstack[size-1].Calls, call)
+}
+
+func (t *callTracer) CaptureTxStart(gasLimit uint64) {
+ t.gasLimit = gasLimit
+}
+
+func (t *callTracer) CaptureTxEnd(restGas uint64) {
+ t.callstack[0].GasUsed = t.gasLimit - restGas
+ if t.config.WithLog {
+ // Logs are not emitted when the call fails
+ clearFailedLogs(&t.callstack[0], false)
+ }
+}
+
+// GetResult returns the json-encoded nested list of call traces, and any
+// error arising from the encoding or forceful termination (via `Stop`).
+func (t *callTracer) GetResult() (json.RawMessage, error) {
+ if len(t.callstack) != 1 {
+ return nil, errors.New("incorrect number of top-level calls")
+ }
+
+ res, err := json.Marshal(t.callstack[0])
+ if err != nil {
+ return nil, err
+ }
+ return json.RawMessage(res), t.reason
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *callTracer) Stop(err error) {
+ t.reason = err
+ atomic.StoreUint32(&t.interrupt, 1)
+}
+
+// clearFailedLogs clears the logs of a callframe and all its children
+// in case of execution failure.
+func clearFailedLogs(cf *callFrame, parentFailed bool) {
+ failed := cf.failed() || parentFailed
+ // Clear own logs
+ if failed {
+ cf.Logs = nil
+ }
+ for i := range cf.Calls {
+ clearFailedLogs(&cf.Calls[i], failed)
+ }
+}
diff --git a/state/runtime/instrumentation/tracers/native/call_flat.go b/state/runtime/instrumentation/tracers/native/call_flat.go
new file mode 100644
index 0000000000..c91077f9de
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/call_flat.go
@@ -0,0 +1,379 @@
+// Copyright 2022 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 .
+
+package native
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+//go:generate go run github.com/fjl/gencodec -type flatCallAction -field-override flatCallActionMarshaling -out gen_flatcallaction_json.go
+//go:generate go run github.com/fjl/gencodec -type flatCallResult -field-override flatCallResultMarshaling -out gen_flatcallresult_json.go
+
+func init() {
+ tracers.DefaultDirectory.Register("flatCallTracer", newFlatCallTracer, false)
+}
+
+var parityErrorMapping = map[string]string{
+ "contract creation code storage out of gas": "Out of gas",
+ "out of gas": "Out of gas",
+ "gas uint64 overflow": "Out of gas",
+ "max code size exceeded": "Out of gas",
+ "invalid jump destination": "Bad jump destination",
+ "execution reverted": "Reverted",
+ "return data out of bounds": "Out of bounds",
+ "stack limit reached 1024 (1023)": "Out of stack",
+ "precompiled failed": "Built-in failed",
+ "invalid input length": "Built-in failed",
+}
+
+var parityErrorMappingStartingWith = map[string]string{
+ "invalid opcode:": "Bad instruction",
+ "stack underflow": "Stack underflow",
+}
+
+// flatCallFrame is a standalone callframe.
+type flatCallFrame struct {
+ Action flatCallAction `json:"action"`
+ BlockHash *common.Hash `json:"blockHash"`
+ BlockNumber uint64 `json:"blockNumber"`
+ Error string `json:"error,omitempty"`
+ Result *flatCallResult `json:"result,omitempty"`
+ Subtraces int `json:"subtraces"`
+ TraceAddress []int `json:"traceAddress"`
+ TransactionHash *common.Hash `json:"transactionHash"`
+ TransactionPosition uint64 `json:"transactionPosition"`
+ Type string `json:"type"`
+}
+
+type flatCallAction struct {
+ Author *common.Address `json:"author,omitempty"`
+ RewardType string `json:"rewardType,omitempty"`
+ SelfDestructed *common.Address `json:"address,omitempty"`
+ Balance *big.Int `json:"balance,omitempty"`
+ CallType string `json:"callType,omitempty"`
+ CreationMethod string `json:"creationMethod,omitempty"`
+ From *common.Address `json:"from,omitempty"`
+ Gas *uint64 `json:"gas,omitempty"`
+ Init *[]byte `json:"init,omitempty"`
+ Input *[]byte `json:"input,omitempty"`
+ RefundAddress *common.Address `json:"refundAddress,omitempty"`
+ To *common.Address `json:"to,omitempty"`
+ Value *big.Int `json:"value,omitempty"`
+}
+
+type flatCallActionMarshaling struct {
+ Balance *hexutil.Big
+ Gas *hexutil.Uint64
+ Init *hexutil.Bytes
+ Input *hexutil.Bytes
+ Value *hexutil.Big
+}
+
+type flatCallResult struct {
+ Address *common.Address `json:"address,omitempty"`
+ Code *[]byte `json:"code,omitempty"`
+ GasUsed *uint64 `json:"gasUsed,omitempty"`
+ Output *[]byte `json:"output,omitempty"`
+}
+
+type flatCallResultMarshaling struct {
+ Code *hexutil.Bytes
+ GasUsed *hexutil.Uint64
+ Output *hexutil.Bytes
+}
+
+// flatCallTracer reports call frame information of a tx in a flat format, i.e.
+// as opposed to the nested format of `callTracer`.
+type flatCallTracer struct {
+ tracer *callTracer
+ config flatCallTracerConfig
+ ctx *tracers.Context // Holds tracer context data
+ reason error // Textual reason for the interruption
+ activePrecompiles []common.Address // Updated on CaptureStart based on given rules
+}
+
+type flatCallTracerConfig struct {
+ ConvertParityErrors bool `json:"convertParityErrors"` // If true, call tracer converts errors to parity format
+ IncludePrecompiles bool `json:"includePrecompiles"` // If true, call tracer includes calls to precompiled contracts
+}
+
+// newFlatCallTracer returns a new flatCallTracer.
+func newFlatCallTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config flatCallTracerConfig
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+
+ tracer, err := tracers.DefaultDirectory.New("callTracer", ctx, cfg)
+ if err != nil {
+ return nil, err
+ }
+ t, ok := tracer.(*callTracer)
+ if !ok {
+ return nil, errors.New("internal error: embedded tracer has wrong type")
+ }
+
+ return &flatCallTracer{tracer: t, ctx: ctx, config: config}, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *flatCallTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.tracer.CaptureStart(env, from, to, create, input, gas, value)
+ // Update list of precompiles based on current block
+ rules := env.ChainConfig().Rules(env.Context.BlockNumber, env.Context.Random != nil, env.Context.Time)
+ t.activePrecompiles = fakevm.ActivePrecompiles(rules)
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *flatCallTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ t.tracer.CaptureEnd(output, gasUsed, err)
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *flatCallTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
+ t.tracer.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *flatCallTracer) CaptureFault(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, depth int, err error) {
+ t.tracer.CaptureFault(pc, op, gas, cost, scope, depth, err)
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *flatCallTracer) CaptureEnter(typ fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ t.tracer.CaptureEnter(typ, from, to, input, gas, value)
+
+ // Child calls must have a value, even if it's zero.
+ // Practically speaking, only STATICCALL has nil value. Set it to zero.
+ if t.tracer.callstack[len(t.tracer.callstack)-1].Value == nil && value == nil {
+ t.tracer.callstack[len(t.tracer.callstack)-1].Value = big.NewInt(0)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *flatCallTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ t.tracer.CaptureExit(output, gasUsed, err)
+
+ // Parity traces don't include CALL/STATICCALLs to precompiles.
+ // By default we remove them from the callstack.
+ if t.config.IncludePrecompiles {
+ return
+ }
+ var (
+ // call has been nested in parent
+ parent = t.tracer.callstack[len(t.tracer.callstack)-1]
+ call = parent.Calls[len(parent.Calls)-1]
+ typ = call.Type
+ to = call.To
+ )
+ if typ == fakevm.CALL || typ == fakevm.STATICCALL {
+ if t.isPrecompiled(*to) {
+ t.tracer.callstack[len(t.tracer.callstack)-1].Calls = parent.Calls[:len(parent.Calls)-1]
+ }
+ }
+}
+
+func (t *flatCallTracer) CaptureTxStart(gasLimit uint64) {
+ t.tracer.CaptureTxStart(gasLimit)
+}
+
+func (t *flatCallTracer) CaptureTxEnd(restGas uint64) {
+ t.tracer.CaptureTxEnd(restGas)
+}
+
+// GetResult returns an empty json object.
+func (t *flatCallTracer) GetResult() (json.RawMessage, error) {
+ if len(t.tracer.callstack) < 1 {
+ return nil, errors.New("invalid number of calls")
+ }
+
+ flat, err := flatFromNested(&t.tracer.callstack[0], []int{}, t.config.ConvertParityErrors, t.ctx)
+ if err != nil {
+ return nil, err
+ }
+
+ res, err := json.Marshal(flat)
+ if err != nil {
+ return nil, err
+ }
+ return res, t.reason
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *flatCallTracer) Stop(err error) {
+ t.tracer.Stop(err)
+}
+
+// isPrecompiled returns whether the addr is a precompile.
+func (t *flatCallTracer) isPrecompiled(addr common.Address) bool {
+ for _, p := range t.activePrecompiles {
+ if p == addr {
+ return true
+ }
+ }
+ return false
+}
+
+func flatFromNested(input *callFrame, traceAddress []int, convertErrs bool, ctx *tracers.Context) (output []flatCallFrame, err error) {
+ var frame *flatCallFrame
+ switch input.Type {
+ case fakevm.CREATE, fakevm.CREATE2:
+ frame = newFlatCreate(input)
+ case fakevm.SELFDESTRUCT:
+ frame = newFlatSuicide(input)
+ case fakevm.CALL, fakevm.STATICCALL, fakevm.CALLCODE, fakevm.DELEGATECALL:
+ frame = newFlatCall(input)
+ default:
+ return nil, fmt.Errorf("unrecognized call frame type: %s", input.Type)
+ }
+
+ frame.TraceAddress = traceAddress
+ frame.Error = input.Error
+ frame.Subtraces = len(input.Calls)
+ fillCallFrameFromContext(frame, ctx)
+ if convertErrs {
+ convertErrorToParity(frame)
+ }
+
+ // Revert output contains useful information (revert reason).
+ // Otherwise discard result.
+ if input.Error != "" && input.Error != fakevm.ErrExecutionReverted.Error() {
+ frame.Result = nil
+ }
+
+ output = append(output, *frame)
+ if len(input.Calls) > 0 {
+ for i, childCall := range input.Calls {
+ childAddr := childTraceAddress(traceAddress, i)
+ childCallCopy := childCall
+ flat, err := flatFromNested(&childCallCopy, childAddr, convertErrs, ctx)
+ if err != nil {
+ return nil, err
+ }
+ output = append(output, flat...)
+ }
+ }
+
+ return output, nil
+}
+
+func newFlatCreate(input *callFrame) *flatCallFrame {
+ var (
+ actionInit = input.Input[:]
+ resultCode = input.Output[:]
+ )
+
+ return &flatCallFrame{
+ Type: strings.ToLower(fakevm.CREATE.String()),
+ Action: flatCallAction{
+ From: &input.From,
+ Gas: &input.Gas,
+ Value: input.Value,
+ Init: &actionInit,
+ },
+ Result: &flatCallResult{
+ GasUsed: &input.GasUsed,
+ Address: input.To,
+ Code: &resultCode,
+ },
+ }
+}
+
+func newFlatCall(input *callFrame) *flatCallFrame {
+ var (
+ actionInput = input.Input[:]
+ resultOutput = input.Output[:]
+ )
+
+ return &flatCallFrame{
+ Type: strings.ToLower(fakevm.CALL.String()),
+ Action: flatCallAction{
+ From: &input.From,
+ To: input.To,
+ Gas: &input.Gas,
+ Value: input.Value,
+ CallType: strings.ToLower(input.Type.String()),
+ Input: &actionInput,
+ },
+ Result: &flatCallResult{
+ GasUsed: &input.GasUsed,
+ Output: &resultOutput,
+ },
+ }
+}
+
+func newFlatSuicide(input *callFrame) *flatCallFrame {
+ return &flatCallFrame{
+ Type: "suicide",
+ Action: flatCallAction{
+ SelfDestructed: &input.From,
+ Balance: input.Value,
+ RefundAddress: input.To,
+ },
+ }
+}
+
+func fillCallFrameFromContext(callFrame *flatCallFrame, ctx *tracers.Context) {
+ if ctx == nil {
+ return
+ }
+ if ctx.BlockHash != (common.Hash{}) {
+ callFrame.BlockHash = &ctx.BlockHash
+ }
+ if ctx.BlockNumber != nil {
+ callFrame.BlockNumber = ctx.BlockNumber.Uint64()
+ }
+ if ctx.TxHash != (common.Hash{}) {
+ callFrame.TransactionHash = &ctx.TxHash
+ }
+ callFrame.TransactionPosition = uint64(ctx.TxIndex)
+}
+
+func convertErrorToParity(call *flatCallFrame) {
+ if call.Error == "" {
+ return
+ }
+
+ if parityError, ok := parityErrorMapping[call.Error]; ok {
+ call.Error = parityError
+ } else {
+ for gethError, parityError := range parityErrorMappingStartingWith {
+ if strings.HasPrefix(call.Error, gethError) {
+ call.Error = parityError
+ }
+ }
+ }
+}
+
+func childTraceAddress(a []int, i int) []int {
+ child := make([]int, 0, len(a)+1)
+ child = append(child, a...)
+ child = append(child, i)
+ return child
+}
diff --git a/state/runtime/instrumentation/tracers/native/gen_account_json.go b/state/runtime/instrumentation/tracers/native/gen_account_json.go
new file mode 100644
index 0000000000..4c39cbc38c
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/gen_account_json.go
@@ -0,0 +1,56 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*accountMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (a account) MarshalJSON() ([]byte, error) {
+ type account struct {
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ Code hexutil.Bytes `json:"code,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ }
+ var enc account
+ enc.Balance = (*hexutil.Big)(a.Balance)
+ enc.Code = a.Code
+ enc.Nonce = a.Nonce
+ enc.Storage = a.Storage
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (a *account) UnmarshalJSON(input []byte) error {
+ type account struct {
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ Code *hexutil.Bytes `json:"code,omitempty"`
+ Nonce *uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+ }
+ var dec account
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Balance != nil {
+ a.Balance = (*big.Int)(dec.Balance)
+ }
+ if dec.Code != nil {
+ a.Code = *dec.Code
+ }
+ if dec.Nonce != nil {
+ a.Nonce = *dec.Nonce
+ }
+ if dec.Storage != nil {
+ a.Storage = dec.Storage
+ }
+ return nil
+}
diff --git a/state/runtime/instrumentation/tracers/native/gen_callframe_json.go b/state/runtime/instrumentation/tracers/native/gen_callframe_json.go
new file mode 100644
index 0000000000..b17cb22dd8
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/gen_callframe_json.go
@@ -0,0 +1,107 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+)
+
+var _ = (*callFrameMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (c callFrame) MarshalJSON() ([]byte, error) {
+ type callFrame0 struct {
+ Type fakevm.OpCode `json:"-"`
+ From common.Address `json:"from"`
+ Gas hexutil.Uint64 `json:"gas"`
+ GasUsed hexutil.Uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty" rlp:"optional"`
+ Input hexutil.Bytes `json:"input" rlp:"optional"`
+ Output hexutil.Bytes `json:"output,omitempty" rlp:"optional"`
+ Error string `json:"error,omitempty" rlp:"optional"`
+ RevertReason string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ Value *hexutil.Big `json:"value,omitempty" rlp:"optional"`
+ TypeString string `json:"type"`
+ }
+ var enc callFrame0
+ enc.Type = c.Type
+ enc.From = c.From
+ enc.Gas = hexutil.Uint64(c.Gas)
+ enc.GasUsed = hexutil.Uint64(c.GasUsed)
+ enc.To = c.To
+ enc.Input = c.Input
+ enc.Output = c.Output
+ enc.Error = c.Error
+ enc.RevertReason = c.RevertReason
+ enc.Calls = c.Calls
+ enc.Logs = c.Logs
+ enc.Value = (*hexutil.Big)(c.Value)
+ enc.TypeString = c.TypeString()
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (c *callFrame) UnmarshalJSON(input []byte) error {
+ type callFrame0 struct {
+ Type *fakevm.OpCode `json:"-"`
+ From *common.Address `json:"from"`
+ Gas *hexutil.Uint64 `json:"gas"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed"`
+ To *common.Address `json:"to,omitempty" rlp:"optional"`
+ Input *hexutil.Bytes `json:"input" rlp:"optional"`
+ Output *hexutil.Bytes `json:"output,omitempty" rlp:"optional"`
+ Error *string `json:"error,omitempty" rlp:"optional"`
+ RevertReason *string `json:"revertReason,omitempty"`
+ Calls []callFrame `json:"calls,omitempty" rlp:"optional"`
+ Logs []callLog `json:"logs,omitempty" rlp:"optional"`
+ Value *hexutil.Big `json:"value,omitempty" rlp:"optional"`
+ }
+ var dec callFrame0
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Type != nil {
+ c.Type = *dec.Type
+ }
+ if dec.From != nil {
+ c.From = *dec.From
+ }
+ if dec.Gas != nil {
+ c.Gas = uint64(*dec.Gas)
+ }
+ if dec.GasUsed != nil {
+ c.GasUsed = uint64(*dec.GasUsed)
+ }
+ if dec.To != nil {
+ c.To = dec.To
+ }
+ if dec.Input != nil {
+ c.Input = *dec.Input
+ }
+ if dec.Output != nil {
+ c.Output = *dec.Output
+ }
+ if dec.Error != nil {
+ c.Error = *dec.Error
+ }
+ if dec.RevertReason != nil {
+ c.RevertReason = *dec.RevertReason
+ }
+ if dec.Calls != nil {
+ c.Calls = dec.Calls
+ }
+ if dec.Logs != nil {
+ c.Logs = dec.Logs
+ }
+ if dec.Value != nil {
+ c.Value = (*big.Int)(dec.Value)
+ }
+ return nil
+}
diff --git a/state/runtime/instrumentation/tracers/native/gen_flatcallaction_json.go b/state/runtime/instrumentation/tracers/native/gen_flatcallaction_json.go
new file mode 100644
index 0000000000..c075606983
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/gen_flatcallaction_json.go
@@ -0,0 +1,110 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*flatCallActionMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (f flatCallAction) MarshalJSON() ([]byte, error) {
+ type flatCallAction struct {
+ Author *common.Address `json:"author,omitempty"`
+ RewardType string `json:"rewardType,omitempty"`
+ SelfDestructed *common.Address `json:"address,omitempty"`
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ CallType string `json:"callType,omitempty"`
+ CreationMethod string `json:"creationMethod,omitempty"`
+ From *common.Address `json:"from,omitempty"`
+ Gas *hexutil.Uint64 `json:"gas,omitempty"`
+ Init *hexutil.Bytes `json:"init,omitempty"`
+ Input *hexutil.Bytes `json:"input,omitempty"`
+ RefundAddress *common.Address `json:"refundAddress,omitempty"`
+ To *common.Address `json:"to,omitempty"`
+ Value *hexutil.Big `json:"value,omitempty"`
+ }
+ var enc flatCallAction
+ enc.Author = f.Author
+ enc.RewardType = f.RewardType
+ enc.SelfDestructed = f.SelfDestructed
+ enc.Balance = (*hexutil.Big)(f.Balance)
+ enc.CallType = f.CallType
+ enc.CreationMethod = f.CreationMethod
+ enc.From = f.From
+ enc.Gas = (*hexutil.Uint64)(f.Gas)
+ enc.Init = (*hexutil.Bytes)(f.Init)
+ enc.Input = (*hexutil.Bytes)(f.Input)
+ enc.RefundAddress = f.RefundAddress
+ enc.To = f.To
+ enc.Value = (*hexutil.Big)(f.Value)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (f *flatCallAction) UnmarshalJSON(input []byte) error {
+ type flatCallAction struct {
+ Author *common.Address `json:"author,omitempty"`
+ RewardType *string `json:"rewardType,omitempty"`
+ SelfDestructed *common.Address `json:"address,omitempty"`
+ Balance *hexutil.Big `json:"balance,omitempty"`
+ CallType *string `json:"callType,omitempty"`
+ CreationMethod *string `json:"creationMethod,omitempty"`
+ From *common.Address `json:"from,omitempty"`
+ Gas *hexutil.Uint64 `json:"gas,omitempty"`
+ Init *hexutil.Bytes `json:"init,omitempty"`
+ Input *hexutil.Bytes `json:"input,omitempty"`
+ RefundAddress *common.Address `json:"refundAddress,omitempty"`
+ To *common.Address `json:"to,omitempty"`
+ Value *hexutil.Big `json:"value,omitempty"`
+ }
+ var dec flatCallAction
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Author != nil {
+ f.Author = dec.Author
+ }
+ if dec.RewardType != nil {
+ f.RewardType = *dec.RewardType
+ }
+ if dec.SelfDestructed != nil {
+ f.SelfDestructed = dec.SelfDestructed
+ }
+ if dec.Balance != nil {
+ f.Balance = (*big.Int)(dec.Balance)
+ }
+ if dec.CallType != nil {
+ f.CallType = *dec.CallType
+ }
+ if dec.CreationMethod != nil {
+ f.CreationMethod = *dec.CreationMethod
+ }
+ if dec.From != nil {
+ f.From = dec.From
+ }
+ if dec.Gas != nil {
+ f.Gas = (*uint64)(dec.Gas)
+ }
+ if dec.Init != nil {
+ f.Init = (*[]byte)(dec.Init)
+ }
+ if dec.Input != nil {
+ f.Input = (*[]byte)(dec.Input)
+ }
+ if dec.RefundAddress != nil {
+ f.RefundAddress = dec.RefundAddress
+ }
+ if dec.To != nil {
+ f.To = dec.To
+ }
+ if dec.Value != nil {
+ f.Value = (*big.Int)(dec.Value)
+ }
+ return nil
+}
diff --git a/state/runtime/instrumentation/tracers/native/gen_flatcallresult_json.go b/state/runtime/instrumentation/tracers/native/gen_flatcallresult_json.go
new file mode 100644
index 0000000000..e9fa5e44da
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/gen_flatcallresult_json.go
@@ -0,0 +1,55 @@
+// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
+
+package native
+
+import (
+ "encoding/json"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+var _ = (*flatCallResultMarshaling)(nil)
+
+// MarshalJSON marshals as JSON.
+func (f flatCallResult) MarshalJSON() ([]byte, error) {
+ type flatCallResult struct {
+ Address *common.Address `json:"address,omitempty"`
+ Code *hexutil.Bytes `json:"code,omitempty"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
+ Output *hexutil.Bytes `json:"output,omitempty"`
+ }
+ var enc flatCallResult
+ enc.Address = f.Address
+ enc.Code = (*hexutil.Bytes)(f.Code)
+ enc.GasUsed = (*hexutil.Uint64)(f.GasUsed)
+ enc.Output = (*hexutil.Bytes)(f.Output)
+ return json.Marshal(&enc)
+}
+
+// UnmarshalJSON unmarshals from JSON.
+func (f *flatCallResult) UnmarshalJSON(input []byte) error {
+ type flatCallResult struct {
+ Address *common.Address `json:"address,omitempty"`
+ Code *hexutil.Bytes `json:"code,omitempty"`
+ GasUsed *hexutil.Uint64 `json:"gasUsed,omitempty"`
+ Output *hexutil.Bytes `json:"output,omitempty"`
+ }
+ var dec flatCallResult
+ if err := json.Unmarshal(input, &dec); err != nil {
+ return err
+ }
+ if dec.Address != nil {
+ f.Address = dec.Address
+ }
+ if dec.Code != nil {
+ f.Code = (*[]byte)(dec.Code)
+ }
+ if dec.GasUsed != nil {
+ f.GasUsed = (*uint64)(dec.GasUsed)
+ }
+ if dec.Output != nil {
+ f.Output = (*[]byte)(dec.Output)
+ }
+ return nil
+}
diff --git a/state/runtime/instrumentation/tracers/native/mux.go b/state/runtime/instrumentation/tracers/native/mux.go
new file mode 100644
index 0000000000..db8ddd6438
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/mux.go
@@ -0,0 +1,138 @@
+// Copyright 2022 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 .
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
+)
+
+func init() {
+ tracers.DefaultDirectory.Register("muxTracer", newMuxTracer, false)
+}
+
+// muxTracer is a go implementation of the Tracer interface which
+// runs multiple tracers in one go.
+type muxTracer struct {
+ names []string
+ tracers []tracers.Tracer
+}
+
+// newMuxTracer returns a new mux tracer.
+func newMuxTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config map[string]json.RawMessage
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+ objects := make([]tracers.Tracer, 0, len(config))
+ names := make([]string, 0, len(config))
+ for k, v := range config {
+ t, err := tracers.DefaultDirectory.New(k, ctx, v)
+ if err != nil {
+ return nil, err
+ }
+ objects = append(objects, t)
+ names = append(names, k)
+ }
+
+ return &muxTracer{names: names, tracers: objects}, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *muxTracer) CaptureStart(env *vm.EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ for _, t := range t.tracers {
+ t.CaptureStart(env, from, to, create, input, gas, value)
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *muxTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ for _, t := range t.tracers {
+ t.CaptureEnd(output, gasUsed, err)
+ }
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *muxTracer) CaptureState(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, rData []byte, depth int, err error) {
+ for _, t := range t.tracers {
+ t.CaptureState(pc, op, gas, cost, scope, rData, depth, err)
+ }
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *muxTracer) CaptureFault(pc uint64, op vm.OpCode, gas, cost uint64, scope *vm.ScopeContext, depth int, err error) {
+ for _, t := range t.tracers {
+ t.CaptureFault(pc, op, gas, cost, scope, depth, err)
+ }
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *muxTracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ for _, t := range t.tracers {
+ t.CaptureEnter(typ, from, to, input, gas, value)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *muxTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ for _, t := range t.tracers {
+ t.CaptureExit(output, gasUsed, err)
+ }
+}
+
+func (t *muxTracer) CaptureTxStart(gasLimit uint64) {
+ for _, t := range t.tracers {
+ t.CaptureTxStart(gasLimit)
+ }
+}
+
+func (t *muxTracer) CaptureTxEnd(restGas uint64) {
+ for _, t := range t.tracers {
+ t.CaptureTxEnd(restGas)
+ }
+}
+
+// GetResult returns an empty json object.
+func (t *muxTracer) GetResult() (json.RawMessage, error) {
+ resObject := make(map[string]json.RawMessage)
+ for i, tt := range t.tracers {
+ r, err := tt.GetResult()
+ if err != nil {
+ return nil, err
+ }
+ resObject[t.names[i]] = r
+ }
+ res, err := json.Marshal(resObject)
+ if err != nil {
+ return nil, err
+ }
+ return res, nil
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *muxTracer) Stop(err error) {
+ for _, t := range t.tracers {
+ t.Stop(err)
+ }
+}
diff --git a/state/runtime/instrumentation/tracers/native/noop.go b/state/runtime/instrumentation/tracers/native/noop.go
new file mode 100644
index 0000000000..fc692363fb
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/noop.go
@@ -0,0 +1,77 @@
+// Copyright 2021 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 .
+
+package native
+
+import (
+ "encoding/json"
+ "math/big"
+
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/ethereum/go-ethereum/common"
+)
+
+func init() {
+ tracers.DefaultDirectory.Register("noopTracer", NewNoopTracer, false)
+}
+
+// noopTracer is a go implementation of the Tracer interface which
+// performs no action. It's mostly useful for testing purposes.
+type noopTracer struct{}
+
+// NewNoopTracer returns a new noop tracer.
+func NewNoopTracer(ctx *tracers.Context, _ json.RawMessage) (tracers.Tracer, error) {
+ return &noopTracer{}, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *noopTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *noopTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *noopTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
+}
+
+// CaptureFault implements the EVMLogger interface to trace an execution fault.
+func (t *noopTracer) CaptureFault(pc uint64, op fakevm.OpCode, gas, cost uint64, _ *fakevm.ScopeContext, depth int, err error) {
+}
+
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (t *noopTracer) CaptureEnter(typ fakevm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (t *noopTracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+}
+
+func (*noopTracer) CaptureTxStart(gasLimit uint64) {}
+
+func (*noopTracer) CaptureTxEnd(restGas uint64) {}
+
+// GetResult returns an empty json object.
+func (t *noopTracer) GetResult() (json.RawMessage, error) {
+ return json.RawMessage(`{}`), nil
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *noopTracer) Stop(err error) {
+}
diff --git a/state/runtime/instrumentation/tracers/native/prestate.go b/state/runtime/instrumentation/tracers/native/prestate.go
new file mode 100644
index 0000000000..16d99a23d4
--- /dev/null
+++ b/state/runtime/instrumentation/tracers/native/prestate.go
@@ -0,0 +1,285 @@
+// Copyright 2022 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 .
+
+package native
+
+import (
+ "bytes"
+ "encoding/json"
+ "math/big"
+ "sync/atomic"
+
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/crypto"
+)
+
+//go:generate go run github.com/fjl/gencodec -type account -field-override accountMarshaling -out gen_account_json.go
+
+func init() {
+ tracers.DefaultDirectory.Register("prestateTracer", NewPrestateTracer, false)
+}
+
+type state = map[common.Address]*account
+
+type account struct {
+ Balance *big.Int `json:"balance,omitempty"`
+ Code []byte `json:"code,omitempty"`
+ Nonce uint64 `json:"nonce,omitempty"`
+ Storage map[common.Hash]common.Hash `json:"storage,omitempty"`
+}
+
+func (a *account) exists() bool {
+ return a.Nonce > 0 || len(a.Code) > 0 || len(a.Storage) > 0 || (a.Balance != nil && a.Balance.Sign() != 0)
+}
+
+type accountMarshaling struct {
+ Balance *hexutil.Big
+ Code hexutil.Bytes
+}
+
+type prestateTracer struct {
+ noopTracer
+ env *fakevm.FakeEVM
+ pre state
+ post state
+ create bool
+ to common.Address
+ gasLimit uint64 // Amount of gas bought for the whole tx
+ config prestateTracerConfig
+ interrupt uint32 // Atomic flag to signal execution interruption
+ reason error // Textual reason for the interruption
+ created map[common.Address]bool
+ deleted map[common.Address]bool
+}
+
+type prestateTracerConfig struct {
+ DiffMode bool `json:"diffMode"` // If true, this tracer will return state modifications
+}
+
+func NewPrestateTracer(ctx *tracers.Context, cfg json.RawMessage) (tracers.Tracer, error) {
+ var config prestateTracerConfig
+ if cfg != nil {
+ if err := json.Unmarshal(cfg, &config); err != nil {
+ return nil, err
+ }
+ }
+ return &prestateTracer{
+ pre: state{},
+ post: state{},
+ config: config,
+ created: make(map[common.Address]bool),
+ deleted: make(map[common.Address]bool),
+ }, nil
+}
+
+// CaptureStart implements the EVMLogger interface to initialize the tracing operation.
+func (t *prestateTracer) CaptureStart(env *fakevm.FakeEVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int) {
+ t.env = env
+ t.create = create
+ t.to = to
+
+ t.lookupAccount(from)
+ t.lookupAccount(to)
+ t.lookupAccount(env.Context.Coinbase)
+
+ // The recipient balance includes the value transferred.
+ toBal := new(big.Int).Sub(t.pre[to].Balance, value)
+ t.pre[to].Balance = toBal
+
+ // The sender balance is after reducing: value and gasLimit.
+ // We need to re-add them to get the pre-tx balance.
+ fromBal := new(big.Int).Set(t.pre[from].Balance)
+ gasPrice := env.TxContext.GasPrice
+ consumedGas := new(big.Int).Mul(gasPrice, new(big.Int).SetUint64(t.gasLimit))
+ fromBal.Add(fromBal, new(big.Int).Add(value, consumedGas))
+ t.pre[from].Balance = fromBal
+ t.pre[from].Nonce--
+
+ if create && t.config.DiffMode {
+ t.created[to] = true
+ }
+}
+
+// CaptureEnd is called after the call finishes to finalize the tracing.
+func (t *prestateTracer) CaptureEnd(output []byte, gasUsed uint64, err error) {
+ if t.config.DiffMode {
+ return
+ }
+
+ if t.create {
+ // Keep existing account prior to contract creation at that address
+ if s := t.pre[t.to]; s != nil && !s.exists() {
+ // Exclude newly created contract.
+ delete(t.pre, t.to)
+ }
+ }
+}
+
+// CaptureState implements the EVMLogger interface to trace a single step of VM execution.
+func (t *prestateTracer) CaptureState(pc uint64, op fakevm.OpCode, gas, cost uint64, scope *fakevm.ScopeContext, rData []byte, depth int, err error) {
+ stack := scope.Stack
+ stackData := stack.Data()
+ stackLen := len(stackData)
+ caller := scope.Contract.Address()
+ switch {
+ case stackLen >= 1 && (op == fakevm.SLOAD || op == fakevm.SSTORE):
+ slot := common.Hash(stackData[stackLen-1].Bytes32())
+ t.lookupStorage(caller, slot)
+ case stackLen >= 1 && (op == fakevm.EXTCODECOPY || op == fakevm.EXTCODEHASH || op == fakevm.EXTCODESIZE || op == fakevm.BALANCE || op == fakevm.SELFDESTRUCT):
+ addr := common.Address(stackData[stackLen-1].Bytes20())
+ t.lookupAccount(addr)
+ if op == fakevm.SELFDESTRUCT {
+ t.deleted[caller] = true
+ }
+ case stackLen >= 5 && (op == fakevm.DELEGATECALL || op == fakevm.CALL || op == fakevm.STATICCALL || op == fakevm.CALLCODE):
+ addr := common.Address(stackData[stackLen-2].Bytes20())
+ t.lookupAccount(addr)
+ case op == fakevm.CREATE:
+ nonce := t.env.StateDB.GetNonce(caller)
+ addr := crypto.CreateAddress(caller, nonce)
+ t.lookupAccount(addr)
+ t.created[addr] = true
+ case stackLen >= 4 && op == fakevm.CREATE2:
+ offset := stackData[stackLen-2]
+ size := stackData[stackLen-3]
+ init := scope.Memory.GetCopy(int64(offset.Uint64()), int64(size.Uint64()))
+ inithash := crypto.Keccak256(init)
+ salt := stackData[stackLen-4]
+ addr := crypto.CreateAddress2(caller, salt.Bytes32(), inithash)
+ t.lookupAccount(addr)
+ t.created[addr] = true
+ }
+}
+
+func (t *prestateTracer) CaptureTxStart(gasLimit uint64) {
+ t.gasLimit = gasLimit
+}
+
+func (t *prestateTracer) CaptureTxEnd(restGas uint64) {
+ if !t.config.DiffMode {
+ return
+ }
+
+ for addr, state := range t.pre {
+ // The deleted account's state is pruned from `post` but kept in `pre`
+ if _, ok := t.deleted[addr]; ok {
+ continue
+ }
+ modified := false
+ postAccount := &account{Storage: make(map[common.Hash]common.Hash)}
+ newBalance := t.env.StateDB.GetBalance(addr)
+ newNonce := t.env.StateDB.GetNonce(addr)
+ newCode := t.env.StateDB.GetCode(addr)
+
+ if newBalance.Cmp(t.pre[addr].Balance) != 0 {
+ modified = true
+ postAccount.Balance = newBalance
+ }
+ if newNonce != t.pre[addr].Nonce {
+ modified = true
+ postAccount.Nonce = newNonce
+ }
+ if !bytes.Equal(newCode, t.pre[addr].Code) {
+ modified = true
+ postAccount.Code = newCode
+ }
+
+ for key, val := range state.Storage {
+ // don't include the empty slot
+ if val == (common.Hash{}) {
+ delete(t.pre[addr].Storage, key)
+ }
+
+ newVal := t.env.StateDB.GetState(addr, key)
+ if val == newVal {
+ // Omit unchanged slots
+ delete(t.pre[addr].Storage, key)
+ } else {
+ modified = true
+ if newVal != (common.Hash{}) {
+ postAccount.Storage[key] = newVal
+ }
+ }
+ }
+
+ if modified {
+ t.post[addr] = postAccount
+ } else {
+ // if state is not modified, then no need to include into the pre state
+ delete(t.pre, addr)
+ }
+ }
+ // the new created contracts' prestate were empty, so delete them
+ for a := range t.created {
+ // the created contract maybe exists in statedb before the creating tx
+ if s := t.pre[a]; s != nil && !s.exists() {
+ delete(t.pre, a)
+ }
+ }
+}
+
+// GetResult returns the json-encoded nested list of call traces, and any
+// error arising from the encoding or forceful termination (via `Stop`).
+func (t *prestateTracer) GetResult() (json.RawMessage, error) {
+ var res []byte
+ var err error
+ if t.config.DiffMode {
+ res, err = json.Marshal(struct {
+ Post state `json:"post"`
+ Pre state `json:"pre"`
+ }{t.post, t.pre})
+ } else {
+ res, err = json.Marshal(t.pre)
+ }
+ if err != nil {
+ return nil, err
+ }
+ return json.RawMessage(res), t.reason
+}
+
+// Stop terminates execution of the tracer at the first opportune moment.
+func (t *prestateTracer) Stop(err error) {
+ t.reason = err
+ atomic.StoreUint32(&t.interrupt, 1)
+}
+
+// lookupAccount fetches details of an account and adds it to the prestate
+// if it doesn't exist there.
+func (t *prestateTracer) lookupAccount(addr common.Address) {
+ if _, ok := t.pre[addr]; ok {
+ return
+ }
+
+ t.pre[addr] = &account{
+ Balance: t.env.StateDB.GetBalance(addr),
+ Nonce: t.env.StateDB.GetNonce(addr),
+ Code: t.env.StateDB.GetCode(addr),
+ Storage: make(map[common.Hash]common.Hash),
+ }
+}
+
+// lookupStorage fetches the requested storage slot and adds
+// it to the prestate of the given contract. It assumes `lookupAccount`
+// has been performed on the contract before.
+func (t *prestateTracer) lookupStorage(addr common.Address, key common.Hash) {
+ if _, ok := t.pre[addr].Storage[key]; ok {
+ return
+ }
+ t.pre[addr].Storage[key] = t.env.StateDB.GetState(addr, key)
+}
diff --git a/state/runtime/instrumentation/tracers/tracers.go b/state/runtime/instrumentation/tracers/tracers.go
index 3523834660..ec21f91e2c 100644
--- a/state/runtime/instrumentation/tracers/tracers.go
+++ b/state/runtime/instrumentation/tracers/tracers.go
@@ -19,7 +19,7 @@ package tracers
import (
"encoding/json"
- "errors"
+ "math/big"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/fakevm"
"github.com/ethereum/go-ethereum/common"
@@ -28,9 +28,10 @@ import (
// Context contains some contextual infos for a transaction execution that is not
// available from within the EVM object.
type Context struct {
- BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
- TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
- TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
+ BlockHash common.Hash // Hash of the block the tx is contained within (zero if dangling tx or call)
+ BlockNumber *big.Int // Number of the block the tx is contained within (zero if dangling tx or call)
+ TxIndex int // Index of the transaction within a block (zero if dangling tx or call)
+ TxHash common.Hash // Hash of the transaction being traced (zero if dangling call)
}
// Tracer interface extends vm.EVMLogger and additionally
@@ -42,31 +43,55 @@ type Tracer interface {
Stop(err error)
}
-type lookupFunc func(string, *Context) (Tracer, error)
+type ctorFn func(*Context, json.RawMessage) (Tracer, error)
+type jsCtorFn func(string, *Context, json.RawMessage) (Tracer, error)
-var (
- lookups []lookupFunc
-)
+type elem struct {
+ ctor ctorFn
+ isJS bool
+}
-// RegisterLookup registers a method as a lookup for tracers, meaning that
-// users can invoke a named tracer through that lookup. If 'wildcard' is true,
-// then the lookup will be placed last. This is typically meant for interpreted
-// engines (js) which can evaluate dynamic user-supplied code.
-func RegisterLookup(wildcard bool, lookup lookupFunc) {
- if wildcard {
- lookups = append(lookups, lookup)
- } else {
- lookups = append([]lookupFunc{lookup}, lookups...)
- }
+// DefaultDirectory is the collection of tracers bundled by default.
+var DefaultDirectory = directory{elems: make(map[string]elem)}
+
+// directory provides functionality to lookup a tracer by name
+// and a function to instantiate it. It falls back to a JS code evaluator
+// if no tracer of the given name exists.
+type directory struct {
+ elems map[string]elem
+ jsEval jsCtorFn
+}
+
+// Register registers a method as a lookup for tracers, meaning that
+// users can invoke a named tracer through that lookup.
+func (d *directory) Register(name string, f ctorFn, isJS bool) {
+ d.elems[name] = elem{ctor: f, isJS: isJS}
+}
+
+// RegisterJSEval registers a tracer that is able to parse
+// dynamic user-provided JS code.
+func (d *directory) RegisterJSEval(f jsCtorFn) {
+ d.jsEval = f
}
// New returns a new instance of a tracer, by iterating through the
-// registered lookups.
-func New(code string, ctx *Context) (Tracer, error) {
- for _, lookup := range lookups {
- if tracer, err := lookup(code, ctx); err == nil {
- return tracer, nil
- }
+// registered lookups. Name is either name of an existing tracer
+// or an arbitrary JS code.
+func (d *directory) New(name string, ctx *Context, cfg json.RawMessage) (Tracer, error) {
+ if elem, ok := d.elems[name]; ok {
+ return elem.ctor(ctx, cfg)
+ }
+ // Assume JS code
+ return d.jsEval(name, ctx, cfg)
+}
+
+// IsJS will return true if the given tracer will evaluate
+// JS code. Because code evaluation has high overhead, this
+// info will be used in determining fast and slow code paths.
+func (d *directory) IsJS(name string) bool {
+ if elem, ok := d.elems[name]; ok {
+ return elem.isJS
}
- return nil, errors.New("tracer not found")
+ // JS eval will execute JS code
+ return true
}
diff --git a/state/state.go b/state/state.go
index 8628b5b56e..59329f269f 100644
--- a/state/state.go
+++ b/state/state.go
@@ -24,10 +24,10 @@ import (
"github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/js"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers"
+ "github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation/tracers/native"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/trie"
"github.com/holiman/uint256"
@@ -36,10 +36,12 @@ import (
const (
// Size of the memory in bytes reserved by the zkEVM
- zkEVMReservedMemorySize int = 128
- two uint = 2
- cTrue = 1
- cFalse = 0
+ two uint = 2
+)
+
+const (
+ cTrue = 1
+ cFalse = 0
)
var (
@@ -849,8 +851,6 @@ func (s *State) GetLastBatch(ctx context.Context, dbTx pgx.Tx) (*Batch, error) {
// DebugTransaction re-executes a tx to generate its trace
func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Hash, traceConfig TraceConfig, dbTx pgx.Tx) (*runtime.ExecutionResult, error) {
- result := new(runtime.ExecutionResult)
-
// gets the transaction
tx, err := s.GetTransactionByHash(ctx, transactionHash, dbTx)
if err != nil {
@@ -918,9 +918,10 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
traceConfigRequest.EnableReturnData = cTrue
}
+ oldStateRoot := previousBlock.Root()
processBatchRequest := &pb.ProcessBatchRequest{
OldBatchNum: batch.BatchNumber - 1,
- OldStateRoot: previousBlock.Root().Bytes(),
+ OldStateRoot: oldStateRoot.Bytes(),
OldAccInputHash: previousBatch.AccInputHash.Bytes(),
BatchL2Data: batchL2Data,
@@ -936,6 +937,7 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
// Send Batch to the Executor
startTime := time.Now()
processBatchResponse, err := s.executorClient.ProcessBatch(ctx, processBatchRequest)
+ endTime := time.Now()
if err != nil {
return nil, err
} else if processBatchResponse.Error != executor.EXECUTOR_ERROR_NO_ERROR {
@@ -943,7 +945,6 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
s.eventLog.LogExecutorError(ctx, processBatchResponse.Error, processBatchRequest)
return nil, err
}
- endTime := time.Now()
// //save process batch response file
// b, err := json.Marshal(processBatchResponse)
@@ -977,25 +978,37 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
return nil, fmt.Errorf("tx hash not found in executor response")
}
- result.CreateAddress = response.CreateAddress
- result.GasLeft = response.GasLeft
- result.GasUsed = response.GasUsed
- result.ReturnValue = response.ReturnValue
- result.StateRoot = response.StateRoot.Bytes()
- result.StructLogs = response.ExecutionTrace
+ result := &runtime.ExecutionResult{
+ CreateAddress: response.CreateAddress,
+ GasLeft: response.GasLeft,
+ GasUsed: response.GasUsed,
+ ReturnValue: response.ReturnValue,
+ StateRoot: response.StateRoot.Bytes(),
+ StructLogs: response.ExecutionTrace,
+ ExecutorTrace: response.CallTrace,
+ }
- if traceConfig.Tracer == nil || *traceConfig.Tracer == "" {
+ // if is the default trace, return the result
+ if traceConfig.IsDefaultTracer() {
return result, nil
}
- // Parse the executor-like trace using the FakeEVM
- jsTracer, err := js.NewJsTracer(*traceConfig.Tracer, new(tracers.Context))
+ senderAddress, err := GetSender(*tx)
if err != nil {
- log.Errorf("debug transaction: failed to create jsTracer, err: %v", err)
- return nil, fmt.Errorf("failed to create jsTracer, err: %v", err)
+ return nil, err
}
- context := instrumentation.Context{}
+ context := instrumentation.Context{
+ From: senderAddress.String(),
+ Input: hex.EncodeToHex(tx.Data()),
+ Gas: strconv.FormatUint(tx.Gas(), encoding.Base10),
+ Value: tx.Value().String(),
+ Output: hex.EncodeToHex(result.ReturnValue),
+ GasPrice: tx.GasPrice().String(),
+ OldStateRoot: oldStateRoot.String(),
+ Time: uint64(endTime.Sub(startTime)),
+ GasUsed: strconv.FormatUint(result.GasUsed, encoding.Base10),
+ }
// Fill trace context
if tx.To() == nil {
@@ -1006,21 +1019,6 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
context.To = tx.To().Hex()
}
- senderAddress, err := GetSender(*tx)
- if err != nil {
- return nil, err
- }
-
- context.From = senderAddress.String()
- context.Input = "0x" + hex.EncodeToString(tx.Data())
- context.Gas = strconv.FormatUint(tx.Gas(), encoding.Base10)
- context.Value = tx.Value().String()
- context.Output = "0x" + hex.EncodeToString(result.ReturnValue)
- context.GasPrice = tx.GasPrice().String()
- context.OldStateRoot = batch.StateRoot.String()
- context.Time = uint64(endTime.Sub(startTime))
- context.GasUsed = strconv.FormatUint(result.GasUsed, encoding.Base10)
-
result.ExecutorTrace.Context = context
gasPrice, ok := new(big.Int).SetString(context.GasPrice, encoding.Base10)
@@ -1029,11 +1027,52 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
return nil, fmt.Errorf("failed to parse gasPrice")
}
- env := fakevm.NewFakeEVM(vm.BlockContext{BlockNumber: big.NewInt(1)}, vm.TxContext{GasPrice: gasPrice}, params.TestChainConfig, fakevm.Config{Debug: true, Tracer: jsTracer})
+ tracerContext := &tracers.Context{
+ BlockHash: receipt.BlockHash,
+ BlockNumber: receipt.BlockNumber,
+ TxIndex: int(receipt.TransactionIndex),
+ TxHash: transactionHash,
+ }
+
+ var evmTracer tracers.Tracer
+ if traceConfig.Is4ByteTracer() {
+ evmTracer, err = native.NewFourByteTracer(tracerContext, traceConfig.TracerConfig)
+ if err != nil {
+ log.Errorf("debug transaction: failed to create 4byteTracer, err: %v", err)
+ return nil, fmt.Errorf("failed to create 4byteTracer, err: %v", err)
+ }
+ } else if traceConfig.IsCallTracer() {
+ evmTracer, err = native.NewCallTracer(tracerContext, traceConfig.TracerConfig)
+ if err != nil {
+ log.Errorf("debug transaction: failed to create callTracer, err: %v", err)
+ return nil, fmt.Errorf("failed to create callTracer, err: %v", err)
+ }
+ } else if traceConfig.IsNoopTracer() {
+ evmTracer, err = native.NewNoopTracer(tracerContext, traceConfig.TracerConfig)
+ if err != nil {
+ log.Errorf("debug transaction: failed to create noopTracer, err: %v", err)
+ return nil, fmt.Errorf("failed to create noopTracer, err: %v", err)
+ }
+ } else if traceConfig.IsPrestateTracer() {
+ evmTracer, err = native.NewPrestateTracer(tracerContext, traceConfig.TracerConfig)
+ if err != nil {
+ log.Errorf("debug transaction: failed to create prestateTracer, err: %v", err)
+ return nil, fmt.Errorf("failed to create prestateTracer, err: %v", err)
+ }
+ } else if traceConfig.IsJSCustomTracer() {
+ evmTracer, err = js.NewJsTracer(*traceConfig.Tracer, tracerContext, traceConfig.TracerConfig)
+ if err != nil {
+ log.Errorf("debug transaction: failed to create jsTracer, err: %v", err)
+ return nil, fmt.Errorf("failed to create jsTracer, err: %v", err)
+ }
+ } else {
+ return nil, fmt.Errorf("invalid tracer: %v, err: %v", traceConfig.Tracer, err)
+ }
+
fakeDB := &FakeDB{State: s, stateRoot: batch.StateRoot.Bytes()}
- env.SetStateDB(fakeDB)
+ evm := fakevm.NewFakeEVM(fakevm.BlockContext{BlockNumber: big.NewInt(1)}, fakevm.TxContext{GasPrice: gasPrice}, fakeDB, params.TestChainConfig, fakevm.Config{Debug: true, Tracer: evmTracer})
- traceResult, err := s.ParseTheTraceUsingTheTracer(env, result.ExecutorTrace, jsTracer)
+ traceResult, err := s.ParseTheTraceUsingTheTracer(evm, result.ExecutorTrace, evmTracer)
if err != nil {
log.Errorf("debug transaction: failed parse the trace using the tracer: %v", err)
return nil, fmt.Errorf("failed parse the trace using the tracer: %v", err)
@@ -1045,8 +1084,9 @@ func (s *State) DebugTransaction(ctx context.Context, transactionHash common.Has
}
// ParseTheTraceUsingTheTracer parses the given trace with the given tracer.
-func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumentation.ExecutorTrace, jsTracer tracers.Tracer) (json.RawMessage, error) {
+func (s *State) ParseTheTraceUsingTheTracer(evm *fakevm.FakeEVM, trace instrumentation.ExecutorTrace, tracer tracers.Tracer) (json.RawMessage, error) {
var previousDepth int
+ var previousOp, previousGas *big.Int
var previousOpcode string
var stateRoot []byte
@@ -1061,11 +1101,8 @@ func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumen
return nil, ErrParsingExecutorTrace
}
- jsTracer.CaptureTxStart(contextGas.Uint64())
- jsTracer.CaptureStart(env, common.HexToAddress(trace.Context.From), common.HexToAddress(trace.Context.To), trace.Context.Type == "CREATE", common.Hex2Bytes(strings.TrimLeft(trace.Context.Input, "0x")), contextGas.Uint64(), value)
-
- stack := fakevm.Newstack()
- memory := fakevm.NewMemory()
+ tracer.CaptureTxStart(contextGas.Uint64())
+ tracer.CaptureStart(evm, common.HexToAddress(trace.Context.From), common.HexToAddress(trace.Context.To), trace.Context.Type == "CREATE", common.Hex2Bytes(strings.TrimLeft(trace.Context.Input, "0x")), contextGas.Uint64(), value)
bigStateRoot, ok := new(big.Int).SetString(trace.Context.OldStateRoot, 0)
if !ok {
@@ -1073,9 +1110,17 @@ func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumen
return nil, ErrParsingExecutorTrace
}
stateRoot = bigStateRoot.Bytes()
- env.StateDB.SetStateRoot(stateRoot)
+ evm.StateDB.SetStateRoot(stateRoot)
+
+ output := common.FromHex(trace.Context.Output)
+ var stepError error
for i, step := range trace.Steps {
+ stepErrorMsg := strings.TrimSpace(step.Error)
+ if stepErrorMsg != "" {
+ stepError = fmt.Errorf(stepErrorMsg)
+ }
+
gas, ok := new(big.Int).SetString(step.Gas, encoding.Base10)
if !ok {
log.Debugf("error while parsing step gas")
@@ -1088,20 +1133,36 @@ func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumen
return nil, ErrParsingExecutorTrace
}
- value, ok := new(big.Int).SetString(step.Contract.Value, encoding.Base10)
- if !ok {
- log.Debugf("error while parsing step value")
- return nil, ErrParsingExecutorTrace
- }
-
op, ok := new(big.Int).SetString(step.Op, 0)
if !ok {
log.Debugf("error while parsing step op")
return nil, ErrParsingExecutorTrace
}
+ // set Stack
+ stack := fakevm.NewStack()
+ for _, stackContent := range step.Stack {
+ valueBigInt, ok := new(big.Int).SetString(stackContent, hex.Base)
+ if !ok {
+ log.Debugf("error while parsing stack valueBigInt")
+ return nil, ErrParsingExecutorTrace
+ }
+ value, _ := uint256.FromBig(valueBigInt)
+ stack.Push(value)
+ }
+
+ // set Memory
+ memory := fakevm.NewMemory()
+ if len(step.Memory) > 0 {
+ memory.Resize(uint64(len(step.Memory)))
+ memory.Set(0, uint64(len(step.Memory)), step.Memory)
+ } else {
+ memory = fakevm.NewMemory()
+ }
+
+ value := hex.DecodeBig(step.Contract.Value)
scope := &fakevm.ScopeContext{
- Contract: vm.NewContract(fakevm.NewAccount(common.HexToAddress(step.Contract.Caller)), fakevm.NewAccount(common.HexToAddress(step.Contract.Address)), value, gas.Uint64()),
+ Contract: fakevm.NewContract(fakevm.NewAccount(common.HexToAddress(step.Contract.Caller)), fakevm.NewAccount(common.HexToAddress(step.Contract.Address)), value, gas.Uint64()),
Memory: memory,
Stack: stack,
}
@@ -1109,65 +1170,49 @@ func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumen
codeAddr := common.HexToAddress(step.Contract.Address)
scope.Contract.CodeAddr = &codeAddr
- opcode := vm.OpCode(op.Uint64()).String()
+ // when a revert is detected, we stop the execution
+ if step.OpCode == "REVERT" {
+ stepError = fakevm.ErrExecutionReverted
+ break
+ }
if previousOpcode == "CALL" && step.Pc != 0 {
- jsTracer.CaptureExit(common.Hex2Bytes(step.ReturnData), gasCost.Uint64(), fmt.Errorf(step.Error))
+ tracer.CaptureExit(step.ReturnData, gasCost.Uint64(), stepError)
}
- if opcode != "CALL" || trace.Steps[i+1].Pc == 0 {
- if step.Error != "" {
- err := fmt.Errorf(step.Error)
- jsTracer.CaptureFault(step.Pc, vm.OpCode(op.Uint64()), gas.Uint64(), gasCost.Uint64(), scope, step.Depth, err)
+ if step.OpCode != "CALL" || trace.Steps[i+1].Pc == 0 {
+ if stepError != nil {
+ tracer.CaptureFault(step.Pc, fakevm.OpCode(op.Uint64()), gas.Uint64(), gasCost.Uint64(), scope, step.Depth, stepError)
} else {
- jsTracer.CaptureState(step.Pc, vm.OpCode(op.Uint64()), gas.Uint64(), gasCost.Uint64(), scope, common.Hex2Bytes(strings.TrimLeft(step.ReturnData, "0x")), step.Depth, nil)
+ tracer.CaptureState(step.Pc, fakevm.OpCode(op.Uint64()), gas.Uint64(), gasCost.Uint64(), scope, step.ReturnData, step.Depth, nil)
}
}
- if opcode == "CREATE" || opcode == "CREATE2" || opcode == "CALL" || opcode == "CALLCODE" || opcode == "DELEGATECALL" || opcode == "STATICCALL" || opcode == "SELFDESTRUCT" {
- jsTracer.CaptureEnter(vm.OpCode(op.Uint64()), common.HexToAddress(step.Contract.Caller), common.HexToAddress(step.Contract.Address), common.Hex2Bytes(strings.TrimLeft(step.Contract.Input, "0x")), gas.Uint64(), value)
+ if step.OpCode == "CALL" || step.OpCode == "CALLCODE" || step.OpCode == "DELEGATECALL" || step.OpCode == "STATICCALL" || step.OpCode == "SELFDESTRUCT" {
+ tracer.CaptureEnter(fakevm.OpCode(op.Uint64()), common.HexToAddress(step.Contract.Caller), common.HexToAddress(step.Contract.Address), []byte(step.Contract.Input), gas.Uint64(), value)
if step.OpCode == "SELFDESTRUCT" {
- jsTracer.CaptureExit(common.Hex2Bytes(step.ReturnData), gasCost.Uint64(), fmt.Errorf(step.Error))
+ tracer.CaptureExit(step.ReturnData, gasCost.Uint64(), stepError)
}
}
- // Set Memory
- if len(step.Memory) > 0 {
- memory.Resize(uint64(fakevm.MemoryItemSize*len(step.Memory) + zkEVMReservedMemorySize))
- for offset, memoryContent := range step.Memory {
- memory.Set(uint64((offset*fakevm.MemoryItemSize)+zkEVMReservedMemorySize), uint64(fakevm.MemoryItemSize), common.Hex2Bytes(memoryContent))
- }
- } else {
- memory = fakevm.NewMemory()
- }
-
- // Set Stack
- stack = fakevm.Newstack()
- for _, stackContent := range step.Stack {
- valueBigInt, ok := new(big.Int).SetString(stackContent, 0)
- if !ok {
- log.Debugf("error while parsing stack valueBigInt")
- return nil, ErrParsingExecutorTrace
- }
- value, _ := uint256.FromBig(valueBigInt)
- stack.Push(value)
+ // when a create2 is detected, the next step contains the contract updated
+ if previousOpcode == "CREATE" || previousOpcode == "CREATE2" {
+ tracer.CaptureEnter(fakevm.OpCode(previousOp.Uint64()), common.HexToAddress(step.Contract.Caller), common.HexToAddress(step.Contract.Address), []byte(step.Contract.Input), previousGas.Uint64(), value)
}
- // Returning from a call or create
+ // returning from a call or create
if previousDepth > step.Depth {
- jsTracer.CaptureExit(common.Hex2Bytes(step.ReturnData), gasCost.Uint64(), fmt.Errorf(step.Error))
+ tracer.CaptureExit(step.ReturnData, gasCost.Uint64(), stepError)
}
- // Set StateRoot
- bigStateRoot, ok := new(big.Int).SetString(step.StateRoot, 0)
- if !ok {
- log.Debugf("error while parsing step stateRoot")
- return nil, ErrParsingExecutorTrace
- }
+ // set StateRoot
+ stateRoot = []byte(step.StateRoot)
+ evm.StateDB.SetStateRoot(stateRoot)
- stateRoot = bigStateRoot.Bytes()
- env.StateDB.SetStateRoot(stateRoot)
+ // set previous step values
previousDepth = step.Depth
+ previousOp = op
+ previousGas = gas
previousOpcode = step.OpCode
}
@@ -1177,10 +1222,11 @@ func (s *State) ParseTheTraceUsingTheTracer(env *fakevm.FakeEVM, trace instrumen
return nil, ErrParsingExecutorTrace
}
- jsTracer.CaptureTxEnd(gasUsed.Uint64())
- jsTracer.CaptureEnd(common.Hex2Bytes(trace.Context.Output), gasUsed.Uint64(), time.Duration(trace.Context.Time), nil)
+ restGas := contextGas.Uint64() - gasUsed.Uint64()
+ tracer.CaptureTxEnd(restGas)
+ tracer.CaptureEnd(output, gasUsed.Uint64(), stepError)
- return jsTracer.GetResult()
+ return tracer.GetResult()
}
// PreProcessTransaction processes the transaction in order to calculate its zkCounters before adding it to the pool
diff --git a/state/types.go b/state/types.go
index add0b713ed..2575a66336 100644
--- a/state/types.go
+++ b/state/types.go
@@ -1,8 +1,10 @@
package state
import (
+ "encoding/json"
"fmt"
"math/big"
+ "strings"
"github.com/0xPolygonHermez/zkevm-node/state/runtime/instrumentation"
"github.com/ethereum/go-ethereum/common"
@@ -145,6 +147,37 @@ type TraceConfig struct {
EnableMemory bool
EnableReturnData bool
Tracer *string
+ TracerConfig json.RawMessage
+}
+
+// IsDefaultTracer returns true when no custom tracer is set
+func (t *TraceConfig) IsDefaultTracer() bool {
+ return t.Tracer == nil || *t.Tracer == ""
+}
+
+// Is4ByteTracer returns true when should use 4byteTracer
+func (t *TraceConfig) Is4ByteTracer() bool {
+ return t.Tracer != nil && *t.Tracer == "4byteTracer"
+}
+
+// IsCallTracer returns true when should use callTracer
+func (t *TraceConfig) IsCallTracer() bool {
+ return t.Tracer != nil && *t.Tracer == "callTracer"
+}
+
+// IsNoopTracer returns true when should use noopTracer
+func (t *TraceConfig) IsNoopTracer() bool {
+ return t.Tracer != nil && *t.Tracer == "noopTracer"
+}
+
+// IsPrestateTracer returns true when should use prestateTracer
+func (t *TraceConfig) IsPrestateTracer() bool {
+ return t.Tracer != nil && *t.Tracer == "prestateTracer"
+}
+
+// IsJSCustomTracer returns true when should use js custom tracer
+func (t *TraceConfig) IsJSCustomTracer() bool {
+ return t.Tracer != nil && strings.Contains(*t.Tracer, "result") && strings.Contains(*t.Tracer, "fault")
}
// TrustedReorg represents a trusted reorg
diff --git a/test/config/debug.node.config.toml b/test/config/debug.node.config.toml
index 7fe30c4dd6..1f91238b66 100644
--- a/test/config/debug.node.config.toml
+++ b/test/config/debug.node.config.toml
@@ -147,7 +147,7 @@ ProfilingEnabled = false
User = "event_user"
Password = "event_password"
Name = "event_db"
- Host = "zkevm-event-db"
- Port = "5432"
+ Host = "localhost"
+ Port = "5435"
EnableLog = false
MaxConns = 200
diff --git a/test/contracts/auto/Creates.sol b/test/contracts/auto/Creates.sol
new file mode 100644
index 0000000000..cdd7c517ba
--- /dev/null
+++ b/test/contracts/auto/Creates.sol
@@ -0,0 +1,67 @@
+// SPDX-License-Identifier: GPL-3.0
+
+pragma solidity >=0.7.0 <0.9.0;
+
+contract Creates {
+ function opCreate(bytes memory bytecode, uint length) public returns(address) {
+ address addr;
+ assembly {
+ addr := create(0, 0xa0, length)
+ sstore(0x0, addr)
+ }
+ return addr;
+ }
+
+ function opCreate2(bytes memory bytecode, uint length) public returns(address) {
+ address addr;
+ assembly {
+ addr := create2(0, 0xa0, length, 0x2)
+ sstore(0x0, addr)
+ }
+ return addr;
+ }
+
+ function opCreate2Complex(bytes memory bytecode, uint length) public returns(address, uint256) {
+ uint256 number = add(1, 2);
+
+ address addr;
+ assembly {
+ addr := create2(0, add(bytecode, 0x20), length, 0x2)
+ sstore(0x0, addr)
+ }
+
+ number = add(2, 4);
+
+ return (addr, number);
+ }
+
+ function add(uint256 a, uint256 b) public pure returns(uint256) {
+ return a + b;
+ }
+
+ function sendValue() public payable {
+ uint bal;
+ assembly{
+ bal := add(bal,callvalue())
+ sstore(0x1, bal)
+ }
+ }
+
+ function opCreateValue(bytes memory bytecode, uint length) public payable returns(address) {
+ address addr;
+ assembly {
+ addr := create(500, 0xa0, length)
+ sstore(0x0, addr)
+ }
+ return addr;
+ }
+
+ function opCreate2Value(bytes memory bytecode, uint length) public payable returns(address) {
+ address addr;
+ assembly {
+ addr := create2(300, 0xa0, length, 0x55555)
+ sstore(0x0, addr)
+ }
+ return addr;
+ }
+}
\ No newline at end of file
diff --git a/test/contracts/bin/Creates/Creates.go b/test/contracts/bin/Creates/Creates.go
new file mode 100644
index 0000000000..af2bf2e773
--- /dev/null
+++ b/test/contracts/bin/Creates/Creates.go
@@ -0,0 +1,360 @@
+// Code generated - DO NOT EDIT.
+// This file is a generated binding and any manual changes will be lost.
+
+package Creates
+
+import (
+ "errors"
+ "math/big"
+ "strings"
+
+ ethereum "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/accounts/abi"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/event"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var (
+ _ = errors.New
+ _ = big.NewInt
+ _ = strings.NewReader
+ _ = ethereum.NotFound
+ _ = bind.Bind
+ _ = common.Big1
+ _ = types.BloomLookup
+ _ = event.NewSubscription
+ _ = abi.ConvertType
+)
+
+// CreatesMetaData contains all meta data concerning the Creates contract.
+var CreatesMetaData = &bind.MetaData{
+ ABI: "[{\"inputs\":[{\"internalType\":\"uint256\",\"name\":\"a\",\"type\":\"uint256\"},{\"internalType\":\"uint256\",\"name\":\"b\",\"type\":\"uint256\"}],\"name\":\"add\",\"outputs\":[{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"pure\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"opCreate\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"opCreate2\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"opCreate2Complex\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"},{\"internalType\":\"uint256\",\"name\":\"\",\"type\":\"uint256\"}],\"stateMutability\":\"nonpayable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"opCreate2Value\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[{\"internalType\":\"bytes\",\"name\":\"bytecode\",\"type\":\"bytes\"},{\"internalType\":\"uint256\",\"name\":\"length\",\"type\":\"uint256\"}],\"name\":\"opCreateValue\",\"outputs\":[{\"internalType\":\"address\",\"name\":\"\",\"type\":\"address\"}],\"stateMutability\":\"payable\",\"type\":\"function\"},{\"inputs\":[],\"name\":\"sendValue\",\"outputs\":[],\"stateMutability\":\"payable\",\"type\":\"function\"}]",
+ Bin: "0x608060405234801561001057600080fd5b50610369806100206000396000f3fe6080604052600436106100705760003560e01c8063771602f71161004e578063771602f7146100d0578063b88c4aa9146100fe578063c935aee414610111578063e3306a251461015057600080fd5b806327c845dc146100755780633c77eba3146100805780635b8e9959146100b0575b600080fd5b61007e34600155565b005b61009361008e366004610236565b610170565b6040516001600160a01b0390911681526020015b60405180910390f35b3480156100bc57600080fd5b506100936100cb366004610236565b610187565b3480156100dc57600080fd5b506100f06100eb3660046102eb565b61019d565b6040519081526020016100a7565b61009361010c366004610236565b6101b0565b34801561011d57600080fd5b5061013161012c366004610236565b6101cb565b604080516001600160a01b0390931683526020830191909152016100a7565b34801561015c57600080fd5b5061009361016b366004610236565b610208565b6000808260a06101f4f06000819055949350505050565b6000808260a06000f06000819055949350505050565b60006101a9828461030d565b9392505050565b600080620555558360a061012cf56000819055949350505050565b60008060006101dc6001600261019d565b90506000600285602088016000f59050806000556101fc6002600461019d565b90969095509350505050565b60008060028360a06000f56000819055949350505050565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561024957600080fd5b823567ffffffffffffffff8082111561026157600080fd5b818501915085601f83011261027557600080fd5b81358181111561028757610287610220565b604051601f8201601f19908116603f011681019083821181831017156102af576102af610220565b816040528281528860208487010111156102c857600080fd5b826020860160208301376000602093820184015298969091013596505050505050565b600080604083850312156102fe57600080fd5b50508035926020909101359150565b6000821982111561032e57634e487b7160e01b600052601160045260246000fd5b50019056fea2646970667358221220cdc0e0bdc2487139b3aa0666f32d3f0ed1e40a81659b28e6dea427224cc6104f64736f6c634300080c0033",
+}
+
+// CreatesABI is the input ABI used to generate the binding from.
+// Deprecated: Use CreatesMetaData.ABI instead.
+var CreatesABI = CreatesMetaData.ABI
+
+// CreatesBin is the compiled bytecode used for deploying new contracts.
+// Deprecated: Use CreatesMetaData.Bin instead.
+var CreatesBin = CreatesMetaData.Bin
+
+// DeployCreates deploys a new Ethereum contract, binding an instance of Creates to it.
+func DeployCreates(auth *bind.TransactOpts, backend bind.ContractBackend) (common.Address, *types.Transaction, *Creates, error) {
+ parsed, err := CreatesMetaData.GetAbi()
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ if parsed == nil {
+ return common.Address{}, nil, nil, errors.New("GetABI returned nil")
+ }
+
+ address, tx, contract, err := bind.DeployContract(auth, *parsed, common.FromHex(CreatesBin), backend)
+ if err != nil {
+ return common.Address{}, nil, nil, err
+ }
+ return address, tx, &Creates{CreatesCaller: CreatesCaller{contract: contract}, CreatesTransactor: CreatesTransactor{contract: contract}, CreatesFilterer: CreatesFilterer{contract: contract}}, nil
+}
+
+// Creates is an auto generated Go binding around an Ethereum contract.
+type Creates struct {
+ CreatesCaller // Read-only binding to the contract
+ CreatesTransactor // Write-only binding to the contract
+ CreatesFilterer // Log filterer for contract events
+}
+
+// CreatesCaller is an auto generated read-only Go binding around an Ethereum contract.
+type CreatesCaller struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// CreatesTransactor is an auto generated write-only Go binding around an Ethereum contract.
+type CreatesTransactor struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// CreatesFilterer is an auto generated log filtering Go binding around an Ethereum contract events.
+type CreatesFilterer struct {
+ contract *bind.BoundContract // Generic contract wrapper for the low level calls
+}
+
+// CreatesSession is an auto generated Go binding around an Ethereum contract,
+// with pre-set call and transact options.
+type CreatesSession struct {
+ Contract *Creates // Generic contract binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+}
+
+// CreatesCallerSession is an auto generated read-only Go binding around an Ethereum contract,
+// with pre-set call options.
+type CreatesCallerSession struct {
+ Contract *CreatesCaller // Generic contract caller binding to set the session for
+ CallOpts bind.CallOpts // Call options to use throughout this session
+}
+
+// CreatesTransactorSession is an auto generated write-only Go binding around an Ethereum contract,
+// with pre-set transact options.
+type CreatesTransactorSession struct {
+ Contract *CreatesTransactor // Generic contract transactor binding to set the session for
+ TransactOpts bind.TransactOpts // Transaction auth options to use throughout this session
+}
+
+// CreatesRaw is an auto generated low-level Go binding around an Ethereum contract.
+type CreatesRaw struct {
+ Contract *Creates // Generic contract binding to access the raw methods on
+}
+
+// CreatesCallerRaw is an auto generated low-level read-only Go binding around an Ethereum contract.
+type CreatesCallerRaw struct {
+ Contract *CreatesCaller // Generic read-only contract binding to access the raw methods on
+}
+
+// CreatesTransactorRaw is an auto generated low-level write-only Go binding around an Ethereum contract.
+type CreatesTransactorRaw struct {
+ Contract *CreatesTransactor // Generic write-only contract binding to access the raw methods on
+}
+
+// NewCreates creates a new instance of Creates, bound to a specific deployed contract.
+func NewCreates(address common.Address, backend bind.ContractBackend) (*Creates, error) {
+ contract, err := bindCreates(address, backend, backend, backend)
+ if err != nil {
+ return nil, err
+ }
+ return &Creates{CreatesCaller: CreatesCaller{contract: contract}, CreatesTransactor: CreatesTransactor{contract: contract}, CreatesFilterer: CreatesFilterer{contract: contract}}, nil
+}
+
+// NewCreatesCaller creates a new read-only instance of Creates, bound to a specific deployed contract.
+func NewCreatesCaller(address common.Address, caller bind.ContractCaller) (*CreatesCaller, error) {
+ contract, err := bindCreates(address, caller, nil, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &CreatesCaller{contract: contract}, nil
+}
+
+// NewCreatesTransactor creates a new write-only instance of Creates, bound to a specific deployed contract.
+func NewCreatesTransactor(address common.Address, transactor bind.ContractTransactor) (*CreatesTransactor, error) {
+ contract, err := bindCreates(address, nil, transactor, nil)
+ if err != nil {
+ return nil, err
+ }
+ return &CreatesTransactor{contract: contract}, nil
+}
+
+// NewCreatesFilterer creates a new log filterer instance of Creates, bound to a specific deployed contract.
+func NewCreatesFilterer(address common.Address, filterer bind.ContractFilterer) (*CreatesFilterer, error) {
+ contract, err := bindCreates(address, nil, nil, filterer)
+ if err != nil {
+ return nil, err
+ }
+ return &CreatesFilterer{contract: contract}, nil
+}
+
+// bindCreates binds a generic wrapper to an already deployed contract.
+func bindCreates(address common.Address, caller bind.ContractCaller, transactor bind.ContractTransactor, filterer bind.ContractFilterer) (*bind.BoundContract, error) {
+ parsed, err := CreatesMetaData.GetAbi()
+ if err != nil {
+ return nil, err
+ }
+ return bind.NewBoundContract(address, *parsed, caller, transactor, filterer), nil
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (_Creates *CreatesRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _Creates.Contract.CreatesCaller.contract.Call(opts, result, method, params...)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (_Creates *CreatesRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _Creates.Contract.CreatesTransactor.contract.Transfer(opts)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (_Creates *CreatesRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _Creates.Contract.CreatesTransactor.contract.Transact(opts, method, params...)
+}
+
+// Call invokes the (constant) contract method with params as input values and
+// sets the output to result. The result type might be a single field for simple
+// returns, a slice of interfaces for anonymous returns and a struct for named
+// returns.
+func (_Creates *CreatesCallerRaw) Call(opts *bind.CallOpts, result *[]interface{}, method string, params ...interface{}) error {
+ return _Creates.Contract.contract.Call(opts, result, method, params...)
+}
+
+// Transfer initiates a plain transaction to move funds to the contract, calling
+// its default method if one is available.
+func (_Creates *CreatesTransactorRaw) Transfer(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _Creates.Contract.contract.Transfer(opts)
+}
+
+// Transact invokes the (paid) contract method with params as input values.
+func (_Creates *CreatesTransactorRaw) Transact(opts *bind.TransactOpts, method string, params ...interface{}) (*types.Transaction, error) {
+ return _Creates.Contract.contract.Transact(opts, method, params...)
+}
+
+// Add is a free data retrieval call binding the contract method 0x771602f7.
+//
+// Solidity: function add(uint256 a, uint256 b) pure returns(uint256)
+func (_Creates *CreatesCaller) Add(opts *bind.CallOpts, a *big.Int, b *big.Int) (*big.Int, error) {
+ var out []interface{}
+ err := _Creates.contract.Call(opts, &out, "add", a, b)
+
+ if err != nil {
+ return *new(*big.Int), err
+ }
+
+ out0 := *abi.ConvertType(out[0], new(*big.Int)).(**big.Int)
+
+ return out0, err
+
+}
+
+// Add is a free data retrieval call binding the contract method 0x771602f7.
+//
+// Solidity: function add(uint256 a, uint256 b) pure returns(uint256)
+func (_Creates *CreatesSession) Add(a *big.Int, b *big.Int) (*big.Int, error) {
+ return _Creates.Contract.Add(&_Creates.CallOpts, a, b)
+}
+
+// Add is a free data retrieval call binding the contract method 0x771602f7.
+//
+// Solidity: function add(uint256 a, uint256 b) pure returns(uint256)
+func (_Creates *CreatesCallerSession) Add(a *big.Int, b *big.Int) (*big.Int, error) {
+ return _Creates.Contract.Add(&_Creates.CallOpts, a, b)
+}
+
+// OpCreate is a paid mutator transaction binding the contract method 0x5b8e9959.
+//
+// Solidity: function opCreate(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesTransactor) OpCreate(opts *bind.TransactOpts, bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "opCreate", bytecode, length)
+}
+
+// OpCreate is a paid mutator transaction binding the contract method 0x5b8e9959.
+//
+// Solidity: function opCreate(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesSession) OpCreate(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate is a paid mutator transaction binding the contract method 0x5b8e9959.
+//
+// Solidity: function opCreate(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesTransactorSession) OpCreate(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2 is a paid mutator transaction binding the contract method 0xe3306a25.
+//
+// Solidity: function opCreate2(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesTransactor) OpCreate2(opts *bind.TransactOpts, bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "opCreate2", bytecode, length)
+}
+
+// OpCreate2 is a paid mutator transaction binding the contract method 0xe3306a25.
+//
+// Solidity: function opCreate2(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesSession) OpCreate2(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2 is a paid mutator transaction binding the contract method 0xe3306a25.
+//
+// Solidity: function opCreate2(bytes bytecode, uint256 length) returns(address)
+func (_Creates *CreatesTransactorSession) OpCreate2(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2Complex is a paid mutator transaction binding the contract method 0xc935aee4.
+//
+// Solidity: function opCreate2Complex(bytes bytecode, uint256 length) returns(address, uint256)
+func (_Creates *CreatesTransactor) OpCreate2Complex(opts *bind.TransactOpts, bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "opCreate2Complex", bytecode, length)
+}
+
+// OpCreate2Complex is a paid mutator transaction binding the contract method 0xc935aee4.
+//
+// Solidity: function opCreate2Complex(bytes bytecode, uint256 length) returns(address, uint256)
+func (_Creates *CreatesSession) OpCreate2Complex(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2Complex(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2Complex is a paid mutator transaction binding the contract method 0xc935aee4.
+//
+// Solidity: function opCreate2Complex(bytes bytecode, uint256 length) returns(address, uint256)
+func (_Creates *CreatesTransactorSession) OpCreate2Complex(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2Complex(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2Value is a paid mutator transaction binding the contract method 0xb88c4aa9.
+//
+// Solidity: function opCreate2Value(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesTransactor) OpCreate2Value(opts *bind.TransactOpts, bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "opCreate2Value", bytecode, length)
+}
+
+// OpCreate2Value is a paid mutator transaction binding the contract method 0xb88c4aa9.
+//
+// Solidity: function opCreate2Value(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesSession) OpCreate2Value(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2Value(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreate2Value is a paid mutator transaction binding the contract method 0xb88c4aa9.
+//
+// Solidity: function opCreate2Value(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesTransactorSession) OpCreate2Value(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreate2Value(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreateValue is a paid mutator transaction binding the contract method 0x3c77eba3.
+//
+// Solidity: function opCreateValue(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesTransactor) OpCreateValue(opts *bind.TransactOpts, bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "opCreateValue", bytecode, length)
+}
+
+// OpCreateValue is a paid mutator transaction binding the contract method 0x3c77eba3.
+//
+// Solidity: function opCreateValue(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesSession) OpCreateValue(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreateValue(&_Creates.TransactOpts, bytecode, length)
+}
+
+// OpCreateValue is a paid mutator transaction binding the contract method 0x3c77eba3.
+//
+// Solidity: function opCreateValue(bytes bytecode, uint256 length) payable returns(address)
+func (_Creates *CreatesTransactorSession) OpCreateValue(bytecode []byte, length *big.Int) (*types.Transaction, error) {
+ return _Creates.Contract.OpCreateValue(&_Creates.TransactOpts, bytecode, length)
+}
+
+// SendValue is a paid mutator transaction binding the contract method 0x27c845dc.
+//
+// Solidity: function sendValue() payable returns()
+func (_Creates *CreatesTransactor) SendValue(opts *bind.TransactOpts) (*types.Transaction, error) {
+ return _Creates.contract.Transact(opts, "sendValue")
+}
+
+// SendValue is a paid mutator transaction binding the contract method 0x27c845dc.
+//
+// Solidity: function sendValue() payable returns()
+func (_Creates *CreatesSession) SendValue() (*types.Transaction, error) {
+ return _Creates.Contract.SendValue(&_Creates.TransactOpts)
+}
+
+// SendValue is a paid mutator transaction binding the contract method 0x27c845dc.
+//
+// Solidity: function sendValue() payable returns()
+func (_Creates *CreatesTransactorSession) SendValue() (*types.Transaction, error) {
+ return _Creates.Contract.SendValue(&_Creates.TransactOpts)
+}
diff --git a/test/docker-compose.yml b/test/docker-compose.yml
index a1f3676dd4..1d4f6d9e82 100644
--- a/test/docker-compose.yml
+++ b/test/docker-compose.yml
@@ -321,7 +321,7 @@ services:
zkevm-prover:
container_name: zkevm-prover
- image: hermeznetwork/zkevm-prover:c514cd1
+ image: hermeznetwork/zkevm-prover:4e3272b
ports:
# - 50051:50051 # Prover
- 50052:50052 # Mock prover
@@ -407,7 +407,7 @@ services:
zkevm-permissionless-prover:
container_name: zkevm-permissionless-prover
- image: hermeznetwork/zkevm-prover:c514cd1
+ image: hermeznetwork/zkevm-prover:4e3272b
ports:
# - 50058:50058 # Prover
- 50059:50052 # Mock prover
diff --git a/test/e2e/debug_test.go b/test/e2e/debug_test.go
index 0e9cb9c8f7..18d99c2f11 100644
--- a/test/e2e/debug_test.go
+++ b/test/e2e/debug_test.go
@@ -12,6 +12,8 @@ import (
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/client"
"github.com/0xPolygonHermez/zkevm-node/jsonrpc/types"
"github.com/0xPolygonHermez/zkevm-node/log"
+ "github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/Counter"
+ "github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/Creates"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/ERC20"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/EmitLog"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/Revert2"
@@ -21,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/accounts/abi/bind"
"github.com/ethereum/go-ethereum/common"
ethTypes "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethclient"
"github.com/stretchr/testify/require"
)
@@ -285,19 +288,69 @@ func TestDebugTraceTransaction(t *testing.T) {
{name: "sc deployment", createSignedTx: createScDeploySignedTx},
{name: "sc call", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
{name: "erc20 transfer", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
+ {name: "create", prepare: prepareCreate, createSignedTx: createCreateSignedTx},
+ {name: "create2", prepare: prepareCreate, createSignedTx: createCreate2SignedTx},
// failed transactions
{name: "sc deployment reverted", createSignedTx: createScDeployRevertedSignedTx},
{name: "sc call reverted", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
{name: "erc20 transfer reverted", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
}
+ privateKey, err := crypto.GenerateKey()
+ require.NoError(t, err)
+ for _, network := range networks {
+ auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
+ require.NoError(t, err)
+
+ ethereumClient := operations.MustGetClient(network.URL)
+ sourceAuth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
+
+ nonce, err := ethereumClient.NonceAt(ctx, sourceAuth.From, nil)
+ require.NoError(t, err)
+
+ balance, err := ethereumClient.BalanceAt(ctx, sourceAuth.From, nil)
+ require.NoError(t, err)
+
+ gasPrice, err := ethereumClient.SuggestGasPrice(ctx)
+ require.NoError(t, err)
+
+ value := big.NewInt(0).Quo(balance, big.NewInt(2))
+
+ gas, err := ethereumClient.EstimateGas(ctx, ethereum.CallMsg{
+ From: sourceAuth.From,
+ To: &auth.From,
+ GasPrice: gasPrice,
+ Value: value,
+ })
+ require.NoError(t, err)
+
+ tx := ethTypes.NewTx(ðTypes.LegacyTx{
+ To: &auth.From,
+ Nonce: nonce,
+ GasPrice: gasPrice,
+ Value: value,
+ Gas: gas,
+ })
+
+ signedTx, err := sourceAuth.Signer(sourceAuth.From, tx)
+ require.NoError(t, err)
+
+ err = ethereumClient.SendTransaction(ctx, signedTx)
+ require.NoError(t, err)
+
+ err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
+ require.NoError(t, err)
+ }
+
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
- log.Debugf(tc.name)
+ log.Debug("************************ ", tc.name, " ************************")
+
for _, network := range networks {
- log.Debugf(network.Name)
+ log.Debug("------------------------ ", network.Name, " ------------------------")
ethereumClient := operations.MustGetClient(network.URL)
- auth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
+ auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
+ require.NoError(t, err)
var customData map[string]interface{}
if tc.prepare != nil {
@@ -366,6 +419,8 @@ func TestDebugTraceTransaction(t *testing.T) {
err = json.Unmarshal(result, &resultMap)
require.NoError(t, err)
+ require.Equal(t, referenceValueMap["failed"], resultMap["failed"], fmt.Sprintf("invalid `failed` for network %s", networkName))
+
resultStructLogsMap := resultMap["structLogs"].([]interface{})
require.Equal(t, len(referenceStructLogsMap), len(resultStructLogsMap))
@@ -417,6 +472,251 @@ func TestDebugTraceTransaction(t *testing.T) {
}
}
+func TestDebugTraceTransactionCallTracer(t *testing.T) {
+ if testing.Short() {
+ t.Skip()
+ }
+
+ const l2NetworkURL = "http://localhost:8124"
+ const l2ExplorerRPCComponentName = "l2-explorer-json-rpc"
+
+ var err error
+ err = operations.Teardown()
+ require.NoError(t, err)
+
+ defer func() {
+ require.NoError(t, operations.Teardown())
+ require.NoError(t, operations.StopComponent(l2ExplorerRPCComponentName))
+ }()
+
+ ctx := context.Background()
+ opsCfg := operations.GetDefaultOperationsConfig()
+ opsMan, err := operations.NewManager(ctx, opsCfg)
+ require.NoError(t, err)
+ err = opsMan.Setup()
+ require.NoError(t, err)
+
+ err = operations.StartComponent(l2ExplorerRPCComponentName, func() (bool, error) { return operations.NodeUpCondition(l2NetworkURL) })
+ require.NoError(t, err)
+
+ const l1NetworkName, l2NetworkName = "Local L1", "Local L2"
+
+ networks := []struct {
+ Name string
+ URL string
+ WebSocketURL string
+ ChainID uint64
+ PrivateKey string
+ }{
+ {
+ Name: l1NetworkName,
+ URL: operations.DefaultL1NetworkURL,
+ ChainID: operations.DefaultL1ChainID,
+ PrivateKey: operations.DefaultSequencerPrivateKey,
+ },
+ {
+ Name: l2NetworkName,
+ URL: l2NetworkURL,
+ ChainID: operations.DefaultL2ChainID,
+ PrivateKey: operations.DefaultSequencerPrivateKey,
+ },
+ }
+
+ results := map[string]json.RawMessage{}
+
+ type testCase struct {
+ name string
+ prepare func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error)
+ createSignedTx func(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error)
+ }
+ testCases := []testCase{
+ // successful transactions
+ {name: "eth transfer", createSignedTx: createEthTransferSignedTx},
+ {name: "sc deployment", createSignedTx: createScDeploySignedTx},
+ {name: "sc call", prepare: prepareScCall, createSignedTx: createScCallSignedTx},
+ {name: "erc20 transfer", prepare: prepareERC20Transfer, createSignedTx: createERC20TransferSignedTx},
+ {name: "create", prepare: prepareCreate, createSignedTx: createCreateSignedTx},
+ {name: "create2", prepare: prepareCreate, createSignedTx: createCreate2SignedTx},
+ // failed transactions
+ {name: "sc deployment reverted", createSignedTx: createScDeployRevertedSignedTx},
+ {name: "sc call reverted", prepare: prepareScCallReverted, createSignedTx: createScCallRevertedSignedTx},
+ {name: "erc20 transfer reverted", prepare: prepareERC20TransferReverted, createSignedTx: createERC20TransferRevertedSignedTx},
+ }
+ privateKey, err := crypto.GenerateKey()
+ require.NoError(t, err)
+ for _, network := range networks {
+ auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
+ require.NoError(t, err)
+
+ ethereumClient := operations.MustGetClient(network.URL)
+ sourceAuth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
+
+ nonce, err := ethereumClient.NonceAt(ctx, sourceAuth.From, nil)
+ require.NoError(t, err)
+
+ balance, err := ethereumClient.BalanceAt(ctx, sourceAuth.From, nil)
+ require.NoError(t, err)
+
+ gasPrice, err := ethereumClient.SuggestGasPrice(ctx)
+ require.NoError(t, err)
+
+ value := big.NewInt(0).Quo(balance, big.NewInt(2))
+
+ gas, err := ethereumClient.EstimateGas(ctx, ethereum.CallMsg{
+ From: sourceAuth.From,
+ To: &auth.From,
+ GasPrice: gasPrice,
+ Value: value,
+ })
+ require.NoError(t, err)
+
+ tx := ethTypes.NewTx(ðTypes.LegacyTx{
+ To: &auth.From,
+ Nonce: nonce,
+ GasPrice: gasPrice,
+ Value: value,
+ Gas: gas,
+ })
+
+ signedTx, err := sourceAuth.Signer(sourceAuth.From, tx)
+ require.NoError(t, err)
+
+ err = ethereumClient.SendTransaction(ctx, signedTx)
+ require.NoError(t, err)
+
+ err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
+ require.NoError(t, err)
+ }
+
+ for _, tc := range testCases {
+ t.Run(tc.name, func(t *testing.T) {
+ log.Debug("************************ ", tc.name, " ************************")
+
+ for _, network := range networks {
+ log.Debug("------------------------ ", network.Name, " ------------------------")
+ ethereumClient := operations.MustGetClient(network.URL)
+ auth, err := bind.NewKeyedTransactorWithChainID(privateKey, big.NewInt(0).SetUint64(network.ChainID))
+ require.NoError(t, err)
+
+ var customData map[string]interface{}
+ if tc.prepare != nil {
+ customData, err = tc.prepare(t, ctx, auth, ethereumClient)
+ require.NoError(t, err)
+ }
+
+ signedTx, err := tc.createSignedTx(t, ctx, auth, ethereumClient, customData)
+ require.NoError(t, err)
+
+ err = ethereumClient.SendTransaction(ctx, signedTx)
+ require.NoError(t, err)
+
+ log.Debugf("tx sent: %v", signedTx.Hash().String())
+
+ err = operations.WaitTxToBeMined(ctx, ethereumClient, signedTx, operations.DefaultTimeoutTxToBeMined)
+ if err != nil && !strings.HasPrefix(err.Error(), "transaction has failed, reason:") {
+ require.NoError(t, err)
+ }
+
+ debugOptions := map[string]interface{}{
+ "disableStorage": false,
+ "disableStack": false,
+ "enableMemory": true,
+ "enableReturnData": true,
+ "tracer": "callTracer",
+ "tracerConfig": map[string]interface{}{
+ "onlyTopCall": false,
+ "withLog": true,
+ },
+ }
+
+ response, err := client.JSONRPCCall(network.URL, "debug_traceTransaction", signedTx.Hash().String(), debugOptions)
+ require.NoError(t, err)
+ require.Nil(t, response.Error)
+ require.NotNil(t, response.Result)
+
+ results[network.Name] = response.Result
+ log.Debug(string(response.Result))
+
+ // save result in a file
+ // sanitizedNetworkName := strings.ReplaceAll(network.Name+"_"+tc.name, " ", "_")
+ // filePath := fmt.Sprintf("/home/tclemos/github.com/0xPolygonHermez/zkevm-node/dist/%v.json", sanitizedNetworkName)
+ // b, _ := signedTx.MarshalBinary()
+ // fileContent := struct {
+ // Tx *ethTypes.Transaction
+ // RLP string
+ // Trace json.RawMessage
+ // }{
+ // Tx: signedTx,
+ // RLP: hex.EncodeToHex(b),
+ // Trace: response.Result,
+ // }
+ // c, err := json.MarshalIndent(fileContent, "", " ")
+ // require.NoError(t, err)
+ // err = os.WriteFile(filePath, c, 0644)
+ // require.NoError(t, err)
+ }
+
+ referenceValueMap := map[string]interface{}{}
+ err = json.Unmarshal(results[l1NetworkName], &referenceValueMap)
+ require.NoError(t, err)
+
+ for networkName, result := range results {
+ if networkName == l1NetworkName {
+ continue
+ }
+
+ resultMap := map[string]interface{}{}
+ err = json.Unmarshal(result, &resultMap)
+ require.NoError(t, err)
+
+ compareCallFrame(t, referenceValueMap, resultMap, networkName)
+ }
+ })
+ }
+}
+
+func compareCallFrame(t *testing.T, referenceValueMap, resultMap map[string]interface{}, networkName string) {
+ require.Equal(t, referenceValueMap["from"], resultMap["from"], fmt.Sprintf("invalid `from` for network %s", networkName))
+ require.Equal(t, referenceValueMap["input"], resultMap["input"], fmt.Sprintf("invalid `input` for network %s", networkName))
+ require.Equal(t, referenceValueMap["output"], resultMap["output"], fmt.Sprintf("invalid `output` for network %s", networkName))
+ require.Equal(t, referenceValueMap["value"], resultMap["value"], fmt.Sprintf("invalid `value` for network %s", networkName))
+ require.Equal(t, referenceValueMap["type"], resultMap["type"], fmt.Sprintf("invalid `type` for network %s", networkName))
+ require.Equal(t, referenceValueMap["error"], resultMap["error"], fmt.Sprintf("invalid `error` for network %s", networkName))
+ require.Equal(t, referenceValueMap["revertReason"], resultMap["revertReason"], fmt.Sprintf("invalid `revertReason` for network %s", networkName))
+
+ referenceLogs, found := referenceValueMap["logs"].([]interface{})
+ if found {
+ resultLogs := resultMap["logs"].([]interface{})
+ require.Equal(t, len(referenceLogs), len(resultLogs), "logs size doesn't match")
+ for logIndex := range referenceLogs {
+ referenceLog := referenceLogs[logIndex].(map[string]interface{})
+ resultLog := resultLogs[logIndex].(map[string]interface{})
+
+ require.Equal(t, referenceLog["data"], resultLog["data"], fmt.Sprintf("log index %v data doesn't match", logIndex))
+ referenceTopics, found := referenceLog["topics"].([]interface{})
+ if found {
+ resultTopics := resultLog["topics"].([]interface{})
+ require.Equal(t, len(referenceTopics), len(resultTopics), "log index %v topics size doesn't match", logIndex)
+ for topicIndex := range referenceTopics {
+ require.Equal(t, referenceTopics[topicIndex], resultTopics[topicIndex], fmt.Sprintf("log index %v topic index %v doesn't match", logIndex, topicIndex))
+ }
+ }
+ }
+ }
+
+ referenceCalls, found := referenceValueMap["calls"].([]interface{})
+ if found {
+ resultCalls := resultMap["calls"].([]interface{})
+ require.Equal(t, len(referenceCalls), len(resultCalls), "logs size doesn't match")
+ for callIndex := range referenceCalls {
+ referenceCall := referenceCalls[callIndex].(map[string]interface{})
+ resultCall := resultCalls[callIndex].(map[string]interface{})
+
+ compareCallFrame(t, referenceCall, resultCall, networkName)
+ }
+ }
+}
+
func TestDebugTraceBlock(t *testing.T) {
if testing.Short() {
t.Skip()
@@ -498,8 +798,10 @@ func TestDebugTraceBlock(t *testing.T) {
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
+ log.Debug("************************ ", tc.name, " ************************")
+
for _, network := range networks {
- log.Debugf(network.Name)
+ log.Debug("------------------------ ", network.Name, " ------------------------")
ethereumClient := operations.MustGetClient(network.URL)
auth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
@@ -805,3 +1107,45 @@ func createERC20TransferRevertedSignedTx(t *testing.T, ctx context.Context, auth
return tx, nil
}
+
+func prepareCreate(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client) (map[string]interface{}, error) {
+ _, tx, sc, err := Creates.DeployCreates(auth, client)
+ require.NoError(t, err)
+
+ err = operations.WaitTxToBeMined(ctx, client, tx, operations.DefaultTimeoutTxToBeMined)
+ require.NoError(t, err)
+
+ return map[string]interface{}{
+ "sc": sc,
+ }, nil
+}
+
+func createCreateSignedTx(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error) {
+ scInterface := customData["sc"]
+ sc := scInterface.(*Creates.Creates)
+
+ opts := *auth
+ opts.NoSend = true
+
+ byteCode := hex.DecodeBig(Counter.CounterBin).Bytes()
+
+ tx, err := sc.OpCreate(&opts, byteCode, big.NewInt(0).SetInt64(int64(len(byteCode))))
+ require.NoError(t, err)
+
+ return tx, nil
+}
+
+func createCreate2SignedTx(t *testing.T, ctx context.Context, auth *bind.TransactOpts, client *ethclient.Client, customData map[string]interface{}) (*ethTypes.Transaction, error) {
+ scInterface := customData["sc"]
+ sc := scInterface.(*Creates.Creates)
+
+ opts := *auth
+ opts.NoSend = true
+
+ byteCode := hex.DecodeBig(Counter.CounterBin).Bytes()
+
+ tx, err := sc.OpCreate2(&opts, byteCode, big.NewInt(0).SetInt64(int64(len(byteCode))))
+ require.NoError(t, err)
+
+ return tx, nil
+}
diff --git a/test/e2e/sc_test.go b/test/e2e/sc_test.go
index 599aa3673b..a6fb0f7f73 100644
--- a/test/e2e/sc_test.go
+++ b/test/e2e/sc_test.go
@@ -6,6 +6,7 @@ import (
"testing"
"github.com/0xPolygonHermez/zkevm-node/log"
+ "github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/Counter"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/EmitLog2"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/FailureTest"
"github.com/0xPolygonHermez/zkevm-node/test/contracts/bin/Read"
@@ -17,6 +18,54 @@ import (
"github.com/stretchr/testify/require"
)
+func TestCounter(t *testing.T) {
+ if testing.Short() {
+ t.Skip()
+ }
+
+ var err error
+ err = operations.Teardown()
+ require.NoError(t, err)
+
+ defer func() { require.NoError(t, operations.Teardown()) }()
+
+ ctx := context.Background()
+ opsCfg := operations.GetDefaultOperationsConfig()
+ opsMan, err := operations.NewManager(ctx, opsCfg)
+ require.NoError(t, err)
+ err = opsMan.Setup()
+ require.NoError(t, err)
+
+ for _, network := range networks {
+ log.Debugf(network.Name)
+ client := operations.MustGetClient(network.URL)
+ auth := operations.MustGetAuth(network.PrivateKey, network.ChainID)
+
+ _, scTx, sc, err := Counter.DeployCounter(auth, client)
+ require.NoError(t, err)
+
+ logTx(scTx)
+ err = operations.WaitTxToBeMined(ctx, client, scTx, operations.DefaultTimeoutTxToBeMined)
+ require.NoError(t, err)
+
+ count, err := sc.GetCount(&bind.CallOpts{Pending: false})
+ require.NoError(t, err)
+
+ assert.Equal(t, 0, count.Cmp(big.NewInt(0)))
+
+ scCallTx, err := sc.Increment(auth)
+ require.NoError(t, err)
+
+ logTx(scCallTx)
+ err = operations.WaitTxToBeMined(ctx, client, scCallTx, operations.DefaultTimeoutTxToBeMined)
+ require.NoError(t, err)
+
+ count, err = sc.GetCount(&bind.CallOpts{Pending: false})
+ require.NoError(t, err)
+ assert.Equal(t, 0, count.Cmp(big.NewInt(1)))
+ }
+}
+
func TestEmitLog2(t *testing.T) {
if testing.Short() {
t.Skip()
diff --git a/tools/zkevmprovermock/Dockerfile b/tools/zkevmprovermock/Dockerfile
index a8e3140dbf..d4d21e0d8c 100644
--- a/tools/zkevmprovermock/Dockerfile
+++ b/tools/zkevmprovermock/Dockerfile
@@ -1,4 +1,4 @@
-FROM golang:1.18-alpine AS build
+FROM golang:1.19-alpine AS build
ENV CGO_ENABLED=0
WORKDIR /app