Apply geth patches on the fly #653 (#660)

This commit is contained in:
Caner Çıdam 2018-02-14 20:44:27 +03:00 committed by Igor Mandrigin
parent 0b979c507d
commit 15c72f3c6b
22 changed files with 1808 additions and 102 deletions

9
Gopkg.lock generated
View File

@ -47,7 +47,6 @@
revision = "935e0e8a636ca4ba70b713f3e38a19e1b77739e8"
[[projects]]
branch = "develop"
name = "github.com/ethereum/go-ethereum"
packages = [
".",
@ -89,7 +88,6 @@
"internal/ethapi",
"les",
"les/flowcontrol",
"les/status",
"light",
"log",
"log/term",
@ -106,11 +104,10 @@
"rpc",
"trie",
"whisper/mailserver",
"whisper/notifications",
"whisper/whisperv5"
]
revision = "5b2cc44bf2b32bb482def02d7c8fa32ba08d0bf4"
source = "https://github.com/status-im/go-ethereum.git"
revision = "4bb3c89d44e372e6a9ab85a8be0c9345265c763a"
version = "v1.7.3"
[[projects]]
name = "github.com/go-playground/locales"
@ -415,6 +412,6 @@
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "1e24f8d3adca6619451ee4406b8064268631ad03c06fdc6323abc00a974797ac"
inputs-digest = "15f735a948b26a34148ed3fa3a1d21cb1a3c45d0b87eb490a2ca1a514e4b1250"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,3 +1,5 @@
ignored = ["github.com/ethereum/go-ethereum/whisper/notifications"]
[prune]
unused-packages = true
go-tests = true
@ -21,10 +23,8 @@
revision = "dcd4997b0664bcfd6ef48e4ae9da8396e08b1cd9"
[[constraint]]
# fork of `go-ethereum` with Status patches
name = "github.com/ethereum/go-ethereum"
branch = "develop"
source = "https://github.com/status-im/go-ethereum.git"
branch = "release/1.7"
[[constraint]]
name = "github.com/prometheus/client_golang"

View File

@ -157,12 +157,19 @@ clean: ##@other Cleanup
deep-clean: clean
rm -Rdf .ethereumtest/StatusChain
vendor-check:
@dep ensure
./_assets/ci/validate-vendor.sh
vendor-check: ##@dependencies Require all new patches and disallow other changes
./_assets/patches/patcher -c
./_assets/ci/isolate-vendor-check.sh
dep-install:
dep-ensure: ##@dependencies Dep ensure and apply all patches
@dep ensure
./_assets/patches/patcher
dep-install: ##@dependencies Install vendoring tool
go get -u github.com/golang/dep/cmd/dep
patch:
patch: ##@patching Revert and apply all patches
./_assets/patches/patcher
patch-revert: ##@patching Revert all patches only
./_assets/patches/patcher -r

View File

@ -14,7 +14,7 @@ It's written in Go and requires Go 1.8 or above.
It uses Makefile to do most common actions. See `make help` output for available commands.
status-go uses [forked ethereum-go](https://github.com/status-im/go-ethereum) with [some patches applied](./geth-patches/) in it, located under [`vendor/`](./vendor/github.com/ethereum/go-ethereum) directory. See [geth-patches README](./geth-patches/README.md) for more info.
status-go uses [ethereum-go](https://github.com/status-im/go-ethereum) with [some patches applied](./_assets/patches/geth) in it, located under [`vendor/`](./vendor/github.com/ethereum/go-ethereum) directory. See [geth patches README](./_assets/patches/geth/README.md) for more info.
# Build
There are two main modes status-go can be built:

View File

@ -0,0 +1,54 @@
#!/usr/bin/env bash
# This is a hack to isolate vendor check in a
# separate clean state. Without this, validate-vendor.sh
# doesn't like our workflow with patches.
#
# How it works:
# 1) Stashes all changes and checks out to a temporary branch.
# 2) Reverts all patches and commits changes.
# 3) Runs "dep ensure" and validate-vendor.sh. Saves exit code and message.
# 4) Commits any changes.
# 5) Goes back to previous branch and removes the temporary branch.
# 6) Applies stashed changes.
# 7) Prints the message and exits with the exit code.
timestamp() {
date +"%s"
}
tempBranchName="isolated-vendor-check-$(timestamp)"
# Stash current changes first, apply later before exiting.
hasChanges=0
changes=($(git status --porcelain))
if [ "$changes" ]; then
git stash
hasChanges=1
fi
branchName="$(git rev-parse --abbrev-ref HEAD)"
git checkout -b $tempBranchName
# Revert all patches.
$(pwd)/_assets/patches/patcher -r
git add .
git commit -m "vendor check - auto"
# Do vendor check.
dep ensure
msg=$("$(pwd)/_assets/ci/validate-vendor.sh")
failed=$?
git add .
git commit -m "vendor check - auto"
# Go back to previous branch, clean and apply stashed.
git checkout "$branchName"
git branch -D $tempBranchName
if [ $hasChanges -eq 1 ]; then
git stash apply > /dev/null 2>&1
fi
echo $msg
exit $failed

View File

@ -25,6 +25,7 @@
## Checking-out all dependencies
`dep ensure` - download all the dependencies based on `Gopkg.lock`.
`make dep-ensure` - ensure all patches are applied, too. **(Recommended)**
`Gopkg.lock` is kept inact if it is in-sync with `Gopkg.toml`. If the `toml`
file is changed, `dep ensure` will re-generate `Gopkg.lock` as well.

View File

@ -3,7 +3,7 @@
Status-go uses [go-ethereum](https://github.com/ethereum/go-ethereum) (**upstream**) as its dependency. As any other Go dependency `go-ethereum` code is vendored and stored in `vendor/` folder.
However, there are a few changes has been made to the upstream, that are specific to Status and should not be merged to the upstream. We keep those changes as a set of patches, that can be applied upon each next release of `go-ethereum`. Patched version of `go-ethereum` is available in the [status-im/go-ethereum](https://github.com/status/go-ethereum) repo.
However, there are a few changes has been made to the upstream, that are specific to Status and should not be merged to the upstream. We keep those changes as a set of patches, that can be applied upon each next release of `go-ethereum`. Patched version of `go-ethereum` is available in vendor folder.
We try to minimize number and amount of changes in those patches as much as possible, and whereas possible, to contribute changes into the upstream.
@ -11,7 +11,7 @@ We try to minimize number and amount of changes in those patches as much as poss
Instructions for creating a patch from the command line:
1. Enter the command line at the status-im/go-ethereum repo root
1. Enter the command line at the go-ethereum dependency root in vendor folder.
1. Create the patch:
1. If you already have a commit that represents the change, find its SHA1 (e.g. `$COMMIT_SHA1`) and do `git diff $COMMIT_SHA1 > file.patch`
1. If the files are staged, do `git diff --cached > file.patch`
@ -31,64 +31,12 @@ Instructions for creating a patch from the command line:
- [`0014-whisperv6-notifications.patch`](./0014-whisperv6-notifications.patch) — adds Whisper v6 notifications (need to be reviewed and documented)
- [`0015-whisperv6-envelopes-tracing.patch`](./0015-whisperv6-envelopes-tracing.patch) — adds Whisper v6 envelope tracing (need to be reviewed and documented)
# Updating upstream version
# Updating
When a new stable release of `go-ethereum` comes out, we need to upgrade our fork and vendored copy.
When a new stable release of `go-ethereum` comes out, we need to upgrade our vendored copy. We use `dep` for vendoring, so for upgrading:
**Note: The process is completely repeatable, so it's safe to remove current `go-ethereum` directory, clone latest upstream version and apply patches from scratch.**
- Change target branch for `go-ethereum` in `Gopkg.toml`.
- `dep ensure -update github.com/ethereum/go-ethereum`
- `make dep-ensure`
### Using existing fork repo (recommended)
#### I. In our fork at /status-im/go-ethereum.
1. Remove the local `develop` branch.
```bash
git branch -D develop
```
1. Pull upstream release branch into `develop` branch.
```bash
git pull git@github.com:ethereum/go-ethereum.git <release_branch>:develop
```
In our case `<release_branch>` would be `release/1.7` because the current stable version is
1.7.x.
1. Apply patches
```bash
for patch in $GOPATH/src/github.com/status-im/status-go/_assets/patches/geth/*.patch;
do
patch -p1 < $patch;
done
```
Once patches applied, you might want to inspect changes between current vendored version and newly patched version by this command:
```bash
diff -Nru -x "*_test.go" -x "vendor" -x ".git" -x "tests" -x "build" --brief $GOPATH/src/github.com/status-im/go-ethereum $GOPATH/src/github.com/status-im/status-go/vendor/github.com/ethereum/go-ethereum
```
1. Push `develop` branch to our remote, rewriting history
```bash
git push -f origin develop
```
#### II. In status-go repository
1. Update vendored `go-ethereum` (note that we use upstream's address there, we override the download link to our fork address in `Gopkg.toml`)
```bash
dep ensure --update github.com/ethereum/go-ethereum
```
`Gopkg.lock` will change and files within `vendor/ethereum/go-ethereum`.
1. Run tests
```bash
make ci
```
1. Commit & push changes, create a PR
This will ensure that dependency is upgraded and fully patched. Upon success, you can do `make vendor-check` after committing all the changes, in order to ensure that all changes are valid.

View File

@ -9,17 +9,25 @@
# Usage: ./patcher -p <base_path> -r -v
# -p: <base_path> is where to apply to (a go-ethereum package)
# -r: reverts all and exit if this flag is present
# -c: reverts all to see what's applied, applies all previously applied back again,
# reports unapplied patches in this branch by comparing with "develop" including
# uncommitted ones and exits (with 1 if there are any)
# -v: verbose error reporting about failed patch
#
# If -p is not present, default path is as below ($basepath).
patches=($( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )/geth/*.patch)
dir=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
patches=("$dir"/geth/*.patch)
# Use this branch as a reference for comparing patches
# in current branch (-c option).
baseBranch="develop"
# Base path is vendor/github.com/ethereum/go-ethereum
# unless specified.
basepath="vendor/github.com/ethereum/go-ethereum"
verbose=0
while getopts :prv opt; do
while getopts :prcv opt; do
case $opt in
p)
basepath=$OPTARG
@ -32,6 +40,54 @@ while getopts :prv opt; do
echo "Reverted all."
exit
;;
c)
applied=()
unapplied=()
# Finds applied patches using reverse order and
# notes them down.
for ((i=${#patches[@]}-1; i>=0; i--)); do
f=${patches[$i]}
git apply "$f" --directory="$basepath" -R > /dev/null 2>&1
if [ $? -eq 1 ]; then
unapplied+=("$f")
else
applied+=("$f")
fi
done
# Applies reverted patches back again.
for ((i=${#applied[@]}-1; i>=0; i--)); do
f=${applied[$i]}
git apply "$f" --directory="$basepath" > /dev/null 2>&1
done
# Sorts out new patches' paths by comparing with base branch.
fromBaseBranch=($(git diff $baseBranch --stat | grep "\\.patch" |
while IFS=" " read -r -a line; do
path="$(pwd)/${line[0]}"
echo "$path"
done
))
# Also does the same with uncommitted.
uncommitted=($(git status -u --porcelain | grep "\\.patch" |
while IFS=" " read -r -a line; do
length=${#line[@]}
path="$(pwd)/${line[$((length - 1))]}"
echo "$path"
done
))
newPatches=( "${fromBaseBranch[@]}" "${uncommitted[@]}" )
# Checks new patches and exits with 1 if there are unapplied.
hasUnapplied=0
for newPatch in "${newPatches[@]}"; do
for unapp in "${unapplied[@]}"; do
if [ "$unapp" == "$newPatch" ]; then
echo "Recently added/changed but not applied: $unapp"
hasUnapplied=1
break
fi
done
done
exit $hasUnapplied
;;
v)
verbose=1
;;

View File

@ -98,8 +98,6 @@ var (
argEnode = flag.String("boot", "", "bootstrap node you want to connect to (e.g. enode://e454......08d50@52.176.211.200:16428)")
argTopic = flag.String("topic", "", "topic in hexadecimal format (e.g. 70a4beef)")
argSaveDir = flag.String("savedir", "", "directory where incoming messages will be saved as files")
argSymPass = flag.String("sympass", "", "SymKey password")
argMsPass = flag.String("mspass", "", "Mailserver password")
)
func main() {
@ -148,13 +146,6 @@ func processArgs() {
} else if *fileExMode {
utils.Fatalf("Parameter 'savedir' is mandatory for file exchange mode")
}
if len(*argSymPass) > 0 {
symPass = *argSymPass
}
if len(*argMsPass) > 0 {
msPassword = *argMsPass
}
if *echoMode {
echo()
@ -424,24 +415,10 @@ func run() {
} else if *fileExMode {
sendFilesLoop()
} else {
pingLoop() // instead of sendLoop()
sendLoop()
}
}
func pingLoop() {
ticker := time.NewTicker(time.Second * 120)
for {
select {
case <-ticker.C:
fmt.Println("I am alive: ", time.Now())
case <-done:
return
}
}
}
func sendLoop() {
for {
s := scanLine("")

View File

@ -0,0 +1,296 @@
// 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 <http://www.gnu.org/licenses/>.
// Package tests implements execution of Ethereum JSON tests.
package tests
import (
"bytes"
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
// A BlockTest checks handling of entire blocks.
type BlockTest struct {
json btJSON
}
func (t *BlockTest) UnmarshalJSON(in []byte) error {
return json.Unmarshal(in, &t.json)
}
type btJSON struct {
Blocks []btBlock `json:"blocks"`
Genesis btHeader `json:"genesisBlockHeader"`
Pre core.GenesisAlloc `json:"pre"`
Post core.GenesisAlloc `json:"postState"`
BestBlock common.UnprefixedHash `json:"lastblockhash"`
Network string `json:"network"`
}
type btBlock struct {
BlockHeader *btHeader
Rlp string
UncleHeaders []*btHeader
}
//go:generate gencodec -type btHeader -field-override btHeaderMarshaling -out gen_btheader.go
type btHeader struct {
Bloom types.Bloom
Coinbase common.Address
MixHash common.Hash
Nonce types.BlockNonce
Number *big.Int
Hash common.Hash
ParentHash common.Hash
ReceiptTrie common.Hash
StateRoot common.Hash
TransactionsTrie common.Hash
UncleHash common.Hash
ExtraData []byte
Difficulty *big.Int
GasLimit *big.Int
GasUsed *big.Int
Timestamp *big.Int
}
type btHeaderMarshaling struct {
ExtraData hexutil.Bytes
Number *math.HexOrDecimal256
Difficulty *math.HexOrDecimal256
GasLimit *math.HexOrDecimal256
GasUsed *math.HexOrDecimal256
Timestamp *math.HexOrDecimal256
}
func (t *BlockTest) Run() error {
config, ok := Forks[t.json.Network]
if !ok {
return UnsupportedForkError{t.json.Network}
}
// import pre accounts & construct test genesis block & state root
db, _ := ethdb.NewMemDatabase()
gblock, err := t.genesis(config).Commit(db)
if err != nil {
return err
}
if gblock.Hash() != t.json.Genesis.Hash {
return fmt.Errorf("genesis block hash doesn't match test: computed=%x, test=%x\n", gblock.Hash().Bytes()[:6], t.json.Genesis.Hash[:6])
}
if gblock.Root() != t.json.Genesis.StateRoot {
return fmt.Errorf("genesis block state root does not match test: computed=%x, test=%x", gblock.Root().Bytes()[:6], t.json.Genesis.StateRoot[:6])
}
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), vm.Config{})
if err != nil {
return err
}
defer chain.Stop()
validBlocks, err := t.insertBlocks(chain)
if err != nil {
return err
}
cmlast := chain.LastBlockHash()
if common.Hash(t.json.BestBlock) != cmlast {
return fmt.Errorf("last block hash validation mismatch: want: %x, have: %x", t.json.BestBlock, cmlast)
}
newDB, err := chain.State()
if err != nil {
return err
}
if err = t.validatePostState(newDB); err != nil {
return fmt.Errorf("post state validation failed: %v", err)
}
return t.validateImportedHeaders(chain, validBlocks)
}
func (t *BlockTest) genesis(config *params.ChainConfig) *core.Genesis {
return &core.Genesis{
Config: config,
Nonce: t.json.Genesis.Nonce.Uint64(),
Timestamp: t.json.Genesis.Timestamp.Uint64(),
ParentHash: t.json.Genesis.ParentHash,
ExtraData: t.json.Genesis.ExtraData,
GasLimit: t.json.Genesis.GasLimit.Uint64(),
GasUsed: t.json.Genesis.GasUsed.Uint64(),
Difficulty: t.json.Genesis.Difficulty,
Mixhash: t.json.Genesis.MixHash,
Coinbase: t.json.Genesis.Coinbase,
Alloc: t.json.Pre,
}
}
/* See https://github.com/ethereum/tests/wiki/Blockchain-Tests-II
Whether a block is valid or not is a bit subtle, it's defined by presence of
blockHeader, transactions and uncleHeaders fields. If they are missing, the block is
invalid and we must verify that we do not accept it.
Since some tests mix valid and invalid blocks we need to check this for every block.
If a block is invalid it does not necessarily fail the test, if it's invalidness is
expected we are expected to ignore it and continue processing and then validate the
post state.
*/
func (t *BlockTest) insertBlocks(blockchain *core.BlockChain) ([]btBlock, error) {
validBlocks := make([]btBlock, 0)
// insert the test blocks, which will execute all transactions
for _, b := range t.json.Blocks {
cb, err := b.decode()
if err != nil {
if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block
} else {
return nil, fmt.Errorf("Block RLP decoding failed when expected to succeed: %v", err)
}
}
// RLP decoding worked, try to insert into chain:
blocks := types.Blocks{cb}
i, err := blockchain.InsertChain(blocks)
if err != nil {
if b.BlockHeader == nil {
continue // OK - block is supposed to be invalid, continue with next block
} else {
return nil, fmt.Errorf("Block #%v insertion into chain failed: %v", blocks[i].Number(), err)
}
}
if b.BlockHeader == nil {
return nil, fmt.Errorf("Block insertion should have failed")
}
// validate RLP decoding by checking all values against test file JSON
if err = validateHeader(b.BlockHeader, cb.Header()); err != nil {
return nil, fmt.Errorf("Deserialised block header validation failed: %v", err)
}
validBlocks = append(validBlocks, b)
}
return validBlocks, nil
}
func validateHeader(h *btHeader, h2 *types.Header) error {
if h.Bloom != h2.Bloom {
return fmt.Errorf("Bloom: want: %x have: %x", h.Bloom, h2.Bloom)
}
if h.Coinbase != h2.Coinbase {
return fmt.Errorf("Coinbase: want: %x have: %x", h.Coinbase, h2.Coinbase)
}
if h.MixHash != h2.MixDigest {
return fmt.Errorf("MixHash: want: %x have: %x", h.MixHash, h2.MixDigest)
}
if h.Nonce != h2.Nonce {
return fmt.Errorf("Nonce: want: %x have: %x", h.Nonce, h2.Nonce)
}
if h.Number.Cmp(h2.Number) != 0 {
return fmt.Errorf("Number: want: %v have: %v", h.Number, h2.Number)
}
if h.ParentHash != h2.ParentHash {
return fmt.Errorf("Parent hash: want: %x have: %x", h.ParentHash, h2.ParentHash)
}
if h.ReceiptTrie != h2.ReceiptHash {
return fmt.Errorf("Receipt hash: want: %x have: %x", h.ReceiptTrie, h2.ReceiptHash)
}
if h.TransactionsTrie != h2.TxHash {
return fmt.Errorf("Tx hash: want: %x have: %x", h.TransactionsTrie, h2.TxHash)
}
if h.StateRoot != h2.Root {
return fmt.Errorf("State hash: want: %x have: %x", h.StateRoot, h2.Root)
}
if h.UncleHash != h2.UncleHash {
return fmt.Errorf("Uncle hash: want: %x have: %x", h.UncleHash, h2.UncleHash)
}
if !bytes.Equal(h.ExtraData, h2.Extra) {
return fmt.Errorf("Extra data: want: %x have: %x", h.ExtraData, h2.Extra)
}
if h.Difficulty.Cmp(h2.Difficulty) != 0 {
return fmt.Errorf("Difficulty: want: %v have: %v", h.Difficulty, h2.Difficulty)
}
if h.GasLimit.Cmp(h2.GasLimit) != 0 {
return fmt.Errorf("GasLimit: want: %v have: %v", h.GasLimit, h2.GasLimit)
}
if h.GasUsed.Cmp(h2.GasUsed) != 0 {
return fmt.Errorf("GasUsed: want: %v have: %v", h.GasUsed, h2.GasUsed)
}
if h.Timestamp.Cmp(h2.Time) != 0 {
return fmt.Errorf("Timestamp: want: %v have: %v", h.Timestamp, h2.Time)
}
return nil
}
func (t *BlockTest) validatePostState(statedb *state.StateDB) error {
// validate post state accounts in test file against what we have in state db
for addr, acct := range t.json.Post {
// address is indirectly verified by the other fields, as it's the db key
code2 := statedb.GetCode(addr)
balance2 := statedb.GetBalance(addr)
nonce2 := statedb.GetNonce(addr)
if !bytes.Equal(code2, acct.Code) {
return fmt.Errorf("account code mismatch for addr: %s want: %v have: %s", addr, acct.Code, hex.EncodeToString(code2))
}
if balance2.Cmp(acct.Balance) != 0 {
return fmt.Errorf("account balance mismatch for addr: %s, want: %d, have: %d", addr, acct.Balance, balance2)
}
if nonce2 != acct.Nonce {
return fmt.Errorf("account nonce mismatch for addr: %s want: %d have: %d", addr, acct.Nonce, nonce2)
}
}
return nil
}
func (t *BlockTest) validateImportedHeaders(cm *core.BlockChain, validBlocks []btBlock) error {
// to get constant lookup when verifying block headers by hash (some tests have many blocks)
bmap := make(map[common.Hash]btBlock, len(t.json.Blocks))
for _, b := range validBlocks {
bmap[b.BlockHeader.Hash] = b
}
// iterate over blocks backwards from HEAD and validate imported
// headers vs test file. some tests have reorgs, and we import
// block-by-block, so we can only validate imported headers after
// all blocks have been processed by BlockChain, as they may not
// be part of the longest chain until last block is imported.
for b := cm.CurrentBlock(); b != nil && b.NumberU64() != 0; b = cm.GetBlockByHash(b.Header().ParentHash) {
if err := validateHeader(bmap[b.Hash()].BlockHeader, b.Header()); err != nil {
return fmt.Errorf("Imported block header validation failed: %v", err)
}
}
return nil
}
func (bb *btBlock) decode() (*types.Block, error) {
data, err := hexutil.Decode(bb.Rlp)
if err != nil {
return nil, err
}
var b types.Block
err = rlp.DecodeBytes(data, &b)
return &b, err
}

View File

@ -0,0 +1,70 @@
// 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 <http://www.gnu.org/licenses/>.
//
package tests
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
)
//go:generate gencodec -type DifficultyTest -field-override difficultyTestMarshaling -out gen_difficultytest.go
type DifficultyTest struct {
ParentTimestamp *big.Int `json:"parentTimestamp"`
ParentDifficulty *big.Int `json:"parentDifficulty"`
UncleHash common.Hash `json:"parentUncles"`
CurrentTimestamp *big.Int `json:"currentTimestamp"`
CurrentBlockNumber uint64 `json:"currentBlockNumber"`
CurrentDifficulty *big.Int `json:"currentDifficulty"`
}
type difficultyTestMarshaling struct {
ParentTimestamp *math.HexOrDecimal256
ParentDifficulty *math.HexOrDecimal256
CurrentTimestamp *math.HexOrDecimal256
CurrentDifficulty *math.HexOrDecimal256
UncleHash common.Hash
CurrentBlockNumber math.HexOrDecimal64
}
func (test *DifficultyTest) Run(config *params.ChainConfig) error {
parentNumber := big.NewInt(int64(test.CurrentBlockNumber - 1))
parent := &types.Header{
Difficulty: test.ParentDifficulty,
Time: test.ParentTimestamp,
Number: parentNumber,
UncleHash: test.UncleHash,
}
actual := ethash.CalcDifficulty(config, test.CurrentTimestamp.Uint64(), parent)
exp := test.CurrentDifficulty
if actual.Cmp(exp) != 0 {
return fmt.Errorf("parent[time %v diff %v unclehash:%x] child[time %v number %v] diff %v != expected %v",
test.ParentTimestamp, test.ParentDifficulty, test.UncleHash,
test.CurrentTimestamp, test.CurrentBlockNumber, actual, exp)
}
return nil
}

View File

@ -0,0 +1,128 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
)
var _ = (*btHeaderMarshaling)(nil)
func (b btHeader) MarshalJSON() ([]byte, error) {
type btHeader struct {
Bloom types.Bloom
Coinbase common.Address
MixHash common.Hash
Nonce types.BlockNonce
Number *math.HexOrDecimal256
Hash common.Hash
ParentHash common.Hash
ReceiptTrie common.Hash
StateRoot common.Hash
TransactionsTrie common.Hash
UncleHash common.Hash
ExtraData hexutil.Bytes
Difficulty *math.HexOrDecimal256
GasLimit *math.HexOrDecimal256
GasUsed *math.HexOrDecimal256
Timestamp *math.HexOrDecimal256
}
var enc btHeader
enc.Bloom = b.Bloom
enc.Coinbase = b.Coinbase
enc.MixHash = b.MixHash
enc.Nonce = b.Nonce
enc.Number = (*math.HexOrDecimal256)(b.Number)
enc.Hash = b.Hash
enc.ParentHash = b.ParentHash
enc.ReceiptTrie = b.ReceiptTrie
enc.StateRoot = b.StateRoot
enc.TransactionsTrie = b.TransactionsTrie
enc.UncleHash = b.UncleHash
enc.ExtraData = b.ExtraData
enc.Difficulty = (*math.HexOrDecimal256)(b.Difficulty)
enc.GasLimit = (*math.HexOrDecimal256)(b.GasLimit)
enc.GasUsed = (*math.HexOrDecimal256)(b.GasUsed)
enc.Timestamp = (*math.HexOrDecimal256)(b.Timestamp)
return json.Marshal(&enc)
}
func (b *btHeader) UnmarshalJSON(input []byte) error {
type btHeader struct {
Bloom *types.Bloom
Coinbase *common.Address
MixHash *common.Hash
Nonce *types.BlockNonce
Number *math.HexOrDecimal256
Hash *common.Hash
ParentHash *common.Hash
ReceiptTrie *common.Hash
StateRoot *common.Hash
TransactionsTrie *common.Hash
UncleHash *common.Hash
ExtraData hexutil.Bytes
Difficulty *math.HexOrDecimal256
GasLimit *math.HexOrDecimal256
GasUsed *math.HexOrDecimal256
Timestamp *math.HexOrDecimal256
}
var dec btHeader
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Bloom != nil {
b.Bloom = *dec.Bloom
}
if dec.Coinbase != nil {
b.Coinbase = *dec.Coinbase
}
if dec.MixHash != nil {
b.MixHash = *dec.MixHash
}
if dec.Nonce != nil {
b.Nonce = *dec.Nonce
}
if dec.Number != nil {
b.Number = (*big.Int)(dec.Number)
}
if dec.Hash != nil {
b.Hash = *dec.Hash
}
if dec.ParentHash != nil {
b.ParentHash = *dec.ParentHash
}
if dec.ReceiptTrie != nil {
b.ReceiptTrie = *dec.ReceiptTrie
}
if dec.StateRoot != nil {
b.StateRoot = *dec.StateRoot
}
if dec.TransactionsTrie != nil {
b.TransactionsTrie = *dec.TransactionsTrie
}
if dec.UncleHash != nil {
b.UncleHash = *dec.UncleHash
}
if dec.ExtraData != nil {
b.ExtraData = dec.ExtraData
}
if dec.Difficulty != nil {
b.Difficulty = (*big.Int)(dec.Difficulty)
}
if dec.GasLimit != nil {
b.GasLimit = (*big.Int)(dec.GasLimit)
}
if dec.GasUsed != nil {
b.GasUsed = (*big.Int)(dec.GasUsed)
}
if dec.Timestamp != nil {
b.Timestamp = (*big.Int)(dec.Timestamp)
}
return nil
}

View File

@ -0,0 +1,66 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
)
var _ = (*difficultyTestMarshaling)(nil)
func (d DifficultyTest) MarshalJSON() ([]byte, error) {
type DifficultyTest struct {
ParentTimestamp *math.HexOrDecimal256 `json:"parentTimestamp"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
UncleHash common.Hash `json:"parentUncles"`
CurrentTimestamp *math.HexOrDecimal256 `json:"currentTimestamp"`
CurrentBlockNumber math.HexOrDecimal64 `json:"currentBlockNumber"`
CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
}
var enc DifficultyTest
enc.ParentTimestamp = (*math.HexOrDecimal256)(d.ParentTimestamp)
enc.ParentDifficulty = (*math.HexOrDecimal256)(d.ParentDifficulty)
enc.UncleHash = d.UncleHash
enc.CurrentTimestamp = (*math.HexOrDecimal256)(d.CurrentTimestamp)
enc.CurrentBlockNumber = math.HexOrDecimal64(d.CurrentBlockNumber)
enc.CurrentDifficulty = (*math.HexOrDecimal256)(d.CurrentDifficulty)
return json.Marshal(&enc)
}
func (d *DifficultyTest) UnmarshalJSON(input []byte) error {
type DifficultyTest struct {
ParentTimestamp *math.HexOrDecimal256 `json:"parentTimestamp"`
ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
UncleHash *common.Hash `json:"parentUncles"`
CurrentTimestamp *math.HexOrDecimal256 `json:"currentTimestamp"`
CurrentBlockNumber *math.HexOrDecimal64 `json:"currentBlockNumber"`
CurrentDifficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
}
var dec DifficultyTest
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.ParentTimestamp != nil {
d.ParentTimestamp = (*big.Int)(dec.ParentTimestamp)
}
if dec.ParentDifficulty != nil {
d.ParentDifficulty = (*big.Int)(dec.ParentDifficulty)
}
if dec.UncleHash != nil {
d.UncleHash = *dec.UncleHash
}
if dec.CurrentTimestamp != nil {
d.CurrentTimestamp = (*big.Int)(dec.CurrentTimestamp)
}
if dec.CurrentBlockNumber != nil {
d.CurrentBlockNumber = uint64(*dec.CurrentBlockNumber)
}
if dec.CurrentDifficulty != nil {
d.CurrentDifficulty = (*big.Int)(dec.CurrentDifficulty)
}
return nil
}

View File

@ -0,0 +1,66 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
)
var _ = (*stEnvMarshaling)(nil)
func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct {
Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"`
Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
}
var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
enc.GasLimit = (*math.HexOrDecimal256)(s.GasLimit)
enc.Number = math.HexOrDecimal64(s.Number)
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
return json.Marshal(&enc)
}
func (s *stEnv) UnmarshalJSON(input []byte) error {
type stEnv struct {
Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
GasLimit *math.HexOrDecimal256 `json:"currentGasLimit" gencodec:"required"`
Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
}
var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Coinbase == nil {
return errors.New("missing required field 'currentCoinbase' for stEnv")
}
s.Coinbase = common.Address(*dec.Coinbase)
if dec.Difficulty == nil {
return errors.New("missing required field 'currentDifficulty' for stEnv")
}
s.Difficulty = (*big.Int)(dec.Difficulty)
if dec.GasLimit == nil {
return errors.New("missing required field 'currentGasLimit' for stEnv")
}
s.GasLimit = (*big.Int)(dec.GasLimit)
if dec.Number == nil {
return errors.New("missing required field 'currentNumber' for stEnv")
}
s.Number = uint64(*dec.Number)
if dec.Timestamp == nil {
return errors.New("missing required field 'currentTimestamp' for stEnv")
}
s.Timestamp = uint64(*dec.Timestamp)
return nil
}

View File

@ -0,0 +1,80 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"math/big"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
)
var _ = (*stTransactionMarshaling)(nil)
func (s stTransaction) MarshalJSON() ([]byte, error) {
type stTransaction struct {
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
Nonce math.HexOrDecimal64 `json:"nonce"`
To string `json:"to"`
Data []string `json:"data"`
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
Value []string `json:"value"`
PrivateKey hexutil.Bytes `json:"secretKey"`
}
var enc stTransaction
enc.GasPrice = (*math.HexOrDecimal256)(s.GasPrice)
enc.Nonce = math.HexOrDecimal64(s.Nonce)
enc.To = s.To
enc.Data = s.Data
if s.GasLimit != nil {
enc.GasLimit = make([]math.HexOrDecimal64, len(s.GasLimit))
for k, v := range s.GasLimit {
enc.GasLimit[k] = math.HexOrDecimal64(v)
}
}
enc.Value = s.Value
enc.PrivateKey = s.PrivateKey
return json.Marshal(&enc)
}
func (s *stTransaction) UnmarshalJSON(input []byte) error {
type stTransaction struct {
GasPrice *math.HexOrDecimal256 `json:"gasPrice"`
Nonce *math.HexOrDecimal64 `json:"nonce"`
To *string `json:"to"`
Data []string `json:"data"`
GasLimit []math.HexOrDecimal64 `json:"gasLimit"`
Value []string `json:"value"`
PrivateKey hexutil.Bytes `json:"secretKey"`
}
var dec stTransaction
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.GasPrice != nil {
s.GasPrice = (*big.Int)(dec.GasPrice)
}
if dec.Nonce != nil {
s.Nonce = uint64(*dec.Nonce)
}
if dec.To != nil {
s.To = *dec.To
}
if dec.Data != nil {
s.Data = dec.Data
}
if dec.GasLimit != nil {
s.GasLimit = make([]uint64, len(dec.GasLimit))
for k, v := range dec.GasLimit {
s.GasLimit[k] = uint64(v)
}
}
if dec.Value != nil {
s.Value = dec.Value
}
if dec.PrivateKey != nil {
s.PrivateKey = dec.PrivateKey
}
return nil
}

View File

@ -0,0 +1,95 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
)
var _ = (*ttTransactionMarshaling)(nil)
func (t ttTransaction) MarshalJSON() ([]byte, error) {
type ttTransaction struct {
Data hexutil.Bytes `gencodec:"required"`
GasLimit *math.HexOrDecimal256 `gencodec:"required"`
GasPrice *math.HexOrDecimal256 `gencodec:"required"`
Nonce math.HexOrDecimal64 `gencodec:"required"`
Value *math.HexOrDecimal256 `gencodec:"required"`
R *math.HexOrDecimal256 `gencodec:"required"`
S *math.HexOrDecimal256 `gencodec:"required"`
V *math.HexOrDecimal256 `gencodec:"required"`
To common.Address `gencodec:"required"`
}
var enc ttTransaction
enc.Data = t.Data
enc.GasLimit = (*math.HexOrDecimal256)(t.GasLimit)
enc.GasPrice = (*math.HexOrDecimal256)(t.GasPrice)
enc.Nonce = math.HexOrDecimal64(t.Nonce)
enc.Value = (*math.HexOrDecimal256)(t.Value)
enc.R = (*math.HexOrDecimal256)(t.R)
enc.S = (*math.HexOrDecimal256)(t.S)
enc.V = (*math.HexOrDecimal256)(t.V)
enc.To = t.To
return json.Marshal(&enc)
}
func (t *ttTransaction) UnmarshalJSON(input []byte) error {
type ttTransaction struct {
Data hexutil.Bytes `gencodec:"required"`
GasLimit *math.HexOrDecimal256 `gencodec:"required"`
GasPrice *math.HexOrDecimal256 `gencodec:"required"`
Nonce *math.HexOrDecimal64 `gencodec:"required"`
Value *math.HexOrDecimal256 `gencodec:"required"`
R *math.HexOrDecimal256 `gencodec:"required"`
S *math.HexOrDecimal256 `gencodec:"required"`
V *math.HexOrDecimal256 `gencodec:"required"`
To *common.Address `gencodec:"required"`
}
var dec ttTransaction
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Data == nil {
return errors.New("missing required field 'data' for ttTransaction")
}
t.Data = dec.Data
if dec.GasLimit == nil {
return errors.New("missing required field 'gasLimit' for ttTransaction")
}
t.GasLimit = (*big.Int)(dec.GasLimit)
if dec.GasPrice == nil {
return errors.New("missing required field 'gasPrice' for ttTransaction")
}
t.GasPrice = (*big.Int)(dec.GasPrice)
if dec.Nonce == nil {
return errors.New("missing required field 'nonce' for ttTransaction")
}
t.Nonce = uint64(*dec.Nonce)
if dec.Value == nil {
return errors.New("missing required field 'value' for ttTransaction")
}
t.Value = (*big.Int)(dec.Value)
if dec.R == nil {
return errors.New("missing required field 'r' for ttTransaction")
}
t.R = (*big.Int)(dec.R)
if dec.S == nil {
return errors.New("missing required field 's' for ttTransaction")
}
t.S = (*big.Int)(dec.S)
if dec.V == nil {
return errors.New("missing required field 'v' for ttTransaction")
}
t.V = (*big.Int)(dec.V)
if dec.To == nil {
return errors.New("missing required field 'to' for ttTransaction")
}
t.To = *dec.To
return nil
}

View File

@ -0,0 +1,88 @@
// Code generated by github.com/fjl/gencodec. DO NOT EDIT.
package tests
import (
"encoding/json"
"errors"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
)
var _ = (*vmExecMarshaling)(nil)
func (v vmExec) MarshalJSON() ([]byte, error) {
type vmExec struct {
Address common.UnprefixedAddress `json:"address" gencodec:"required"`
Caller common.UnprefixedAddress `json:"caller" gencodec:"required"`
Origin common.UnprefixedAddress `json:"origin" gencodec:"required"`
Code hexutil.Bytes `json:"code" gencodec:"required"`
Data hexutil.Bytes `json:"data" gencodec:"required"`
Value *math.HexOrDecimal256 `json:"value" gencodec:"required"`
GasLimit math.HexOrDecimal64 `json:"gas" gencodec:"required"`
GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"`
}
var enc vmExec
enc.Address = common.UnprefixedAddress(v.Address)
enc.Caller = common.UnprefixedAddress(v.Caller)
enc.Origin = common.UnprefixedAddress(v.Origin)
enc.Code = v.Code
enc.Data = v.Data
enc.Value = (*math.HexOrDecimal256)(v.Value)
enc.GasLimit = math.HexOrDecimal64(v.GasLimit)
enc.GasPrice = (*math.HexOrDecimal256)(v.GasPrice)
return json.Marshal(&enc)
}
func (v *vmExec) UnmarshalJSON(input []byte) error {
type vmExec struct {
Address *common.UnprefixedAddress `json:"address" gencodec:"required"`
Caller *common.UnprefixedAddress `json:"caller" gencodec:"required"`
Origin *common.UnprefixedAddress `json:"origin" gencodec:"required"`
Code hexutil.Bytes `json:"code" gencodec:"required"`
Data hexutil.Bytes `json:"data" gencodec:"required"`
Value *math.HexOrDecimal256 `json:"value" gencodec:"required"`
GasLimit *math.HexOrDecimal64 `json:"gas" gencodec:"required"`
GasPrice *math.HexOrDecimal256 `json:"gasPrice" gencodec:"required"`
}
var dec vmExec
if err := json.Unmarshal(input, &dec); err != nil {
return err
}
if dec.Address == nil {
return errors.New("missing required field 'address' for vmExec")
}
v.Address = common.Address(*dec.Address)
if dec.Caller == nil {
return errors.New("missing required field 'caller' for vmExec")
}
v.Caller = common.Address(*dec.Caller)
if dec.Origin == nil {
return errors.New("missing required field 'origin' for vmExec")
}
v.Origin = common.Address(*dec.Origin)
if dec.Code == nil {
return errors.New("missing required field 'code' for vmExec")
}
v.Code = dec.Code
if dec.Data == nil {
return errors.New("missing required field 'data' for vmExec")
}
v.Data = dec.Data
if dec.Value == nil {
return errors.New("missing required field 'value' for vmExec")
}
v.Value = (*big.Int)(dec.Value)
if dec.GasLimit == nil {
return errors.New("missing required field 'gas' for vmExec")
}
v.GasLimit = uint64(*dec.GasLimit)
if dec.GasPrice == nil {
return errors.New("missing required field 'gasPrice' for vmExec")
}
v.GasPrice = (*big.Int)(dec.GasPrice)
return nil
}

88
vendor/github.com/ethereum/go-ethereum/tests/init.go generated vendored Normal file
View File

@ -0,0 +1,88 @@
// 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 <http://www.gnu.org/licenses/>.
package tests
import (
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/params"
)
// This table defines supported forks and their chain config.
var Forks = map[string]*params.ChainConfig{
"Frontier": {
ChainId: big.NewInt(1),
},
"Homestead": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
},
"EIP150": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
},
"EIP158": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
},
"Byzantium": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
DAOForkBlock: big.NewInt(0),
ByzantiumBlock: big.NewInt(0),
},
"FrontierToHomesteadAt5": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(5),
},
"HomesteadToEIP150At5": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(5),
},
"HomesteadToDaoAt5": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
DAOForkBlock: big.NewInt(5),
DAOForkSupport: true,
},
"EIP158ToByzantiumAt5": {
ChainId: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
EIP155Block: big.NewInt(0),
EIP158Block: big.NewInt(0),
ByzantiumBlock: big.NewInt(5),
},
}
// UnsupportedForkError is returned when a test requests a fork that isn't implemented.
type UnsupportedForkError struct {
Name string
}
func (e UnsupportedForkError) Error() string {
return fmt.Sprintf("unsupported fork %q", e.Name)
}

View File

@ -0,0 +1,159 @@
// 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 <http://www.gnu.org/licenses/>.
package tests
import (
"bytes"
"encoding/hex"
"errors"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/rlp"
)
// RLPTest is the JSON structure of a single RLP test.
type RLPTest struct {
// If the value of In is "INVALID" or "VALID", the test
// checks whether Out can be decoded into a value of
// type interface{}.
//
// For other JSON values, In is treated as a driver for
// calls to rlp.Stream. The test also verifies that encoding
// In produces the bytes in Out.
In interface{}
// Out is a hex-encoded RLP value.
Out string
}
// Run executes the test.
func (t *RLPTest) Run() error {
outb, err := hex.DecodeString(t.Out)
if err != nil {
return fmt.Errorf("invalid hex in Out")
}
// Handle simple decoding tests with no actual In value.
if t.In == "VALID" || t.In == "INVALID" {
return checkDecodeInterface(outb, t.In == "VALID")
}
// Check whether encoding the value produces the same bytes.
in := translateJSON(t.In)
b, err := rlp.EncodeToBytes(in)
if err != nil {
return fmt.Errorf("encode failed: %v", err)
}
if !bytes.Equal(b, outb) {
return fmt.Errorf("encode produced %x, want %x", b, outb)
}
// Test stream decoding.
s := rlp.NewStream(bytes.NewReader(outb), 0)
return checkDecodeFromJSON(s, in)
}
func checkDecodeInterface(b []byte, isValid bool) error {
err := rlp.DecodeBytes(b, new(interface{}))
switch {
case isValid && err != nil:
return fmt.Errorf("decoding failed: %v", err)
case !isValid && err == nil:
return fmt.Errorf("decoding of invalid value succeeded")
}
return nil
}
// translateJSON makes test json values encodable with RLP.
func translateJSON(v interface{}) interface{} {
switch v := v.(type) {
case float64:
return uint64(v)
case string:
if len(v) > 0 && v[0] == '#' { // # starts a faux big int.
big, ok := new(big.Int).SetString(v[1:], 10)
if !ok {
panic(fmt.Errorf("bad test: bad big int: %q", v))
}
return big
}
return []byte(v)
case []interface{}:
new := make([]interface{}, len(v))
for i := range v {
new[i] = translateJSON(v[i])
}
return new
default:
panic(fmt.Errorf("can't handle %T", v))
}
}
// checkDecodeFromJSON decodes from s guided by exp. exp drives the
// Stream by invoking decoding operations (Uint, Big, List, ...) based
// on the type of each value. The value decoded from the RLP stream
// must match the JSON value.
func checkDecodeFromJSON(s *rlp.Stream, exp interface{}) error {
switch exp := exp.(type) {
case uint64:
i, err := s.Uint()
if err != nil {
return addStack("Uint", exp, err)
}
if i != exp {
return addStack("Uint", exp, fmt.Errorf("result mismatch: got %d", i))
}
case *big.Int:
big := new(big.Int)
if err := s.Decode(&big); err != nil {
return addStack("Big", exp, err)
}
if big.Cmp(exp) != 0 {
return addStack("Big", exp, fmt.Errorf("result mismatch: got %d", big))
}
case []byte:
b, err := s.Bytes()
if err != nil {
return addStack("Bytes", exp, err)
}
if !bytes.Equal(b, exp) {
return addStack("Bytes", exp, fmt.Errorf("result mismatch: got %x", b))
}
case []interface{}:
if _, err := s.List(); err != nil {
return addStack("List", exp, err)
}
for i, v := range exp {
if err := checkDecodeFromJSON(s, v); err != nil {
return addStack(fmt.Sprintf("[%d]", i), exp, err)
}
}
if err := s.ListEnd(); err != nil {
return addStack("ListEnd", exp, err)
}
default:
panic(fmt.Errorf("unhandled type: %T", exp))
}
return nil
}
func addStack(op string, val interface{}, err error) error {
lines := strings.Split(err.Error(), "\n")
lines = append(lines, fmt.Sprintf("\t%s: %v", op, val))
return errors.New(strings.Join(lines, "\n"))
}

View File

@ -0,0 +1,245 @@
// 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 <http://www.gnu.org/licenses/>.
package tests
import (
"encoding/hex"
"encoding/json"
"fmt"
"math/big"
"strings"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/crypto/sha3"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
// StateTest checks transaction processing without block context.
// See https://github.com/ethereum/EIPs/issues/176 for the test format specification.
type StateTest struct {
json stJSON
}
// StateSubtest selects a specific configuration of a General State Test.
type StateSubtest struct {
Fork string
Index int
}
func (t *StateTest) UnmarshalJSON(in []byte) error {
return json.Unmarshal(in, &t.json)
}
type stJSON struct {
Env stEnv `json:"env"`
Pre core.GenesisAlloc `json:"pre"`
Tx stTransaction `json:"transaction"`
Out hexutil.Bytes `json:"out"`
Post map[string][]stPostState `json:"post"`
}
type stPostState struct {
Root common.UnprefixedHash `json:"hash"`
Logs common.UnprefixedHash `json:"logs"`
Indexes struct {
Data int `json:"data"`
Gas int `json:"gas"`
Value int `json:"value"`
}
}
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct {
Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
GasLimit *big.Int `json:"currentGasLimit" gencodec:"required"`
Number uint64 `json:"currentNumber" gencodec:"required"`
Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
}
type stEnvMarshaling struct {
Coinbase common.UnprefixedAddress
Difficulty *math.HexOrDecimal256
GasLimit *math.HexOrDecimal256
Number math.HexOrDecimal64
Timestamp math.HexOrDecimal64
}
//go:generate gencodec -type stTransaction -field-override stTransactionMarshaling -out gen_sttransaction.go
type stTransaction struct {
GasPrice *big.Int `json:"gasPrice"`
Nonce uint64 `json:"nonce"`
To string `json:"to"`
Data []string `json:"data"`
GasLimit []uint64 `json:"gasLimit"`
Value []string `json:"value"`
PrivateKey []byte `json:"secretKey"`
}
type stTransactionMarshaling struct {
GasPrice *math.HexOrDecimal256
Nonce math.HexOrDecimal64
GasLimit []math.HexOrDecimal64
PrivateKey hexutil.Bytes
}
// Subtests returns all valid subtests of the test.
func (t *StateTest) Subtests() []StateSubtest {
var sub []StateSubtest
for fork, pss := range t.json.Post {
for i := range pss {
sub = append(sub, StateSubtest{fork, i})
}
}
return sub
}
// Run executes a specific subtest.
func (t *StateTest) Run(subtest StateSubtest, vmconfig vm.Config) (*state.StateDB, error) {
config, ok := Forks[subtest.Fork]
if !ok {
return nil, UnsupportedForkError{subtest.Fork}
}
block, _ := t.genesis(config).ToBlock()
db, _ := ethdb.NewMemDatabase()
statedb := makePreState(db, t.json.Pre)
post := t.json.Post[subtest.Fork][subtest.Index]
msg, err := t.json.Tx.toMessage(post)
if err != nil {
return nil, err
}
context := core.NewEVMContext(msg, block.Header(), nil, &t.json.Env.Coinbase)
context.GetHash = vmTestBlockHash
evm := vm.NewEVM(context, statedb, config, vmconfig)
gaspool := new(core.GasPool)
gaspool.AddGas(block.GasLimit())
snapshot := statedb.Snapshot()
if _, _, _, err := core.ApplyMessage(evm, msg, gaspool); err != nil {
statedb.RevertToSnapshot(snapshot)
}
if logs := rlpHash(statedb.Logs()); logs != common.Hash(post.Logs) {
return statedb, fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, post.Logs)
}
root, _ := statedb.CommitTo(db, config.IsEIP158(block.Number()))
if root != common.Hash(post.Root) {
return statedb, fmt.Errorf("post state root mismatch: got %x, want %x", root, post.Root)
}
return statedb, nil
}
func (t *StateTest) gasLimit(subtest StateSubtest) uint64 {
return t.json.Tx.GasLimit[t.json.Post[subtest.Fork][subtest.Index].Indexes.Gas]
}
func makePreState(db ethdb.Database, accounts core.GenesisAlloc) *state.StateDB {
sdb := state.NewDatabase(db)
statedb, _ := state.New(common.Hash{}, sdb)
for addr, a := range accounts {
statedb.SetCode(addr, a.Code)
statedb.SetNonce(addr, a.Nonce)
statedb.SetBalance(addr, a.Balance)
for k, v := range a.Storage {
statedb.SetState(addr, k, v)
}
}
// Commit and re-open to start with a clean state.
root, _ := statedb.CommitTo(db, false)
statedb, _ = state.New(root, sdb)
return statedb
}
func (t *StateTest) genesis(config *params.ChainConfig) *core.Genesis {
return &core.Genesis{
Config: config,
Coinbase: t.json.Env.Coinbase,
Difficulty: t.json.Env.Difficulty,
GasLimit: t.json.Env.GasLimit.Uint64(),
Number: t.json.Env.Number,
Timestamp: t.json.Env.Timestamp,
Alloc: t.json.Pre,
}
}
func (tx *stTransaction) toMessage(ps stPostState) (core.Message, error) {
// Derive sender from private key if present.
var from common.Address
if len(tx.PrivateKey) > 0 {
key, err := crypto.ToECDSA(tx.PrivateKey)
if err != nil {
return nil, fmt.Errorf("invalid private key: %v", err)
}
from = crypto.PubkeyToAddress(key.PublicKey)
}
// Parse recipient if present.
var to *common.Address
if tx.To != "" {
to = new(common.Address)
if err := to.UnmarshalText([]byte(tx.To)); err != nil {
return nil, fmt.Errorf("invalid to address: %v", err)
}
}
// Get values specific to this post state.
if ps.Indexes.Data > len(tx.Data) {
return nil, fmt.Errorf("tx data index %d out of bounds", ps.Indexes.Data)
}
if ps.Indexes.Value > len(tx.Value) {
return nil, fmt.Errorf("tx value index %d out of bounds", ps.Indexes.Value)
}
if ps.Indexes.Gas > len(tx.GasLimit) {
return nil, fmt.Errorf("tx gas limit index %d out of bounds", ps.Indexes.Gas)
}
dataHex := tx.Data[ps.Indexes.Data]
valueHex := tx.Value[ps.Indexes.Value]
gasLimit := tx.GasLimit[ps.Indexes.Gas]
// Value, Data hex encoding is messy: https://github.com/ethereum/tests/issues/203
value := new(big.Int)
if valueHex != "0x" {
v, ok := math.ParseBig256(valueHex)
if !ok {
return nil, fmt.Errorf("invalid tx value %q", valueHex)
}
value = v
}
data, err := hex.DecodeString(strings.TrimPrefix(dataHex, "0x"))
if err != nil {
return nil, fmt.Errorf("invalid tx data %q", dataHex)
}
msg := types.NewMessage(from, to, tx.Nonce, value, new(big.Int).SetUint64(gasLimit), tx.GasPrice, data, true)
return msg, nil
}
func rlpHash(x interface{}) (h common.Hash) {
hw := sha3.NewKeccak256()
rlp.Encode(hw, x)
hw.Sum(h[:0])
return h
}

View File

@ -0,0 +1,133 @@
// 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 <http://www.gnu.org/licenses/>.
package tests
import (
"bytes"
"errors"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
)
// TransactionTest checks RLP decoding and sender derivation of transactions.
type TransactionTest struct {
json ttJSON
}
type ttJSON struct {
BlockNumber math.HexOrDecimal64 `json:"blockNumber"`
RLP hexutil.Bytes `json:"rlp"`
Sender hexutil.Bytes `json:"sender"`
Transaction *ttTransaction `json:"transaction"`
}
//go:generate gencodec -type ttTransaction -field-override ttTransactionMarshaling -out gen_tttransaction.go
type ttTransaction struct {
Data []byte `gencodec:"required"`
GasLimit *big.Int `gencodec:"required"`
GasPrice *big.Int `gencodec:"required"`
Nonce uint64 `gencodec:"required"`
Value *big.Int `gencodec:"required"`
R *big.Int `gencodec:"required"`
S *big.Int `gencodec:"required"`
V *big.Int `gencodec:"required"`
To common.Address `gencodec:"required"`
}
type ttTransactionMarshaling struct {
Data hexutil.Bytes
GasLimit *math.HexOrDecimal256
GasPrice *math.HexOrDecimal256
Nonce math.HexOrDecimal64
Value *math.HexOrDecimal256
R *math.HexOrDecimal256
S *math.HexOrDecimal256
V *math.HexOrDecimal256
}
func (tt *TransactionTest) Run(config *params.ChainConfig) error {
tx := new(types.Transaction)
if err := rlp.DecodeBytes(tt.json.RLP, tx); err != nil {
if tt.json.Transaction == nil {
return nil
} else {
return fmt.Errorf("RLP decoding failed: %v", err)
}
}
// Check sender derivation.
signer := types.MakeSigner(config, new(big.Int).SetUint64(uint64(tt.json.BlockNumber)))
sender, err := types.Sender(signer, tx)
if err != nil {
return err
}
if sender != common.BytesToAddress(tt.json.Sender) {
return fmt.Errorf("Sender mismatch: got %x, want %x", sender, tt.json.Sender)
}
// Check decoded fields.
err = tt.json.Transaction.verify(signer, tx)
if tt.json.Sender == nil && err == nil {
return errors.New("field validations succeeded but should fail")
}
if tt.json.Sender != nil && err != nil {
return fmt.Errorf("field validations failed after RLP decoding: %s", err)
}
return nil
}
func (tt *ttTransaction) verify(signer types.Signer, tx *types.Transaction) error {
if !bytes.Equal(tx.Data(), tt.Data) {
return fmt.Errorf("Tx input data mismatch: got %x want %x", tx.Data(), tt.Data)
}
if tx.Gas().Cmp(tt.GasLimit) != 0 {
return fmt.Errorf("GasLimit mismatch: got %v, want %v", tx.Gas(), tt.GasLimit)
}
if tx.GasPrice().Cmp(tt.GasPrice) != 0 {
return fmt.Errorf("GasPrice mismatch: got %v, want %v", tx.GasPrice(), tt.GasPrice)
}
if tx.Nonce() != tt.Nonce {
return fmt.Errorf("Nonce mismatch: got %v, want %v", tx.Nonce(), tt.Nonce)
}
v, r, s := tx.RawSignatureValues()
if r.Cmp(tt.R) != 0 {
return fmt.Errorf("R mismatch: got %v, want %v", r, tt.R)
}
if s.Cmp(tt.S) != 0 {
return fmt.Errorf("S mismatch: got %v, want %v", s, tt.S)
}
if v.Cmp(tt.V) != 0 {
return fmt.Errorf("V mismatch: got %v, want %v", v, tt.V)
}
if tx.To() == nil {
if tt.To != (common.Address{}) {
return fmt.Errorf("To mismatch when recipient is nil (contract creation): %x", tt.To)
}
} else if *tx.To() != tt.To {
return fmt.Errorf("To mismatch: got %x, want %x", *tx.To(), tt.To)
}
if tx.Value().Cmp(tt.Value) != 0 {
return fmt.Errorf("Value mismatch: got %x, want %x", tx.Value(), tt.Value)
}
return nil
}

View File

@ -0,0 +1,152 @@
// 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 <http://www.gnu.org/licenses/>.
package tests
import (
"bytes"
"encoding/json"
"fmt"
"math/big"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/params"
)
// VMTest checks EVM execution without block or transaction context.
// See https://github.com/ethereum/tests/wiki/VM-Tests for the test format specification.
type VMTest struct {
json vmJSON
}
func (t *VMTest) UnmarshalJSON(data []byte) error {
return json.Unmarshal(data, &t.json)
}
type vmJSON struct {
Env stEnv `json:"env"`
Exec vmExec `json:"exec"`
Logs common.UnprefixedHash `json:"logs"`
GasRemaining *math.HexOrDecimal64 `json:"gas"`
Out hexutil.Bytes `json:"out"`
Pre core.GenesisAlloc `json:"pre"`
Post core.GenesisAlloc `json:"post"`
PostStateRoot common.Hash `json:"postStateRoot"`
}
//go:generate gencodec -type vmExec -field-override vmExecMarshaling -out gen_vmexec.go
type vmExec struct {
Address common.Address `json:"address" gencodec:"required"`
Caller common.Address `json:"caller" gencodec:"required"`
Origin common.Address `json:"origin" gencodec:"required"`
Code []byte `json:"code" gencodec:"required"`
Data []byte `json:"data" gencodec:"required"`
Value *big.Int `json:"value" gencodec:"required"`
GasLimit uint64 `json:"gas" gencodec:"required"`
GasPrice *big.Int `json:"gasPrice" gencodec:"required"`
}
type vmExecMarshaling struct {
Address common.UnprefixedAddress
Caller common.UnprefixedAddress
Origin common.UnprefixedAddress
Code hexutil.Bytes
Data hexutil.Bytes
Value *math.HexOrDecimal256
GasLimit math.HexOrDecimal64
GasPrice *math.HexOrDecimal256
}
func (t *VMTest) Run(vmconfig vm.Config) error {
db, _ := ethdb.NewMemDatabase()
statedb := makePreState(db, t.json.Pre)
ret, gasRemaining, err := t.exec(statedb, vmconfig)
if t.json.GasRemaining == nil {
if err == nil {
return fmt.Errorf("gas unspecified (indicating an error), but VM returned no error")
}
if gasRemaining > 0 {
return fmt.Errorf("gas unspecified (indicating an error), but VM returned gas remaining > 0")
}
return nil
}
// Test declares gas, expecting outputs to match.
if !bytes.Equal(ret, t.json.Out) {
return fmt.Errorf("return data mismatch: got %x, want %x", ret, t.json.Out)
}
if gasRemaining != uint64(*t.json.GasRemaining) {
return fmt.Errorf("remaining gas %v, want %v", gasRemaining, *t.json.GasRemaining)
}
for addr, account := range t.json.Post {
for k, wantV := range account.Storage {
if haveV := statedb.GetState(addr, k); haveV != wantV {
return fmt.Errorf("wrong storage value at %x:\n got %x\n want %x", k, haveV, wantV)
}
}
}
// if root := statedb.IntermediateRoot(false); root != t.json.PostStateRoot {
// return fmt.Errorf("post state root mismatch, got %x, want %x", root, t.json.PostStateRoot)
// }
if logs := rlpHash(statedb.Logs()); logs != common.Hash(t.json.Logs) {
return fmt.Errorf("post state logs hash mismatch: got %x, want %x", logs, t.json.Logs)
}
return nil
}
func (t *VMTest) exec(statedb *state.StateDB, vmconfig vm.Config) ([]byte, uint64, error) {
evm := t.newEVM(statedb, vmconfig)
e := t.json.Exec
return evm.Call(vm.AccountRef(e.Caller), e.Address, e.Data, e.GasLimit, e.Value)
}
func (t *VMTest) newEVM(statedb *state.StateDB, vmconfig vm.Config) *vm.EVM {
initialCall := true
canTransfer := func(db vm.StateDB, address common.Address, amount *big.Int) bool {
if initialCall {
initialCall = false
return true
}
return core.CanTransfer(db, address, amount)
}
transfer := func(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {}
context := vm.Context{
CanTransfer: canTransfer,
Transfer: transfer,
GetHash: vmTestBlockHash,
Origin: t.json.Exec.Origin,
Coinbase: t.json.Env.Coinbase,
BlockNumber: new(big.Int).SetUint64(t.json.Env.Number),
Time: new(big.Int).SetUint64(t.json.Env.Timestamp),
GasLimit: t.json.Env.GasLimit,
Difficulty: t.json.Env.Difficulty,
GasPrice: t.json.Exec.GasPrice,
}
vmconfig.NoRecursion = true
return vm.NewEVM(context, statedb, params.MainnetChainConfig, vmconfig)
}
func vmTestBlockHash(n uint64) common.Hash {
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
}