mirror of
https://github.com/arnaucube/go-ethereum.git
synced 2026-03-04 08:04:50 +01:00
Compare commits
39 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
81d9d7d385 | ||
|
|
822355f8a6 | ||
|
|
7d9c6f611a | ||
|
|
dc2e34ddf3 | ||
|
|
ef9265d0d7 | ||
|
|
ac0c5dd77f | ||
|
|
9fa42b1e02 | ||
|
|
0718ebbbce | ||
|
|
843073d453 | ||
|
|
33a6e52aef | ||
|
|
18c8ded42c | ||
|
|
00ba748707 | ||
|
|
8d81eb9999 | ||
|
|
2425a748ff | ||
|
|
facfe40528 | ||
|
|
5a6008e004 | ||
|
|
abaa56fea5 | ||
|
|
a79977bc20 | ||
|
|
794d2eb582 | ||
|
|
d7721def26 | ||
|
|
ddcf02b119 | ||
|
|
ff697e82dc | ||
|
|
df64a9f5ef | ||
|
|
4fced0972d | ||
|
|
18971d9cb4 | ||
|
|
74d5251b70 | ||
|
|
46a527d014 | ||
|
|
e97b30169b | ||
|
|
438efdab28 | ||
|
|
f7e6fb7d1c | ||
|
|
718881bd35 | ||
|
|
d1bb89d46d | ||
|
|
1639f1174e | ||
|
|
d9ed63ec38 | ||
|
|
ee58202f2f | ||
|
|
9315bc9c3c | ||
|
|
945bcb8293 | ||
|
|
f1949f4d99 | ||
|
|
968ab8aa4f |
@@ -27,7 +27,7 @@ matrix:
|
||||
- debhelper
|
||||
- dput
|
||||
script:
|
||||
- go run build/ci.go travis-debsrc
|
||||
- go run build/ci.go debsrc -signer "Felix Lange (Geth CI Testing Key) <fjl@twurst.com>" -upload ppa:lp-fjl/geth-ci-testing
|
||||
|
||||
install:
|
||||
- go get golang.org/x/tools/cmd/cover
|
||||
|
||||
28
Makefile
28
Makefile
@@ -42,12 +42,12 @@ geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 get
|
||||
@ls -ld $(GOBIN)/geth-linux-*
|
||||
|
||||
geth-linux-386:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/386 -v ./cmd/geth
|
||||
@echo "Linux 386 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep 386
|
||||
|
||||
geth-linux-amd64:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/amd64 -v ./cmd/geth
|
||||
@echo "Linux amd64 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep amd64
|
||||
|
||||
@@ -56,32 +56,32 @@ geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-ar
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm
|
||||
|
||||
geth-linux-arm-5:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-5 -v ./cmd/geth
|
||||
@echo "Linux ARMv5 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-5
|
||||
|
||||
geth-linux-arm-6:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-6 -v ./cmd/geth
|
||||
@echo "Linux ARMv6 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-6
|
||||
|
||||
geth-linux-arm-7:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm-7 -v ./cmd/geth
|
||||
@echo "Linux ARMv7 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm-7
|
||||
|
||||
geth-linux-arm64:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/arm64 -v ./cmd/geth
|
||||
@echo "Linux ARM64 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep arm64
|
||||
|
||||
geth-linux-mips64:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64 -v ./cmd/geth
|
||||
@echo "Linux MIPS64 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep mips64
|
||||
|
||||
geth-linux-mips64le:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=linux/mips64le -v ./cmd/geth
|
||||
@echo "Linux MIPS64le cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-linux-* | grep mips64le
|
||||
|
||||
@@ -90,12 +90,12 @@ geth-darwin: geth-darwin-386 geth-darwin-amd64
|
||||
@ls -ld $(GOBIN)/geth-darwin-*
|
||||
|
||||
geth-darwin-386:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/386 -v ./cmd/geth
|
||||
@echo "Darwin 386 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-darwin-* | grep 386
|
||||
|
||||
geth-darwin-amd64:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=darwin/amd64 -v ./cmd/geth
|
||||
@echo "Darwin amd64 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-darwin-* | grep amd64
|
||||
|
||||
@@ -104,21 +104,21 @@ geth-windows: geth-windows-386 geth-windows-amd64
|
||||
@ls -ld $(GOBIN)/geth-windows-*
|
||||
|
||||
geth-windows-386:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/386 -v ./cmd/geth
|
||||
@echo "Windows 386 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-windows-* | grep 386
|
||||
|
||||
geth-windows-amd64:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=windows/amd64 -v ./cmd/geth
|
||||
@echo "Windows amd64 cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-windows-* | grep amd64
|
||||
|
||||
geth-android:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=android-21/aar -v ./cmd/geth
|
||||
@echo "Android cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-android-*
|
||||
|
||||
geth-ios:
|
||||
build/env.sh go run build/ci.go xgo --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth
|
||||
build/env.sh go run build/ci.go xgo -- --go=$(GO) --dest=$(GOBIN) --targets=ios-7.0/framework -v ./cmd/geth
|
||||
@echo "iOS framework cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-ios-*
|
||||
|
||||
@@ -27,10 +27,11 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Default chain configuration which sets homestead phase at block 0 (i.e. no frontier)
|
||||
var chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)}
|
||||
var chainConfig = ¶ms.ChainConfig{ChainId: new(big.Int), HomesteadBlock: big.NewInt(0), EIP150Block: new(big.Int), EIP158Block: new(big.Int)}
|
||||
|
||||
// This nil assignment ensures compile time that SimulatedBackend implements bind.ContractBackend.
|
||||
var _ bind.ContractBackend = (*SimulatedBackend)(nil)
|
||||
@@ -72,8 +73,7 @@ func (b *SimulatedBackend) Commit() {
|
||||
|
||||
// Rollback aborts all pending transactions, reverting to the last committed state.
|
||||
func (b *SimulatedBackend) Rollback() {
|
||||
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||
|
||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(int, *core.BlockGen) {})
|
||||
b.pendingBlock = blocks[0]
|
||||
b.pendingState, _ = state.New(b.pendingBlock.Root(), b.database)
|
||||
}
|
||||
@@ -97,7 +97,8 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
|
||||
statedb *state.StateDB
|
||||
)
|
||||
if pending {
|
||||
block, statedb = b.pendingBlock, b.pendingState.Copy()
|
||||
block, statedb = b.pendingBlock, b.pendingState
|
||||
defer statedb.RevertToSnapshot(statedb.Snapshot())
|
||||
} else {
|
||||
block = b.blockchain.CurrentBlock()
|
||||
statedb, _ = b.blockchain.State()
|
||||
@@ -119,6 +120,7 @@ func (b *SimulatedBackend) ContractCall(contract common.Address, data []byte, pe
|
||||
value: new(big.Int),
|
||||
data: data,
|
||||
}
|
||||
|
||||
// Execute the call and return
|
||||
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
||||
@@ -146,8 +148,10 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
|
||||
// Create a copy of the currently pending state db to screw around with
|
||||
var (
|
||||
block = b.pendingBlock
|
||||
statedb = b.pendingState.Copy()
|
||||
statedb = b.pendingState
|
||||
)
|
||||
defer statedb.RevertToSnapshot(statedb.Snapshot())
|
||||
|
||||
// If there's no code to interact with, respond with an appropriate error
|
||||
if contract != nil {
|
||||
if code := statedb.GetCode(*contract); len(code) == 0 {
|
||||
@@ -178,7 +182,7 @@ func (b *SimulatedBackend) EstimateGasLimit(sender common.Address, contract *com
|
||||
// SendTransaction implements ContractTransactor.SendTransaction, delegating the raw
|
||||
// transaction injection to the remote node.
|
||||
func (b *SimulatedBackend) SendTransaction(tx *types.Transaction) error {
|
||||
blocks, _ := core.GenerateChain(nil, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
blocks, _ := core.GenerateChain(chainConfig, b.blockchain.CurrentBlock(), b.database, 1, func(number int, block *core.BlockGen) {
|
||||
for _, tx := range b.pendingBlock.Transactions() {
|
||||
block.AddTx(tx)
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
Debian Packaging
|
||||
----------------
|
||||
# Debian Packaging
|
||||
|
||||
Tagged releases and develop branch commits are available as installable Debian packages
|
||||
for Ubuntu. Packages are built for the all Ubuntu versions which are supported by
|
||||
@@ -8,6 +7,7 @@ Canonical:
|
||||
- Trusty Tahr (14.04 LTS)
|
||||
- Wily Werewolf (15.10)
|
||||
- Xenial Xerus (16.04 LTS)
|
||||
- Yakkety Yak (16.10)
|
||||
|
||||
Packages of develop branch commits have suffix -unstable and cannot be installed alongside
|
||||
the stable version. Switching between release streams requires user intervention.
|
||||
@@ -21,6 +21,29 @@ variable which Travis CI makes available to certain builds.
|
||||
We want to build go-ethereum with the most recent version of Go, irrespective of the Go
|
||||
version that is available in the main Ubuntu repository. In order to make this possible,
|
||||
our PPA depends on the ~gophers/ubuntu/archive PPA. Our source package build-depends on
|
||||
golang-1.6, which is co-installable alongside the regular golang package. PPA dependencies
|
||||
golang-1.7, which is co-installable alongside the regular golang package. PPA dependencies
|
||||
can be edited at https://launchpad.net/%7Elp-fjl/+archive/ubuntu/geth-ci-testing/+edit-dependencies
|
||||
|
||||
## Building Packages Locally (for testing)
|
||||
|
||||
You need to run Ubuntu to do test packaging.
|
||||
|
||||
Add the gophers PPA and install Go 1.7 and Debian packaging tools:
|
||||
|
||||
$ sudo apt-add-repository ppa:gophers/ubuntu/archive
|
||||
$ sudo apt-get update
|
||||
$ sudo apt-get install build-essential golang-1.7 devscripts debhelper
|
||||
|
||||
Create the source packages:
|
||||
|
||||
$ go run build/ci.go debsrc -workdir dist
|
||||
|
||||
Then go into the source package directory for your running distribution and build the package:
|
||||
|
||||
$ cd dist/ethereum-unstable-1.5.0+xenial
|
||||
$ dpkg-buildpackage
|
||||
|
||||
Built packages are placed in the dist/ directory.
|
||||
|
||||
$ cd ..
|
||||
$ dpkg-deb -c geth-unstable_1.5.0+xenial_amd64.deb
|
||||
|
||||
189
build/ci.go
189
build/ci.go
@@ -120,8 +120,6 @@ func main() {
|
||||
doArchive(os.Args[2:])
|
||||
case "debsrc":
|
||||
doDebianSource(os.Args[2:])
|
||||
case "travis-debsrc":
|
||||
doTravisDebianSource(os.Args[2:])
|
||||
case "xgo":
|
||||
doXgo(os.Args[2:])
|
||||
default:
|
||||
@@ -132,8 +130,8 @@ func main() {
|
||||
// Compiling
|
||||
|
||||
func doInstall(cmdline []string) {
|
||||
commitHash := flag.String("gitcommit", "", "Git commit hash embedded into binary.")
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
|
||||
// Check Go version. People regularly open issues about compilation
|
||||
// failure with outdated Go. This should save them the trouble.
|
||||
@@ -150,13 +148,17 @@ func doInstall(cmdline []string) {
|
||||
packages = flag.Args()
|
||||
}
|
||||
|
||||
goinstall := goTool("install", makeBuildFlags(*commitHash)...)
|
||||
goinstall := goTool("install", buildFlags(env)...)
|
||||
goinstall.Args = append(goinstall.Args, "-v")
|
||||
goinstall.Args = append(goinstall.Args, packages...)
|
||||
build.MustRun(goinstall)
|
||||
}
|
||||
|
||||
func makeBuildFlags(commitHash string) (flags []string) {
|
||||
func buildFlags(env build.Environment) (flags []string) {
|
||||
if os.Getenv("GO_OPENCL") != "" {
|
||||
flags = append(flags, "-tags", "opencl")
|
||||
}
|
||||
|
||||
// Since Go 1.5, the separator char for link time assignments
|
||||
// is '=' and using ' ' prints a warning. However, Go < 1.5 does
|
||||
// not support using '='.
|
||||
@@ -164,26 +166,9 @@ func makeBuildFlags(commitHash string) (flags []string) {
|
||||
if runtime.Version() > "go1.5" || strings.Contains(runtime.Version(), "devel") {
|
||||
sep = "="
|
||||
}
|
||||
|
||||
if os.Getenv("GO_OPENCL") != "" {
|
||||
flags = append(flags, "-tags", "opencl")
|
||||
}
|
||||
|
||||
// Set gitCommit constant via link-time assignment. If this is a git checkout, we can
|
||||
// just get the current commit hash through git. Otherwise we fall back to the hash
|
||||
// that was passed as -gitcommit.
|
||||
//
|
||||
// -gitcommit is required for Debian package builds. The source package doesn't
|
||||
// contain .git but we still want to embed the commit hash into the packaged binary.
|
||||
// The hash is rendered into the debian/rules build script when the source package is
|
||||
// created.
|
||||
if _, err := os.Stat(filepath.Join(".git", "HEAD")); !os.IsNotExist(err) {
|
||||
if c := build.GitCommit(); c != "" {
|
||||
commitHash = c
|
||||
}
|
||||
}
|
||||
if commitHash != "" {
|
||||
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+commitHash)
|
||||
// Set gitCommit constant via link-time assignment.
|
||||
if env.Commit != "" {
|
||||
flags = append(flags, "-ldflags", "-X main.gitCommit"+sep+env.Commit)
|
||||
}
|
||||
return flags
|
||||
}
|
||||
@@ -253,7 +238,11 @@ func doArchive(cmdline []string) {
|
||||
default:
|
||||
log.Fatal("unknown archive type: ", atype)
|
||||
}
|
||||
base := makeArchiveBasename()
|
||||
|
||||
env := build.Env()
|
||||
maybeSkipArchive(env)
|
||||
|
||||
base := archiveBasename(env)
|
||||
if err := build.WriteArchive("geth-"+base, ext, gethArchiveFiles); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
@@ -262,36 +251,41 @@ func doArchive(cmdline []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func makeArchiveBasename() string {
|
||||
func archiveBasename(env build.Environment) string {
|
||||
// date := time.Now().UTC().Format("200601021504")
|
||||
platform := runtime.GOOS + "-" + runtime.GOARCH
|
||||
archive := platform + "-" + build.VERSION()
|
||||
if commit := build.GitCommit(); commit != "" {
|
||||
archive += "-" + commit[:8]
|
||||
if env.Commit != "" {
|
||||
archive += "-" + env.Commit[:8]
|
||||
}
|
||||
return archive
|
||||
}
|
||||
|
||||
// skips archiving for some build configurations.
|
||||
func maybeSkipArchive(env build.Environment) {
|
||||
if env.IsPullRequest {
|
||||
log.Printf("skipping because this is a PR build")
|
||||
os.Exit(0)
|
||||
}
|
||||
if env.Branch != "develop" && !strings.HasPrefix(env.Tag, "v1.") {
|
||||
log.Printf("skipping because branch %q, tag %q is not on the whitelist", env.Branch, env.Tag)
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Debian Packaging
|
||||
|
||||
// CLI entry point for Travis CI.
|
||||
func doTravisDebianSource(cmdline []string) {
|
||||
func doDebianSource(cmdline []string) {
|
||||
var (
|
||||
signer = flag.String("signer", "", `Signing key name, also used as package author`)
|
||||
upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`)
|
||||
workdir = flag.String("workdir", "", `Output directory for packages (uses temp dir if unset)`)
|
||||
now = time.Now()
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
// Package only whitelisted branches.
|
||||
switch {
|
||||
case os.Getenv("TRAVIS_REPO_SLUG") != "ethereum/go-ethereum":
|
||||
log.Printf("skipping because this is a fork build")
|
||||
return
|
||||
case os.Getenv("TRAVIS_PULL_REQUEST") != "false":
|
||||
log.Printf("skipping because this is a PR build")
|
||||
return
|
||||
case os.Getenv("TRAVIS_BRANCH") != "develop" && !strings.HasPrefix(os.Getenv("TRAVIS_TAG"), "v1."):
|
||||
log.Printf("skipping because branch %q tag %q is not on the whitelist",
|
||||
os.Getenv("TRAVIS_BRANCH"),
|
||||
os.Getenv("TRAVIS_TAG"))
|
||||
return
|
||||
}
|
||||
*workdir = makeWorkdir(*workdir)
|
||||
env := build.Env()
|
||||
maybeSkipArchive(env)
|
||||
|
||||
// Import the signing key.
|
||||
if b64key := os.Getenv("PPA_SIGNING_KEY"); b64key != "" {
|
||||
@@ -304,46 +298,16 @@ func doTravisDebianSource(cmdline []string) {
|
||||
build.MustRun(gpg)
|
||||
}
|
||||
|
||||
// Assign unstable status to non-tag builds.
|
||||
unstable := "true"
|
||||
if os.Getenv("TRAVIS_BRANCH") != "develop" && os.Getenv("TRAVIS_TAG") != "" {
|
||||
unstable = "false"
|
||||
}
|
||||
|
||||
doDebianSource([]string{
|
||||
"-signer", "Felix Lange (Geth CI Testing Key) <fjl@twurst.com>",
|
||||
"-buildnum", os.Getenv("TRAVIS_BUILD_NUMBER"),
|
||||
"-upload", "ppa:lp-fjl/geth-ci-testing",
|
||||
"-unstable", unstable,
|
||||
})
|
||||
}
|
||||
|
||||
// CLI entry point for doing packaging locally.
|
||||
func doDebianSource(cmdline []string) {
|
||||
var (
|
||||
signer = flag.String("signer", "", `Signing key name, also used as package author`)
|
||||
upload = flag.String("upload", "", `Where to upload the source package (usually "ppa:ethereum/ethereum")`)
|
||||
buildnum = flag.String("buildnum", "", `Build number (included in version)`)
|
||||
unstable = flag.Bool("unstable", false, `Use package name suffix "-unstable"`)
|
||||
now = time.Now()
|
||||
)
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
|
||||
// Create the debian worktree in /tmp.
|
||||
tmpdir, err := ioutil.TempDir("", "eth-deb-build-")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Create the packages.
|
||||
for _, distro := range debDistros {
|
||||
meta := newDebMetadata(distro, *signer, *buildnum, *unstable, now)
|
||||
pkgdir := stageDebianSource(tmpdir, meta)
|
||||
meta := newDebMetadata(distro, *signer, env, now)
|
||||
pkgdir := stageDebianSource(*workdir, meta)
|
||||
debuild := exec.Command("debuild", "-S", "-sa", "-us", "-uc")
|
||||
debuild.Dir = pkgdir
|
||||
build.MustRun(debuild)
|
||||
|
||||
changes := fmt.Sprintf("%s_%s_source.changes", meta.Name(), meta.VersionString())
|
||||
changes = filepath.Join(tmpdir, changes)
|
||||
changes = filepath.Join(*workdir, changes)
|
||||
if *signer != "" {
|
||||
build.MustRunCommand("debsign", changes)
|
||||
}
|
||||
@@ -353,35 +317,53 @@ func doDebianSource(cmdline []string) {
|
||||
}
|
||||
}
|
||||
|
||||
type debExecutable struct {
|
||||
Name, Description string
|
||||
func makeWorkdir(wdflag string) string {
|
||||
var err error
|
||||
if wdflag != "" {
|
||||
err = os.MkdirAll(wdflag, 0744)
|
||||
} else {
|
||||
wdflag, err = ioutil.TempDir("", "eth-deb-build-")
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
return wdflag
|
||||
}
|
||||
|
||||
func isUnstableBuild(env build.Environment) bool {
|
||||
if env.Branch != "develop" && env.Tag != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
type debMetadata struct {
|
||||
Env build.Environment
|
||||
|
||||
// go-ethereum version being built. Note that this
|
||||
// is not the debian package version. The package version
|
||||
// is constructed by VersionString.
|
||||
Version string
|
||||
|
||||
Author string // "name <email>", also selects signing key
|
||||
Buildnum string // build number
|
||||
Distro, Commit, Time string
|
||||
Executables []debExecutable
|
||||
Unstable bool
|
||||
Author string // "name <email>", also selects signing key
|
||||
Distro, Time string
|
||||
Executables []debExecutable
|
||||
}
|
||||
|
||||
func newDebMetadata(distro, author, buildnum string, unstable bool, t time.Time) debMetadata {
|
||||
type debExecutable struct {
|
||||
Name, Description string
|
||||
}
|
||||
|
||||
func newDebMetadata(distro, author string, env build.Environment, t time.Time) debMetadata {
|
||||
if author == "" {
|
||||
// No signing key, use default author.
|
||||
author = "Ethereum Builds <fjl@ethereum.org>"
|
||||
}
|
||||
return debMetadata{
|
||||
Unstable: unstable,
|
||||
Env: env,
|
||||
Author: author,
|
||||
Distro: distro,
|
||||
Commit: build.GitCommit(),
|
||||
Version: build.VERSION(),
|
||||
Buildnum: buildnum,
|
||||
Time: t.Format(time.RFC1123Z),
|
||||
Executables: debExecutables,
|
||||
}
|
||||
@@ -390,7 +372,7 @@ func newDebMetadata(distro, author, buildnum string, unstable bool, t time.Time)
|
||||
// Name returns the name of the metapackage that depends
|
||||
// on all executable packages.
|
||||
func (meta debMetadata) Name() string {
|
||||
if meta.Unstable {
|
||||
if isUnstableBuild(meta.Env) {
|
||||
return "ethereum-unstable"
|
||||
}
|
||||
return "ethereum"
|
||||
@@ -399,8 +381,8 @@ func (meta debMetadata) Name() string {
|
||||
// VersionString returns the debian version of the packages.
|
||||
func (meta debMetadata) VersionString() string {
|
||||
vsn := meta.Version
|
||||
if meta.Buildnum != "" {
|
||||
vsn += "+build" + meta.Buildnum
|
||||
if meta.Env.Buildnum != "" {
|
||||
vsn += "+build" + meta.Env.Buildnum
|
||||
}
|
||||
if meta.Distro != "" {
|
||||
vsn += "+" + meta.Distro
|
||||
@@ -419,7 +401,7 @@ func (meta debMetadata) ExeList() string {
|
||||
|
||||
// ExeName returns the package name of an executable package.
|
||||
func (meta debMetadata) ExeName(exe debExecutable) string {
|
||||
if meta.Unstable {
|
||||
if isUnstableBuild(meta.Env) {
|
||||
return exe.Name + "-unstable"
|
||||
}
|
||||
return exe.Name
|
||||
@@ -428,7 +410,7 @@ func (meta debMetadata) ExeName(exe debExecutable) string {
|
||||
// ExeConflicts returns the content of the Conflicts field
|
||||
// for executable packages.
|
||||
func (meta debMetadata) ExeConflicts(exe debExecutable) string {
|
||||
if meta.Unstable {
|
||||
if isUnstableBuild(meta.Env) {
|
||||
// Set up the conflicts list so that the *-unstable packages
|
||||
// cannot be installed alongside the regular version.
|
||||
//
|
||||
@@ -461,8 +443,8 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
|
||||
build.RenderString("8\n", filepath.Join(debian, "compat"), 0644, meta)
|
||||
build.RenderString("3.0 (native)\n", filepath.Join(debian, "source/format"), 0644, meta)
|
||||
for _, exe := range meta.Executables {
|
||||
install := filepath.Join(debian, exe.Name+".install")
|
||||
docs := filepath.Join(debian, exe.Name+".docs")
|
||||
install := filepath.Join(debian, meta.ExeName(exe)+".install")
|
||||
docs := filepath.Join(debian, meta.ExeName(exe)+".docs")
|
||||
build.Render("build/deb.install", install, 0644, exe)
|
||||
build.Render("build/deb.docs", docs, 0644, exe)
|
||||
}
|
||||
@@ -473,18 +455,19 @@ func stageDebianSource(tmpdir string, meta debMetadata) (pkgdir string) {
|
||||
// Cross compilation
|
||||
|
||||
func doXgo(cmdline []string) {
|
||||
flag.CommandLine.Parse(cmdline)
|
||||
env := build.Env()
|
||||
|
||||
// Make sure xgo is available for cross compilation
|
||||
gogetxgo := goTool("get", "github.com/karalabe/xgo")
|
||||
build.MustRun(gogetxgo)
|
||||
|
||||
// Execute the actual cross compilation
|
||||
pkg := cmdline[len(cmdline)-1]
|
||||
args := append(cmdline[:len(cmdline)-1], makeBuildFlags("")...)
|
||||
|
||||
build.MustRun(xgoTool(append(args, pkg)...))
|
||||
xgo := xgoTool(append(buildFlags(env), flag.Args()...))
|
||||
build.MustRun(xgo)
|
||||
}
|
||||
|
||||
func xgoTool(args ...string) *exec.Cmd {
|
||||
func xgoTool(args []string) *exec.Cmd {
|
||||
cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
|
||||
cmd.Env = []string{
|
||||
"GOPATH=" + build.GOPATH(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
{{.Name}} ({{.VersionString}}) {{.Distro}}; urgency=low
|
||||
|
||||
* git build of {{.Commit}}
|
||||
* git build of {{.Env.Commit}}
|
||||
|
||||
-- {{.Author}} {{.Time}}
|
||||
|
||||
@@ -2,7 +2,7 @@ Source: {{.Name}}
|
||||
Section: science
|
||||
Priority: extra
|
||||
Maintainer: {{.Author}}
|
||||
Build-Depends: debhelper (>= 8.0.0), golang-1.6
|
||||
Build-Depends: debhelper (>= 8.0.0), golang-1.7
|
||||
Standards-Version: 3.9.5
|
||||
Homepage: https://ethereum.org
|
||||
Vcs-Git: git://github.com/ethereum/go-ethereum.git
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#export DH_VERBOSE=1
|
||||
|
||||
override_dh_auto_build:
|
||||
build/env.sh /usr/lib/go-1.6/bin/go run build/ci.go install -gitcommit {{.Commit}}
|
||||
build/env.sh /usr/lib/go-1.7/bin/go run build/ci.go install -git-commit={{.Env.Commit}} -git-branch={{.Env.Branch}} -git-tag={{.Env.Tag}} -buildnum={{.Env.Buildnum}} -pull-request={{.Env.IsPullRequest}}
|
||||
|
||||
override_dh_auto_test:
|
||||
|
||||
|
||||
@@ -74,12 +74,13 @@ func runTestWithReader(test string, r io.Reader) error {
|
||||
var err error
|
||||
switch strings.ToLower(test) {
|
||||
case "bk", "block", "blocktest", "blockchaintest", "blocktests", "blockchaintests":
|
||||
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, r, skipTests)
|
||||
err = tests.RunBlockTestWithReader(params.MainNetHomesteadBlock, params.MainNetDAOForkBlock, params.MainNetHomesteadGasRepriceBlock, r, skipTests)
|
||||
case "st", "state", "statetest", "statetests":
|
||||
rs := tests.RuleSet{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true}
|
||||
rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
|
||||
err = tests.RunStateTestWithReader(rs, r, skipTests)
|
||||
case "tx", "transactiontest", "transactiontests":
|
||||
err = tests.RunTransactionTestsWithReader(r, skipTests)
|
||||
rs := ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock, DAOForkBlock: params.MainNetDAOForkBlock, DAOForkSupport: true, EIP150Block: params.MainNetHomesteadGasRepriceBlock}
|
||||
err = tests.RunTransactionTestsWithReader(rs, r, skipTests)
|
||||
case "vm", "vmtest", "vmtests":
|
||||
err = tests.RunVmTestWithReader(r, skipTests)
|
||||
case "rlp", "rlptest", "rlptests":
|
||||
|
||||
@@ -30,8 +30,10 @@ import (
|
||||
"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/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
@@ -141,7 +143,9 @@ func run(ctx *cli.Context) error {
|
||||
)
|
||||
} else {
|
||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
||||
receiver.SetCode(common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name)))
|
||||
|
||||
code := common.Hex2Bytes(ctx.GlobalString(CodeFlag.Name))
|
||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||
ret, err = vmenv.Call(
|
||||
sender,
|
||||
receiver.Address(),
|
||||
@@ -154,7 +158,7 @@ func run(ctx *cli.Context) error {
|
||||
vmdone := time.Since(tstart)
|
||||
|
||||
if ctx.GlobalBool(DumpFlag.Name) {
|
||||
statedb.Commit()
|
||||
statedb.Commit(true)
|
||||
fmt.Println(string(statedb.Dump()))
|
||||
}
|
||||
vm.StdErrFormat(vmenv.StructLogs())
|
||||
@@ -215,27 +219,30 @@ func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg
|
||||
return env
|
||||
}
|
||||
|
||||
// ruleSet implements vm.RuleSet and will always default to the homestead rule set.
|
||||
// ruleSet implements vm.ChainConfig and will always default to the homestead rule set.
|
||||
type ruleSet struct{}
|
||||
|
||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||
func (ruleSet) GasTable(*big.Int) params.GasTable {
|
||||
return params.GasTableHomesteadGasRepriceFork
|
||||
}
|
||||
|
||||
func (self *VMEnv) RuleSet() vm.RuleSet { return ruleSet{} }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) MakeSnapshot() vm.Database { return self.state.Copy() }
|
||||
func (self *VMEnv) SetSnapshot(db vm.Database) { self.state.Set(db.(*state.StateDB)) }
|
||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) ChainConfig() *params.ChainConfig { return params.TestChainConfig }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) SnapshotDatabase() int { return self.state.Snapshot() }
|
||||
func (self *VMEnv) RevertToSnapshot(snap int) { self.state.RevertToSnapshot(snap) }
|
||||
func (self *VMEnv) Origin() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return common.Big0 }
|
||||
func (self *VMEnv) Coinbase() common.Address { return *self.transactor }
|
||||
func (self *VMEnv) Time() *big.Int { return self.time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return common.Big1 }
|
||||
func (self *VMEnv) BlockHash() []byte { return make([]byte, 32) }
|
||||
func (self *VMEnv) Value() *big.Int { return self.value }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return big.NewInt(1000000000) }
|
||||
func (self *VMEnv) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *VMEnv) Depth() int { return 0 }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
||||
if self.block.Number().Cmp(big.NewInt(int64(n))) == 0 {
|
||||
return self.block.Hash()
|
||||
|
||||
@@ -47,11 +47,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
clientIdentifier = "Geth" // Client identifier to advertise over the network
|
||||
versionMajor = 1 // Major version component of the current release
|
||||
versionMinor = 4 // Minor version component of the current release
|
||||
versionPatch = 14 // Patch version component of the current release
|
||||
versionMeta = "prerelease" // Version metadata to append to the version string
|
||||
clientIdentifier = "Geth" // Client identifier to advertise over the network
|
||||
versionMajor = 1 // Major version component of the current release
|
||||
versionMinor = 4 // Minor version component of the current release
|
||||
versionPatch = 19 // Patch version component of the current release
|
||||
versionMeta = "stable" // Version metadata to append to the version string
|
||||
|
||||
versionOracle = "0xfa7b9770ca4cb04296cac84f37736d4041251cdf" // Ethereum address of the Geth release oracle
|
||||
)
|
||||
|
||||
@@ -26,7 +26,6 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -130,8 +129,7 @@ func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node
|
||||
ethConf := ð.Config{
|
||||
TestGenesisState: db,
|
||||
TestGenesisBlock: test.Genesis,
|
||||
ChainConfig: &core.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
|
||||
AccountManager: accman,
|
||||
ChainConfig: ¶ms.ChainConfig{HomesteadBlock: params.MainNetHomesteadBlock},
|
||||
}
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) { return eth.New(ctx, ethConf) }); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -22,13 +22,11 @@ import (
|
||||
"io/ioutil"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
@@ -667,17 +665,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
|
||||
}
|
||||
// Configure the Ethereum service
|
||||
accman := MakeAccountManager(ctx)
|
||||
|
||||
// initialise new random number generator
|
||||
rand := rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
// get enabled jit flag
|
||||
jitEnabled := ctx.GlobalBool(VMEnableJitFlag.Name)
|
||||
// if the jit is not enabled enable it for 10 pct of the people
|
||||
if !jitEnabled && rand.Float64() < 0.1 {
|
||||
jitEnabled = true
|
||||
glog.V(logger.Info).Infoln("You're one of the lucky few that will try out the JIT VM (random). If you get a consensus failure please be so kind to report this incident with the block hash that failed. You can switch to the regular VM by setting --jitvm=false")
|
||||
}
|
||||
|
||||
ethConf := ð.Config{
|
||||
ChainConfig: MustMakeChainConfig(ctx),
|
||||
FastSync: ctx.GlobalBool(FastSyncFlag.Name),
|
||||
@@ -781,7 +769,7 @@ func SetupNetwork(ctx *cli.Context) {
|
||||
}
|
||||
|
||||
// MustMakeChainConfig reads the chain configuration from the database in ctx.Datadir.
|
||||
func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
|
||||
func MustMakeChainConfig(ctx *cli.Context) *params.ChainConfig {
|
||||
db := MakeChainDatabase(ctx)
|
||||
defer db.Close()
|
||||
|
||||
@@ -789,9 +777,9 @@ func MustMakeChainConfig(ctx *cli.Context) *core.ChainConfig {
|
||||
}
|
||||
|
||||
// MustMakeChainConfigFromDb reads the chain configuration from the given database.
|
||||
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainConfig {
|
||||
func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainConfig {
|
||||
// If the chain is already initialized, use any existing chain configs
|
||||
config := new(core.ChainConfig)
|
||||
config := new(params.ChainConfig)
|
||||
|
||||
genesis := core.GetBlock(db, core.GetCanonicalHash(db, 0))
|
||||
if genesis != nil {
|
||||
@@ -805,6 +793,9 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
|
||||
Fatalf("Could not make chain configuration: %v", err)
|
||||
}
|
||||
}
|
||||
if config.ChainId == nil {
|
||||
config.ChainId = new(big.Int)
|
||||
}
|
||||
// Set any missing fields due to them being unset or system upgrade
|
||||
if config.HomesteadBlock == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
@@ -821,6 +812,41 @@ func MustMakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *core.ChainC
|
||||
}
|
||||
config.DAOForkSupport = true
|
||||
}
|
||||
if config.EIP150Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Block = params.TestNetHomesteadGasRepriceBlock
|
||||
} else {
|
||||
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
|
||||
}
|
||||
}
|
||||
if config.EIP150Hash == (common.Hash{}) {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Hash = params.TestNetHomesteadGasRepriceHash
|
||||
} else {
|
||||
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
|
||||
}
|
||||
}
|
||||
if config.EIP155Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Block = params.TestNetSpuriousDragon
|
||||
} else {
|
||||
config.EIP155Block = params.MainNetSpuriousDragon
|
||||
}
|
||||
}
|
||||
if config.EIP158Block == nil {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP158Block = params.TestNetSpuriousDragon
|
||||
} else {
|
||||
config.EIP158Block = params.MainNetSpuriousDragon
|
||||
}
|
||||
}
|
||||
if config.ChainId.BitLen() == 0 {
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.ChainId = params.TestNetChainID
|
||||
} else {
|
||||
config.ChainId = params.MainNetChainID
|
||||
}
|
||||
}
|
||||
// Force override any existing configs if explicitly requested
|
||||
switch {
|
||||
case ctx.GlobalBool(SupportDAOFork.Name):
|
||||
|
||||
@@ -32,11 +32,12 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// registryAPIBackend is a backend for an Ethereum Registry.
|
||||
type registryAPIBackend struct {
|
||||
config *core.ChainConfig
|
||||
config *params.ChainConfig
|
||||
bc *core.BlockChain
|
||||
chainDb ethdb.Database
|
||||
txPool *core.TxPool
|
||||
@@ -45,12 +46,12 @@ type registryAPIBackend struct {
|
||||
|
||||
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
|
||||
type PrivateRegistarAPI struct {
|
||||
config *core.ChainConfig
|
||||
config *params.ChainConfig
|
||||
be *registryAPIBackend
|
||||
}
|
||||
|
||||
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
|
||||
func NewPrivateRegistarAPI(config *core.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
|
||||
func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
|
||||
return &PrivateRegistarAPI{
|
||||
config: config,
|
||||
be: ®istryAPIBackend{
|
||||
|
||||
@@ -30,10 +30,10 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/jsre"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -100,10 +100,10 @@ func newTester(t *testing.T, confOverride func(*eth.Config)) *tester {
|
||||
t.Fatalf("failed to create node: %v", err)
|
||||
}
|
||||
ethConf := ð.Config{
|
||||
ChainConfig: &core.ChainConfig{HomesteadBlock: new(big.Int)},
|
||||
ChainConfig: ¶ms.ChainConfig{ChainId: new(big.Int), HomesteadBlock: new(big.Int)},
|
||||
Etherbase: common.HexToAddress(testAddress),
|
||||
AccountManager: accman,
|
||||
PowTest: true,
|
||||
AccountManager: accman,
|
||||
}
|
||||
if confOverride != nil {
|
||||
confOverride(ethConf)
|
||||
|
||||
@@ -163,12 +163,12 @@ func benchInsertChain(b *testing.B, disk bool, gen func(int, *BlockGen)) {
|
||||
// Generate a chain of b.N blocks using the supplied block
|
||||
// generator function.
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{benchRootAddr, benchRootFunds})
|
||||
chain, _ := GenerateChain(nil, genesis, db, b.N, gen)
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, b.N, gen)
|
||||
|
||||
// Time the insertion of the new chain.
|
||||
// State and blocks are stored in the same DB.
|
||||
evmux := new(event.TypeMux)
|
||||
chainman, _ := NewBlockChain(db, &ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
|
||||
chainman, _ := NewBlockChain(db, ¶ms.ChainConfig{HomesteadBlock: new(big.Int)}, FakePow{}, evmux)
|
||||
defer chainman.Stop()
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
@@ -41,13 +41,13 @@ var (
|
||||
//
|
||||
// BlockValidator implements Validator.
|
||||
type BlockValidator struct {
|
||||
config *ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
config *params.ChainConfig // Chain configuration options
|
||||
bc *BlockChain // Canonical block chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewBlockValidator(config *ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||
func NewBlockValidator(config *params.ChainConfig, blockchain *BlockChain, pow pow.PoW) *BlockValidator {
|
||||
validator := &BlockValidator{
|
||||
config: config,
|
||||
Pow: pow,
|
||||
@@ -128,7 +128,7 @@ func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *stat
|
||||
}
|
||||
// Validate the state root against the received state root and throw
|
||||
// an error if they don't match.
|
||||
if root := statedb.IntermediateRoot(); header.Root != root {
|
||||
if root := statedb.IntermediateRoot(v.config.IsEIP158(header.Number)); header.Root != root {
|
||||
return fmt.Errorf("invalid merkle root: header=%x computed=%x", header.Root, root)
|
||||
}
|
||||
return nil
|
||||
@@ -203,7 +203,7 @@ func (v *BlockValidator) ValidateHeader(header, parent *types.Header, checkPow b
|
||||
// Validates a header. Returns an error if the header is invalid.
|
||||
//
|
||||
// See YP section 4.3.4. "Block Header Validity"
|
||||
func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Header, parent *types.Header, checkPow, uncle bool) error {
|
||||
if big.NewInt(int64(len(header.Extra))).Cmp(params.MaximumExtraDataSize) == 1 {
|
||||
return fmt.Errorf("Header extra data too long (%d)", len(header.Extra))
|
||||
}
|
||||
@@ -248,13 +248,21 @@ func ValidateHeader(config *ChainConfig, pow pow.PoW, header *types.Header, pare
|
||||
}
|
||||
}
|
||||
// If all checks passed, validate the extra-data field for hard forks
|
||||
return ValidateDAOHeaderExtraData(config, header)
|
||||
if err := ValidateDAOHeaderExtraData(config, header); err != nil {
|
||||
return err
|
||||
}
|
||||
if config.EIP150Block != nil && config.EIP150Block.Cmp(header.Number) == 0 {
|
||||
if config.EIP150Hash != (common.Hash{}) && config.EIP150Hash != header.Hash() {
|
||||
return ValidationError("Homestead gas reprice fork hash mismatch: have 0x%x, want 0x%x", header.Hash(), config.EIP150Hash)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CalcDifficulty is the difficulty adjustment algorithm. It returns
|
||||
// the difficulty that a new block should have when created at time
|
||||
// given the parent block's time and difficulty.
|
||||
func CalcDifficulty(config *ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
func CalcDifficulty(config *params.ChainConfig, time, parentTime uint64, parentNumber, parentDiff *big.Int) *big.Int {
|
||||
if config.IsHomestead(new(big.Int).Add(parentNumber, common.Big1)) {
|
||||
return calcDifficultyHomestead(time, parentTime, parentNumber, parentDiff)
|
||||
} else {
|
||||
|
||||
@@ -27,11 +27,13 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow/ezp"
|
||||
)
|
||||
|
||||
func testChainConfig() *ChainConfig {
|
||||
return &ChainConfig{HomesteadBlock: big.NewInt(0)}
|
||||
func testChainConfig() *params.ChainConfig {
|
||||
return params.TestChainConfig
|
||||
//return ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)}
|
||||
}
|
||||
|
||||
func proc() (Validator, *BlockChain) {
|
||||
@@ -51,15 +53,15 @@ func TestNumber(t *testing.T) {
|
||||
_, chain := proc()
|
||||
|
||||
statedb, _ := state.New(chain.Genesis().Root(), chain.chainDb)
|
||||
header := makeHeader(chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
cfg := testChainConfig()
|
||||
header := makeHeader(cfg, chain.Genesis(), statedb)
|
||||
header.Number = big.NewInt(3)
|
||||
err := ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
|
||||
if err != BlockNumberErr {
|
||||
t.Errorf("expected block number error, got %q", err)
|
||||
}
|
||||
|
||||
header = makeHeader(chain.Genesis(), statedb)
|
||||
header = makeHeader(cfg, chain.Genesis(), statedb)
|
||||
err = ValidateHeader(cfg, pow, header, chain.Genesis().Header(), false, false)
|
||||
if err == BlockNumberErr {
|
||||
t.Errorf("didn't expect block number error")
|
||||
|
||||
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
@@ -80,7 +81,7 @@ const (
|
||||
// included in the canonical one where as GetBlockByNumber always represents the
|
||||
// canonical chain.
|
||||
type BlockChain struct {
|
||||
config *ChainConfig // chain & network configuration
|
||||
config *params.ChainConfig // chain & network configuration
|
||||
|
||||
hc *HeaderChain
|
||||
chainDb ethdb.Database
|
||||
@@ -115,7 +116,7 @@ type BlockChain struct {
|
||||
// NewBlockChain returns a fully initialised block chain using information
|
||||
// available in the database. It initialiser the default Ethereum Validator and
|
||||
// Processor.
|
||||
func NewBlockChain(chainDb ethdb.Database, config *ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||
func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.PoW, mux *event.TypeMux) (*BlockChain, error) {
|
||||
bodyCache, _ := lru.New(bodyCacheLimit)
|
||||
bodyRLPCache, _ := lru.New(bodyCacheLimit)
|
||||
blockCache, _ := lru.New(blockCacheLimit)
|
||||
@@ -269,7 +270,7 @@ func (self *BlockChain) FastSyncCommitHead(hash common.Hash) error {
|
||||
if block == nil {
|
||||
return fmt.Errorf("non existent block [%x…]", hash[:4])
|
||||
}
|
||||
if _, err := trie.NewSecure(block.Root(), self.chainDb); err != nil {
|
||||
if _, err := trie.NewSecure(block.Root(), self.chainDb, 0); err != nil {
|
||||
return err
|
||||
}
|
||||
// If all checks out, manually set the head block
|
||||
@@ -824,19 +825,16 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
// faster than direct delivery and requires much less mutex
|
||||
// acquiring.
|
||||
var (
|
||||
stats struct{ queued, processed, ignored int }
|
||||
stats = insertStats{startTime: time.Now()}
|
||||
events = make([]interface{}, 0, len(chain))
|
||||
coalescedLogs vm.Logs
|
||||
tstart = time.Now()
|
||||
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
nonceChecked = make([]bool, len(chain))
|
||||
)
|
||||
|
||||
// Start the parallel nonce verifier.
|
||||
nonceAbort, nonceResults := verifyNoncesFromBlocks(self.pow, chain)
|
||||
defer close(nonceAbort)
|
||||
|
||||
txcount := 0
|
||||
for i, block := range chain {
|
||||
if atomic.LoadInt32(&self.procInterrupt) == 1 {
|
||||
glog.V(logger.Debug).Infoln("Premature abort during block chain processing")
|
||||
@@ -907,7 +905,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, self.config.VmConfig)
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
return i, err
|
||||
@@ -919,7 +917,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
return i, err
|
||||
}
|
||||
// Write state changes to database
|
||||
_, err = self.stateCache.Commit()
|
||||
_, err = self.stateCache.Commit(self.config.IsEIP158(block.Number()))
|
||||
if err != nil {
|
||||
return i, err
|
||||
}
|
||||
@@ -931,7 +929,6 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
return i, err
|
||||
}
|
||||
|
||||
txcount += len(block.Transactions())
|
||||
// write the block to the chain and get the status
|
||||
status, err := self.WriteBlock(block)
|
||||
if err != nil {
|
||||
@@ -966,19 +963,54 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
}
|
||||
|
||||
stats.processed++
|
||||
if glog.V(logger.Info) {
|
||||
stats.report(chain, i)
|
||||
}
|
||||
}
|
||||
|
||||
if (stats.queued > 0 || stats.processed > 0 || stats.ignored > 0) && bool(glog.V(logger.Info)) {
|
||||
tend := time.Since(tstart)
|
||||
start, end := chain[0], chain[len(chain)-1]
|
||||
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", stats.processed, stats.queued, stats.ignored, txcount, tend, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
||||
}
|
||||
go self.postChainEvents(events, coalescedLogs)
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// insertStats tracks and reports on block insertion.
|
||||
type insertStats struct {
|
||||
queued, processed, ignored int
|
||||
lastIndex int
|
||||
startTime time.Time
|
||||
}
|
||||
|
||||
const (
|
||||
statsReportLimit = 1024
|
||||
statsReportTimeLimit = 8 * time.Second
|
||||
)
|
||||
|
||||
// report prints statistics if some number of blocks have been processed
|
||||
// or more than a few seconds have passed since the last message.
|
||||
func (st *insertStats) report(chain []*types.Block, index int) {
|
||||
limit := statsReportLimit
|
||||
if index == len(chain)-1 {
|
||||
limit = 0 // Always print a message for the last block.
|
||||
}
|
||||
now := time.Now()
|
||||
duration := now.Sub(st.startTime)
|
||||
if duration > statsReportTimeLimit || st.queued > limit || st.processed > limit || st.ignored > limit {
|
||||
start, end := chain[st.lastIndex], chain[index]
|
||||
txcount := countTransactions(chain[st.lastIndex : index+1])
|
||||
glog.Infof("imported %d block(s) (%d queued %d ignored) including %d txs in %v. #%v [%x / %x]\n", st.processed, st.queued, st.ignored, txcount, duration, end.Number(), start.Hash().Bytes()[:4], end.Hash().Bytes()[:4])
|
||||
*st = insertStats{startTime: now, lastIndex: index}
|
||||
}
|
||||
}
|
||||
|
||||
func countTransactions(chain []*types.Block) (c int) {
|
||||
for _, b := range chain {
|
||||
c += len(b.Transactions())
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// reorgs takes two blocks, an old chain and a new chain and will reconstruct the blocks and inserts them
|
||||
// to be part of the new canonical chain and accumulates potential missing transactions and post an
|
||||
// event about them
|
||||
@@ -1228,4 +1260,4 @@ func (self *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
|
||||
}
|
||||
|
||||
// Config retrieves the blockchain's chain configuration.
|
||||
func (self *BlockChain) Config() *ChainConfig { return self.config }
|
||||
func (self *BlockChain) Config() *params.ChainConfig { return self.config }
|
||||
|
||||
@@ -154,7 +154,7 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
blockchain.mu.Lock()
|
||||
WriteTd(blockchain.chainDb, block.Hash(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash())))
|
||||
WriteBlock(blockchain.chainDb, block)
|
||||
statedb.Commit()
|
||||
statedb.Commit(false)
|
||||
blockchain.mu.Unlock()
|
||||
}
|
||||
return nil
|
||||
@@ -712,7 +712,7 @@ func TestFastVsFullChains(t *testing.T) {
|
||||
funds = big.NewInt(1000000000)
|
||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||
)
|
||||
blocks, receipts := GenerateChain(nil, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, 1024, func(i int, block *BlockGen) {
|
||||
block.SetCoinbase(common.Address{0x00})
|
||||
|
||||
// If the block number is multiple of 3, send a few bonus transactions to the miner
|
||||
@@ -795,7 +795,7 @@ func TestLightVsFastVsFullChainHeads(t *testing.T) {
|
||||
genesis = GenesisBlockForTesting(gendb, address, funds)
|
||||
)
|
||||
height := uint64(1024)
|
||||
blocks, receipts := GenerateChain(nil, genesis, gendb, int(height), nil)
|
||||
blocks, receipts := GenerateChain(params.TestChainConfig, genesis, gendb, int(height), nil)
|
||||
|
||||
// Configure a subchain to roll back
|
||||
remove := []common.Hash{}
|
||||
@@ -895,7 +895,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
// - futureAdd: transaction added after the reorg has already finished
|
||||
var pastAdd, freshAdd, futureAdd *types.Transaction
|
||||
|
||||
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastDrop, _ = types.NewTransaction(gen.TxNonce(addr2), addr2, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key2)
|
||||
@@ -920,7 +920,7 @@ func TestChainTxReorgs(t *testing.T) {
|
||||
}
|
||||
|
||||
// overwrite the old chain
|
||||
chain, _ = GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
pastAdd, _ = types.NewTransaction(gen.TxNonce(addr3), addr3, big.NewInt(1000), params.TxGas, nil, nil).SignECDSA(key3)
|
||||
@@ -990,7 +990,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
|
||||
subs := evmux.Subscribe(RemovedLogsEvent{})
|
||||
chain, _ := GenerateChain(nil, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 2, func(i int, gen *BlockGen) {
|
||||
if i == 1 {
|
||||
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), code).SignECDSA(key1)
|
||||
if err != nil {
|
||||
@@ -1003,7 +1003,7 @@ func TestLogReorgs(t *testing.T) {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
chain, _ = GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
chain, _ = GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert forked chain: %v", err)
|
||||
}
|
||||
@@ -1025,12 +1025,12 @@ func TestReorgSideEvent(t *testing.T) {
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
|
||||
chain, _ := GenerateChain(nil, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 3, func(i int, gen *BlockGen) {})
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
t.Fatalf("failed to insert chain: %v", err)
|
||||
}
|
||||
|
||||
replacementBlocks, _ := GenerateChain(nil, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||
replacementBlocks, _ := GenerateChain(params.TestChainConfig, genesis, db, 4, func(i int, gen *BlockGen) {
|
||||
tx, err := types.NewContractCreation(gen.TxNonce(addr1), new(big.Int), big.NewInt(1000000), new(big.Int), nil).SignECDSA(key1)
|
||||
if i == 2 {
|
||||
gen.OffsetTime(-1)
|
||||
@@ -1101,7 +1101,7 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, testChainConfig(), FakePow{}, evmux)
|
||||
|
||||
chain, _ := GenerateChain(nil, genesis, db, 10, func(i int, gen *BlockGen) {})
|
||||
chain, _ := GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *BlockGen) {})
|
||||
|
||||
for i, _ := range chain {
|
||||
go func(block *types.Block) {
|
||||
@@ -1128,3 +1128,106 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
blockchain.InsertChain(types.Blocks{chain[i]})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP155Transition(t *testing.T) {
|
||||
// Configure and generate a sample block chain
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
mux event.TypeMux
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux)
|
||||
blocks, _ := GenerateChain(config, genesis, db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
||||
tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil)
|
||||
tx.SetSigner(signer)
|
||||
return tx.SignECDSA(key)
|
||||
}
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = basicTx(types.HomesteadSigner{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
case 2:
|
||||
tx, err = basicTx(types.HomesteadSigner{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
case 3:
|
||||
tx, err = basicTx(types.HomesteadSigner{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
|
||||
tx, err = basicTx(types.NewEIP155Signer(config.ChainId))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
}
|
||||
})
|
||||
|
||||
if _, err := blockchain.InsertChain(blocks); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block := blockchain.GetBlockByNumber(1)
|
||||
if block.Transactions()[0].Protected() {
|
||||
t.Error("Expected block[0].txs[0] to not be replay protected")
|
||||
}
|
||||
|
||||
block = blockchain.GetBlockByNumber(3)
|
||||
if block.Transactions()[0].Protected() {
|
||||
t.Error("Expected block[3].txs[0] to not be replay protected")
|
||||
}
|
||||
if !block.Transactions()[1].Protected() {
|
||||
t.Error("Expected block[3].txs[1] to be replay protected")
|
||||
}
|
||||
if _, err := blockchain.InsertChain(blocks[4:]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// generate an invalid chain id transaction
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(2), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
blocks, _ = GenerateChain(config, blocks[len(blocks)-1], db, 4, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
basicTx = func(signer types.Signer) (*types.Transaction, error) {
|
||||
tx := types.NewTransaction(block.TxNonce(address), common.Address{}, new(big.Int), big.NewInt(21000), new(big.Int), nil)
|
||||
tx.SetSigner(signer)
|
||||
return tx.SignECDSA(key)
|
||||
}
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = basicTx(types.NewEIP155Signer(big.NewInt(2)))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
}
|
||||
})
|
||||
_, err := blockchain.InsertChain(blocks)
|
||||
if err != types.ErrInvalidChainId {
|
||||
t.Error("expected error:", types.ErrInvalidChainId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,8 +35,8 @@ import (
|
||||
*/
|
||||
|
||||
// MakeChainConfig returns a new ChainConfig with the ethereum default chain settings.
|
||||
func MakeChainConfig() *ChainConfig {
|
||||
return &ChainConfig{
|
||||
func MakeChainConfig() *params.ChainConfig {
|
||||
return ¶ms.ChainConfig{
|
||||
HomesteadBlock: big.NewInt(0),
|
||||
DAOForkBlock: nil,
|
||||
DAOForkSupport: true,
|
||||
@@ -73,6 +73,8 @@ type BlockGen struct {
|
||||
txs []*types.Transaction
|
||||
receipts []*types.Receipt
|
||||
uncles []*types.Header
|
||||
|
||||
config *params.ChainConfig
|
||||
}
|
||||
|
||||
// SetCoinbase sets the coinbase of the generated block.
|
||||
@@ -106,7 +108,7 @@ func (b *BlockGen) AddTx(tx *types.Transaction) {
|
||||
b.SetCoinbase(common.Address{})
|
||||
}
|
||||
b.statedb.StartRecord(tx.Hash(), common.Hash{}, len(b.txs))
|
||||
receipt, _, _, err := ApplyTransaction(MakeChainConfig(), nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
receipt, _, _, err := ApplyTransaction(b.config, nil, b.gasPool, b.statedb, b.header, tx, b.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -131,7 +133,7 @@ func (b *BlockGen) AddUncheckedReceipt(receipt *types.Receipt) {
|
||||
// TxNonce returns the next valid transaction nonce for the
|
||||
// account at addr. It panics if the account does not exist.
|
||||
func (b *BlockGen) TxNonce(addr common.Address) uint64 {
|
||||
if !b.statedb.HasAccount(addr) {
|
||||
if !b.statedb.Exist(addr) {
|
||||
panic("account does not exist")
|
||||
}
|
||||
return b.statedb.GetNonce(addr)
|
||||
@@ -178,10 +180,10 @@ func (b *BlockGen) OffsetTime(seconds int64) {
|
||||
// Blocks created by GenerateChain do not contain valid proof of work
|
||||
// values. Inserting them into BlockChain requires use of FakePow or
|
||||
// a similar non-validating proof of work implementation.
|
||||
func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||
func GenerateChain(config *params.ChainConfig, parent *types.Block, db ethdb.Database, n int, gen func(int, *BlockGen)) ([]*types.Block, []types.Receipts) {
|
||||
blocks, receipts := make(types.Blocks, n), make([]types.Receipts, n)
|
||||
genblock := func(i int, h *types.Header, statedb *state.StateDB) (*types.Block, types.Receipts) {
|
||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb}
|
||||
b := &BlockGen{parent: parent, i: i, chain: blocks, header: h, statedb: statedb, config: config}
|
||||
|
||||
// Mutate the state and block according to any hard-fork specs
|
||||
if config == nil {
|
||||
@@ -203,7 +205,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
|
||||
gen(i, b)
|
||||
}
|
||||
AccumulateRewards(statedb, h, b.uncles)
|
||||
root, err := statedb.Commit()
|
||||
root, err := statedb.Commit(config.IsEIP158(h.Number))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("state write error: %v", err))
|
||||
}
|
||||
@@ -215,7 +217,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
header := makeHeader(parent, statedb)
|
||||
header := makeHeader(config, parent, statedb)
|
||||
block, receipt := genblock(i, header, statedb)
|
||||
blocks[i] = block
|
||||
receipts[i] = receipt
|
||||
@@ -224,7 +226,7 @@ func GenerateChain(config *ChainConfig, parent *types.Block, db ethdb.Database,
|
||||
return blocks, receipts
|
||||
}
|
||||
|
||||
func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
||||
func makeHeader(config *params.ChainConfig, parent *types.Block, state *state.StateDB) *types.Header {
|
||||
var time *big.Int
|
||||
if parent.Time() == nil {
|
||||
time = big.NewInt(10)
|
||||
@@ -232,7 +234,7 @@ func makeHeader(parent *types.Block, state *state.StateDB) *types.Header {
|
||||
time = new(big.Int).Add(parent.Time(), big.NewInt(10)) // block time is fixed at 10 seconds
|
||||
}
|
||||
return &types.Header{
|
||||
Root: state.IntermediateRoot(),
|
||||
Root: state.IntermediateRoot(config.IsEIP158(parent.Number())),
|
||||
ParentHash: parent.Hash(),
|
||||
Coinbase: parent.Coinbase(),
|
||||
Difficulty: CalcDifficulty(MakeChainConfig(), time.Uint64(), new(big.Int).Sub(time, big.NewInt(10)).Uint64(), parent.Number(), parent.Difficulty()),
|
||||
@@ -283,7 +285,7 @@ func makeHeaderChain(parent *types.Header, n int, db ethdb.Database, seed int) [
|
||||
|
||||
// makeBlockChain creates a deterministic chain of blocks rooted at parent.
|
||||
func makeBlockChain(parent *types.Block, n int, db ethdb.Database, seed int) []*types.Block {
|
||||
blocks, _ := GenerateChain(nil, parent, db, n, func(i int, b *BlockGen) {
|
||||
blocks, _ := GenerateChain(params.TestChainConfig, parent, db, n, func(i int, b *BlockGen) {
|
||||
b.SetCoinbase(common.Address{0: byte(seed), 19: byte(i)})
|
||||
})
|
||||
return blocks
|
||||
|
||||
@@ -41,13 +41,16 @@ func ExampleGenerateChain() {
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
)
|
||||
|
||||
chainConfig := ¶ms.ChainConfig{
|
||||
HomesteadBlock: new(big.Int),
|
||||
}
|
||||
// Ensure that key1 has some funds in the genesis block.
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr1, big.NewInt(1000000)})
|
||||
|
||||
// This call generates a chain of 5 blocks. The function runs for
|
||||
// each block and adds different features to gen based on the
|
||||
// block index.
|
||||
chain, _ := GenerateChain(nil, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
chain, _ := GenerateChain(chainConfig, genesis, db, 5, func(i int, gen *BlockGen) {
|
||||
switch i {
|
||||
case 0:
|
||||
// In block 1, addr1 sends addr2 some ether.
|
||||
@@ -77,7 +80,7 @@ func ExampleGenerateChain() {
|
||||
|
||||
// Import the chain. This runs all block validation rules.
|
||||
evmux := &event.TypeMux{}
|
||||
blockchain, _ := NewBlockChain(db, MakeChainConfig(), FakePow{}, evmux)
|
||||
blockchain, _ := NewBlockChain(db, chainConfig, FakePow{}, evmux)
|
||||
if i, err := blockchain.InsertChain(chain); err != nil {
|
||||
fmt.Printf("insert error (block %d): %v\n", chain[i].NumberU64(), err)
|
||||
return
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
)
|
||||
|
||||
@@ -60,7 +61,7 @@ func TestPowVerification(t *testing.T) {
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
@@ -115,7 +116,7 @@ func testPowConcurrentVerification(t *testing.T, threads int) {
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(nil, genesis, testdb, 8, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 8, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
@@ -186,7 +187,7 @@ func testPowConcurrentAbortion(t *testing.T, threads int) {
|
||||
var (
|
||||
testdb, _ = ethdb.NewMemDatabase()
|
||||
genesis = GenesisBlockForTesting(testdb, common.Address{}, new(big.Int))
|
||||
blocks, _ = GenerateChain(nil, genesis, testdb, 1024, nil)
|
||||
blocks, _ = GenerateChain(params.TestChainConfig, genesis, testdb, 1024, nil)
|
||||
)
|
||||
headers := make([]*types.Header, len(blocks))
|
||||
for i, block := range blocks {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
// Copyright 2016 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 core
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
var ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
//
|
||||
// ChainConfig is stored in the database on a per block basis. This means
|
||||
// that any network, identified by its genesis block, can have its own
|
||||
// set of configuration options.
|
||||
type ChainConfig struct {
|
||||
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
|
||||
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
|
||||
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
|
||||
|
||||
VmConfig vm.Config `json:"-"`
|
||||
}
|
||||
|
||||
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
||||
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
||||
if c.HomesteadBlock == nil || num == nil {
|
||||
return false
|
||||
}
|
||||
return num.Cmp(c.HomesteadBlock) >= 0
|
||||
}
|
||||
@@ -33,7 +33,7 @@ import (
|
||||
// with the fork specific extra-data set
|
||||
// b) if the node is pro-fork, require blocks in the specific range to have the
|
||||
// unique extra-data set.
|
||||
func ValidateDAOHeaderExtraData(config *ChainConfig, header *types.Header) error {
|
||||
func ValidateDAOHeaderExtraData(config *params.ChainConfig, header *types.Header) error {
|
||||
// Short circuit validation if the node doesn't care about the DAO fork
|
||||
if config.DAOForkBlock == nil {
|
||||
return nil
|
||||
|
||||
@@ -33,17 +33,17 @@ func TestDAOForkRangeExtradata(t *testing.T) {
|
||||
// Generate a common prefix for both pro-forkers and non-forkers
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
genesis := WriteGenesisBlockForTesting(db)
|
||||
prefix, _ := GenerateChain(nil, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
prefix, _ := GenerateChain(params.TestChainConfig, genesis, db, int(forkBlock.Int64()-1), func(i int, gen *BlockGen) {})
|
||||
|
||||
// Create the concurrent, conflicting two nodes
|
||||
proDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(proDb)
|
||||
proConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
|
||||
proConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: true}
|
||||
proBc, _ := NewBlockChain(proDb, proConf, new(FakePow), new(event.TypeMux))
|
||||
|
||||
conDb, _ := ethdb.NewMemDatabase()
|
||||
WriteGenesisBlockForTesting(conDb)
|
||||
conConf := &ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
|
||||
conConf := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0), DAOForkBlock: forkBlock, DAOForkSupport: false}
|
||||
conBc, _ := NewBlockChain(conDb, conConf, new(FakePow), new(event.TypeMux))
|
||||
|
||||
if _, err := proBc.InsertChain(prefix); err != nil {
|
||||
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
@@ -28,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -52,7 +54,8 @@ var (
|
||||
|
||||
blockHashPrefix = []byte("block-hash-") // [deprecated by the header/block split, remove eventually]
|
||||
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
configPrefix = []byte("ethereum-config-") // config prefix for the db
|
||||
ChainConfigNotFoundErr = errors.New("ChainConfig not found") // general config not found error
|
||||
)
|
||||
|
||||
// GetCanonicalHash retrieves a hash assigned to a canonical block number.
|
||||
@@ -536,7 +539,7 @@ func WriteBlockChainVersion(db ethdb.Database, vsn int) {
|
||||
}
|
||||
|
||||
// WriteChainConfig writes the chain config settings to the database.
|
||||
func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) error {
|
||||
func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *params.ChainConfig) error {
|
||||
// short circuit and ignore if nil config. GetChainConfig
|
||||
// will return a default.
|
||||
if cfg == nil {
|
||||
@@ -552,13 +555,13 @@ func WriteChainConfig(db ethdb.Database, hash common.Hash, cfg *ChainConfig) err
|
||||
}
|
||||
|
||||
// GetChainConfig will fetch the network settings based on the given hash.
|
||||
func GetChainConfig(db ethdb.Database, hash common.Hash) (*ChainConfig, error) {
|
||||
func GetChainConfig(db ethdb.Database, hash common.Hash) (*params.ChainConfig, error) {
|
||||
jsonChainConfig, _ := db.Get(append(configPrefix, hash[:]...))
|
||||
if len(jsonChainConfig) == 0 {
|
||||
return nil, ChainConfigNotFoundErr
|
||||
}
|
||||
|
||||
var config ChainConfig
|
||||
var config params.ChainConfig
|
||||
if err := json.Unmarshal(jsonChainConfig, &config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -62,7 +63,7 @@ func (d *diffTest) UnmarshalJSON(b []byte) (err error) {
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestDifficultyFrontier(t *testing.T) {
|
||||
func TestCalcDifficulty(t *testing.T) {
|
||||
file, err := os.Open("../tests/files/BasicTests/difficulty.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
@@ -75,9 +76,10 @@ func TestDifficultyFrontier(t *testing.T) {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
config := ¶ms.ChainConfig{HomesteadBlock: big.NewInt(1150000)}
|
||||
for name, test := range tests {
|
||||
number := new(big.Int).Sub(test.CurrentBlocknumber, big.NewInt(1))
|
||||
diff := calcDifficultyFrontier(test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||
diff := CalcDifficulty(config, test.CurrentTimestamp, test.ParentTimestamp, number, test.ParentDifficulty)
|
||||
if diff.Cmp(test.CurrentDifficulty) != 0 {
|
||||
t.Error(name, "failed. Expected", test.CurrentDifficulty, "and calculated", diff)
|
||||
}
|
||||
@@ -561,7 +563,7 @@ func TestMipmapChain(t *testing.T) {
|
||||
defer db.Close()
|
||||
|
||||
genesis := WriteGenesisBlockForTesting(db, GenesisAccount{addr, big.NewInt(1000000)})
|
||||
chain, receipts := GenerateChain(nil, genesis, db, 1010, func(i int, gen *BlockGen) {
|
||||
chain, receipts := GenerateChain(params.TestChainConfig, genesis, db, 1010, func(i int, gen *BlockGen) {
|
||||
var receipts types.Receipts
|
||||
switch i {
|
||||
case 1:
|
||||
|
||||
@@ -27,14 +27,14 @@ import (
|
||||
|
||||
// Call executes within the given contract
|
||||
func Call(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||
ret, _, err = exec(env, caller, &addr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
ret, _, err = exec(true, env, caller, &addr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// CallCode executes the given address' code as the given contract address
|
||||
func CallCode(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice, value *big.Int) (ret []byte, err error) {
|
||||
callerAddr := caller.Address()
|
||||
ret, _, err = exec(env, caller, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
ret, _, err = exec(false, env, caller, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, value)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
@@ -43,13 +43,13 @@ func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address
|
||||
callerAddr := caller.Address()
|
||||
originAddr := env.Origin()
|
||||
callerValue := caller.Value()
|
||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
||||
ret, _, err = execDelegateCall(env, caller, &originAddr, &callerAddr, &addr, env.Db().GetCodeHash(addr), input, env.Db().GetCode(addr), gas, gasPrice, callerValue)
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Create creates a new contract with the given code
|
||||
func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPrice, value *big.Int) (ret []byte, address common.Address, err error) {
|
||||
ret, address, err = exec(env, caller, nil, nil, nil, code, gas, gasPrice, value)
|
||||
ret, address, err = exec(true, env, caller, nil, nil, crypto.Keccak256Hash(code), nil, code, gas, gasPrice, value)
|
||||
// Here we get an error if we run into maximum stack depth,
|
||||
// See: https://github.com/ethereum/yellowpaper/pull/131
|
||||
// and YP definitions for CREATE instruction
|
||||
@@ -59,7 +59,7 @@ func Create(env vm.Environment, caller vm.ContractRef, code []byte, gas, gasPric
|
||||
return ret, address, err
|
||||
}
|
||||
|
||||
func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
func exec(transfers bool, env vm.Environment, caller vm.ContractRef, address, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
evm := env.Vm()
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
@@ -85,27 +85,37 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||
createAccount = true
|
||||
}
|
||||
|
||||
snapshotPreTransfer := env.MakeSnapshot()
|
||||
snapshotPreTransfer := env.SnapshotDatabase()
|
||||
var (
|
||||
from = env.Db().GetAccount(caller.Address())
|
||||
to vm.Account
|
||||
)
|
||||
if createAccount {
|
||||
to = env.Db().CreateAccount(*address)
|
||||
if env.ChainConfig().IsEIP158(env.BlockNumber()) {
|
||||
env.Db().SetNonce(*address, 1)
|
||||
}
|
||||
} else {
|
||||
if !env.Db().Exist(*address) {
|
||||
if vm.Precompiled[address.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
return nil, common.Address{}, nil
|
||||
}
|
||||
|
||||
to = env.Db().CreateAccount(*address)
|
||||
} else {
|
||||
to = env.Db().GetAccount(*address)
|
||||
}
|
||||
}
|
||||
env.Transfer(from, to, value)
|
||||
if transfers {
|
||||
env.Transfer(from, to, 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 := vm.NewContract(caller, to, value, gas, gasPrice)
|
||||
contract.SetCallCode(codeAddr, code)
|
||||
contract.SetCallCode(codeAddr, codeHash, code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.Run(contract, input)
|
||||
@@ -126,16 +136,16 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A
|
||||
// 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 && (env.RuleSet().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||
if err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError) {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.SetSnapshot(snapshotPreTransfer)
|
||||
env.RevertToSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
}
|
||||
|
||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toAddr, codeAddr *common.Address, codeHash common.Hash, input, code []byte, gas, gasPrice, value *big.Int) (ret []byte, addr common.Address, err error) {
|
||||
evm := env.Vm()
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
@@ -144,7 +154,7 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
||||
return nil, common.Address{}, vm.DepthError
|
||||
}
|
||||
|
||||
snapshot := env.MakeSnapshot()
|
||||
snapshot := env.SnapshotDatabase()
|
||||
|
||||
var to vm.Account
|
||||
if !env.Db().Exist(*toAddr) {
|
||||
@@ -155,14 +165,14 @@ func execDelegateCall(env vm.Environment, caller vm.ContractRef, originAddr, toA
|
||||
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := vm.NewContract(caller, to, value, gas, gasPrice).AsDelegate()
|
||||
contract.SetCallCode(codeAddr, code)
|
||||
contract.SetCallCode(codeAddr, codeHash, code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = evm.Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.SetSnapshot(snapshot)
|
||||
env.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
|
||||
@@ -43,7 +43,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
||||
}
|
||||
|
||||
var genesis struct {
|
||||
ChainConfig *ChainConfig `json:"config"`
|
||||
ChainConfig *params.ChainConfig `json:"config"`
|
||||
Nonce string
|
||||
Timestamp string
|
||||
ParentHash string
|
||||
@@ -73,7 +73,7 @@ func WriteGenesisBlock(chainDb ethdb.Database, reader io.Reader) (*types.Block,
|
||||
statedb.SetState(address, common.HexToHash(key), common.HexToHash(value))
|
||||
}
|
||||
}
|
||||
root, stateBatch := statedb.CommitBatch()
|
||||
root, stateBatch := statedb.CommitBatch(false)
|
||||
|
||||
difficulty := common.String2Big(genesis.Difficulty)
|
||||
block := types.NewBlock(&types.Header{
|
||||
@@ -128,7 +128,7 @@ func GenesisBlockForTesting(db ethdb.Database, addr common.Address, balance *big
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
obj := statedb.GetOrNewStateObject(addr)
|
||||
obj.SetBalance(balance)
|
||||
root, err := statedb.Commit()
|
||||
root, err := statedb.Commit(false)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot write state: %v", err))
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/hashicorp/golang-lru"
|
||||
)
|
||||
@@ -41,7 +42,7 @@ import (
|
||||
// It is not thread safe either, the encapsulating chain structures should do
|
||||
// the necessary mutex locking/unlocking.
|
||||
type HeaderChain struct {
|
||||
config *ChainConfig
|
||||
config *params.ChainConfig
|
||||
|
||||
chainDb ethdb.Database
|
||||
genesisHeader *types.Header
|
||||
@@ -65,7 +66,7 @@ type getHeaderValidatorFn func() HeaderValidator
|
||||
// getValidator should return the parent's validator
|
||||
// procInterrupt points to the parent's interrupt semaphore
|
||||
// wg points to the parent's shutdown wait group
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, getValidator getHeaderValidatorFn, procInterrupt func() bool) (*HeaderChain, error) {
|
||||
headerCache, _ := lru.New(headerCacheLimit)
|
||||
tdCache, _ := lru.New(tdCacheLimit)
|
||||
|
||||
@@ -443,13 +444,13 @@ func (hc *HeaderChain) SetGenesis(head *types.Header) {
|
||||
//
|
||||
// headerValidator implements HeaderValidator.
|
||||
type headerValidator struct {
|
||||
config *ChainConfig
|
||||
config *params.ChainConfig
|
||||
hc *HeaderChain // Canonical header chain
|
||||
Pow pow.PoW // Proof of work used for validating
|
||||
}
|
||||
|
||||
// NewBlockValidator returns a new block validator which is safe for re-use
|
||||
func NewHeaderValidator(config *ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
|
||||
func NewHeaderValidator(config *params.ChainConfig, chain *HeaderChain, pow pow.PoW) HeaderValidator {
|
||||
return &headerValidator{
|
||||
config: config,
|
||||
Pow: pow,
|
||||
|
||||
@@ -52,7 +52,7 @@ func (self *StateDB) RawDump() Dump {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
obj := NewObject(common.BytesToAddress(addr), data, nil)
|
||||
obj := newObject(nil, common.BytesToAddress(addr), data, nil)
|
||||
account := DumpAccount{
|
||||
Balance: data.Balance.String(),
|
||||
Nonce: data.Nonce,
|
||||
|
||||
117
core/state/journal.go
Normal file
117
core/state/journal.go
Normal file
@@ -0,0 +1,117 @@
|
||||
// Copyright 2016 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 state
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
type journalEntry interface {
|
||||
undo(*StateDB)
|
||||
}
|
||||
|
||||
type journal []journalEntry
|
||||
|
||||
type (
|
||||
// Changes to the account trie.
|
||||
createObjectChange struct {
|
||||
account *common.Address
|
||||
}
|
||||
resetObjectChange struct {
|
||||
prev *StateObject
|
||||
}
|
||||
suicideChange struct {
|
||||
account *common.Address
|
||||
prev bool // whether account had already suicided
|
||||
prevbalance *big.Int
|
||||
}
|
||||
|
||||
// Changes to individual accounts.
|
||||
balanceChange struct {
|
||||
account *common.Address
|
||||
prev *big.Int
|
||||
}
|
||||
nonceChange struct {
|
||||
account *common.Address
|
||||
prev uint64
|
||||
}
|
||||
storageChange struct {
|
||||
account *common.Address
|
||||
key, prevalue common.Hash
|
||||
}
|
||||
codeChange struct {
|
||||
account *common.Address
|
||||
prevcode, prevhash []byte
|
||||
}
|
||||
|
||||
// Changes to other state values.
|
||||
refundChange struct {
|
||||
prev *big.Int
|
||||
}
|
||||
addLogChange struct {
|
||||
txhash common.Hash
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).deleted = true
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
|
||||
func (ch resetObjectChange) undo(s *StateDB) {
|
||||
s.setStateObject(ch.prev)
|
||||
}
|
||||
|
||||
func (ch suicideChange) undo(s *StateDB) {
|
||||
obj := s.GetStateObject(*ch.account)
|
||||
if obj != nil {
|
||||
obj.suicided = ch.prev
|
||||
obj.setBalance(ch.prevbalance)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch balanceChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
func (ch nonceChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setNonce(ch.prev)
|
||||
}
|
||||
|
||||
func (ch codeChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setCode(common.BytesToHash(ch.prevhash), ch.prevcode)
|
||||
}
|
||||
|
||||
func (ch storageChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setState(ch.key, ch.prevalue)
|
||||
}
|
||||
|
||||
func (ch refundChange) undo(s *StateDB) {
|
||||
s.refund = ch.prev
|
||||
}
|
||||
|
||||
func (ch addLogChange) undo(s *StateDB) {
|
||||
logs := s.logs[ch.txhash]
|
||||
if len(logs) == 1 {
|
||||
delete(s.logs, ch.txhash)
|
||||
} else {
|
||||
s.logs[ch.txhash] = logs[:len(logs)-1]
|
||||
}
|
||||
}
|
||||
@@ -29,11 +29,8 @@ func create() (*ManagedState, *account) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := New(common.Hash{}, db)
|
||||
ms := ManageState(statedb)
|
||||
so := &StateObject{address: addr}
|
||||
so.SetNonce(100)
|
||||
ms.StateDB.stateObjects[addr] = so
|
||||
ms.accounts[addr] = newAccount(so)
|
||||
|
||||
ms.StateDB.SetNonce(addr, 100)
|
||||
ms.accounts[addr] = newAccount(ms.StateDB.GetStateObject(addr))
|
||||
return ms, ms.accounts[addr]
|
||||
}
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ func (self Storage) Copy() Storage {
|
||||
type StateObject struct {
|
||||
address common.Address // Ethereum address of this account
|
||||
data Account
|
||||
db *StateDB
|
||||
|
||||
// DB error.
|
||||
// State objects are used by the consensus core and VM which are
|
||||
@@ -75,19 +76,26 @@ type StateObject struct {
|
||||
dbErr error
|
||||
|
||||
// Write caches.
|
||||
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
|
||||
code Code // contract bytecode, which gets set when code is loaded
|
||||
storage Storage // Cached storage (flushed when updated)
|
||||
trie *trie.SecureTrie // storage trie, which becomes non-nil on first access
|
||||
code Code // contract bytecode, which gets set when code is loaded
|
||||
|
||||
cachedStorage Storage // Storage entry cache to avoid duplicate reads
|
||||
dirtyStorage Storage // Storage entries that need to be flushed to disk
|
||||
|
||||
// Cache flags.
|
||||
// When an object is marked for deletion it will be delete from the trie
|
||||
// during the "update" phase of the state transition
|
||||
// When an object is marked suicided it will be delete from the trie
|
||||
// during the "update" phase of the state transition.
|
||||
dirtyCode bool // true if the code was updated
|
||||
remove bool
|
||||
suicided bool
|
||||
deleted bool
|
||||
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
|
||||
}
|
||||
|
||||
// empty returns whether the account is considered empty.
|
||||
func (s *StateObject) empty() bool {
|
||||
return s.data.Nonce == 0 && s.data.Balance.BitLen() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
|
||||
}
|
||||
|
||||
// Account is the Ethereum consensus representation of accounts.
|
||||
// These objects are stored in the main account trie.
|
||||
type Account struct {
|
||||
@@ -97,15 +105,15 @@ type Account struct {
|
||||
CodeHash []byte
|
||||
}
|
||||
|
||||
// NewObject creates a state object.
|
||||
func NewObject(address common.Address, data Account, onDirty func(addr common.Address)) *StateObject {
|
||||
// newObject creates a state object.
|
||||
func newObject(db *StateDB, address common.Address, data Account, onDirty func(addr common.Address)) *StateObject {
|
||||
if data.Balance == nil {
|
||||
data.Balance = new(big.Int)
|
||||
}
|
||||
if data.CodeHash == nil {
|
||||
data.CodeHash = emptyCodeHash
|
||||
}
|
||||
return &StateObject{address: address, data: data, storage: make(Storage), onDirty: onDirty}
|
||||
return &StateObject{db: db, address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty}
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
@@ -120,8 +128,8 @@ func (self *StateObject) setError(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateObject) MarkForDeletion() {
|
||||
self.remove = true
|
||||
func (self *StateObject) markSuicided() {
|
||||
self.suicided = true
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
@@ -134,9 +142,9 @@ func (self *StateObject) MarkForDeletion() {
|
||||
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
|
||||
if c.trie == nil {
|
||||
var err error
|
||||
c.trie, err = trie.NewSecure(c.data.Root, db)
|
||||
c.trie, err = trie.NewSecure(c.data.Root, db, 0)
|
||||
if err != nil {
|
||||
c.trie, _ = trie.NewSecure(common.Hash{}, db)
|
||||
c.trie, _ = trie.NewSecure(common.Hash{}, db, 0)
|
||||
c.setError(fmt.Errorf("can't create storage trie: %v", err))
|
||||
}
|
||||
}
|
||||
@@ -145,24 +153,38 @@ func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
|
||||
|
||||
// GetState returns a value in account storage.
|
||||
func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash {
|
||||
value, exists := self.storage[key]
|
||||
value, exists := self.cachedStorage[key]
|
||||
if exists {
|
||||
return value
|
||||
}
|
||||
// Load from DB in case it is missing.
|
||||
tr := self.getTrie(db)
|
||||
var ret []byte
|
||||
rlp.DecodeBytes(tr.Get(key[:]), &ret)
|
||||
value = common.BytesToHash(ret)
|
||||
if enc := self.getTrie(db).Get(key[:]); len(enc) > 0 {
|
||||
_, content, _, err := rlp.Split(enc)
|
||||
if err != nil {
|
||||
self.setError(err)
|
||||
}
|
||||
value.SetBytes(content)
|
||||
}
|
||||
if (value != common.Hash{}) {
|
||||
self.storage[key] = value
|
||||
self.cachedStorage[key] = value
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
// SetState updates a value in account storage.
|
||||
func (self *StateObject) SetState(key, value common.Hash) {
|
||||
self.storage[key] = value
|
||||
func (self *StateObject) SetState(db trie.Database, key, value common.Hash) {
|
||||
self.db.journal = append(self.db.journal, storageChange{
|
||||
account: &self.address,
|
||||
key: key,
|
||||
prevalue: self.GetState(db, key),
|
||||
})
|
||||
self.setState(key, value)
|
||||
}
|
||||
|
||||
func (self *StateObject) setState(key, value common.Hash) {
|
||||
self.cachedStorage[key] = value
|
||||
self.dirtyStorage[key] = value
|
||||
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
self.onDirty = nil
|
||||
@@ -172,7 +194,8 @@ func (self *StateObject) SetState(key, value common.Hash) {
|
||||
// updateTrie writes cached storage modifications into the object's storage trie.
|
||||
func (self *StateObject) updateTrie(db trie.Database) {
|
||||
tr := self.getTrie(db)
|
||||
for key, value := range self.storage {
|
||||
for key, value := range self.dirtyStorage {
|
||||
delete(self.dirtyStorage, key)
|
||||
if (value == common.Hash{}) {
|
||||
tr.Delete(key[:])
|
||||
continue
|
||||
@@ -184,7 +207,7 @@ func (self *StateObject) updateTrie(db trie.Database) {
|
||||
}
|
||||
|
||||
// UpdateRoot sets the trie root to the current root hash of
|
||||
func (self *StateObject) UpdateRoot(db trie.Database) {
|
||||
func (self *StateObject) updateRoot(db trie.Database) {
|
||||
self.updateTrie(db)
|
||||
self.data.Root = self.trie.Hash()
|
||||
}
|
||||
@@ -194,7 +217,6 @@ func (self *StateObject) UpdateRoot(db trie.Database) {
|
||||
func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) error {
|
||||
self.updateTrie(db)
|
||||
if self.dbErr != nil {
|
||||
fmt.Println("dbErr:", self.dbErr)
|
||||
return self.dbErr
|
||||
}
|
||||
root, err := self.trie.CommitTo(dbw)
|
||||
@@ -204,8 +226,12 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e
|
||||
return err
|
||||
}
|
||||
|
||||
// AddBalance removes amount from c's balance.
|
||||
// It is used to add funds to the destination account of a transfer.
|
||||
func (c *StateObject) AddBalance(amount *big.Int) {
|
||||
if amount.Cmp(common.Big0) == 0 {
|
||||
// EIP158: We must check emptiness for the objects such that the account
|
||||
// clearing (0,0,0 objects) can take effect.
|
||||
if amount.Cmp(common.Big0) == 0 && !c.empty() {
|
||||
return
|
||||
}
|
||||
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
|
||||
@@ -215,6 +241,8 @@ func (c *StateObject) AddBalance(amount *big.Int) {
|
||||
}
|
||||
}
|
||||
|
||||
// SubBalance removes amount from c's balance.
|
||||
// It is used to remove funds from the origin account of a transfer.
|
||||
func (c *StateObject) SubBalance(amount *big.Int) {
|
||||
if amount.Cmp(common.Big0) == 0 {
|
||||
return
|
||||
@@ -227,6 +255,14 @@ func (c *StateObject) SubBalance(amount *big.Int) {
|
||||
}
|
||||
|
||||
func (self *StateObject) SetBalance(amount *big.Int) {
|
||||
self.db.journal = append(self.db.journal, balanceChange{
|
||||
account: &self.address,
|
||||
prev: new(big.Int).Set(self.data.Balance),
|
||||
})
|
||||
self.setBalance(amount)
|
||||
}
|
||||
|
||||
func (self *StateObject) setBalance(amount *big.Int) {
|
||||
self.data.Balance = amount
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
@@ -237,12 +273,13 @@ func (self *StateObject) SetBalance(amount *big.Int) {
|
||||
// Return the gas back to the origin. Used by the Virtual machine or Closures
|
||||
func (c *StateObject) ReturnGas(gas, price *big.Int) {}
|
||||
|
||||
func (self *StateObject) Copy(db trie.Database, onDirty func(addr common.Address)) *StateObject {
|
||||
stateObject := NewObject(self.address, self.data, onDirty)
|
||||
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
|
||||
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||
stateObject.trie = self.trie
|
||||
stateObject.code = self.code
|
||||
stateObject.storage = self.storage.Copy()
|
||||
stateObject.remove = self.remove
|
||||
stateObject.dirtyStorage = self.dirtyStorage.Copy()
|
||||
stateObject.cachedStorage = self.dirtyStorage.Copy()
|
||||
stateObject.suicided = self.suicided
|
||||
stateObject.dirtyCode = self.dirtyCode
|
||||
stateObject.deleted = self.deleted
|
||||
return stateObject
|
||||
@@ -273,9 +310,19 @@ func (self *StateObject) Code(db trie.Database) []byte {
|
||||
return code
|
||||
}
|
||||
|
||||
func (self *StateObject) SetCode(code []byte) {
|
||||
func (self *StateObject) SetCode(codeHash common.Hash, code []byte) {
|
||||
prevcode := self.Code(self.db.db)
|
||||
self.db.journal = append(self.db.journal, codeChange{
|
||||
account: &self.address,
|
||||
prevhash: self.CodeHash(),
|
||||
prevcode: prevcode,
|
||||
})
|
||||
self.setCode(codeHash, code)
|
||||
}
|
||||
|
||||
func (self *StateObject) setCode(codeHash common.Hash, code []byte) {
|
||||
self.code = code
|
||||
self.data.CodeHash = crypto.Keccak256(code)
|
||||
self.data.CodeHash = codeHash[:]
|
||||
self.dirtyCode = true
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
@@ -284,6 +331,14 @@ func (self *StateObject) SetCode(code []byte) {
|
||||
}
|
||||
|
||||
func (self *StateObject) SetNonce(nonce uint64) {
|
||||
self.db.journal = append(self.db.journal, nonceChange{
|
||||
account: &self.address,
|
||||
prev: self.data.Nonce,
|
||||
})
|
||||
self.setNonce(nonce)
|
||||
}
|
||||
|
||||
func (self *StateObject) setNonce(nonce uint64) {
|
||||
self.data.Nonce = nonce
|
||||
if self.onDirty != nil {
|
||||
self.onDirty(self.Address())
|
||||
@@ -312,15 +367,15 @@ func (self *StateObject) Value() *big.Int {
|
||||
|
||||
func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) {
|
||||
// When iterating over the storage check the cache first
|
||||
for h, value := range self.storage {
|
||||
for h, value := range self.cachedStorage {
|
||||
cb(h, value)
|
||||
}
|
||||
|
||||
it := self.trie.Iterator()
|
||||
it := self.getTrie(self.db.db).Iterator()
|
||||
for it.Next() {
|
||||
// ignore cached values
|
||||
key := common.BytesToHash(self.trie.GetKey(it.Key))
|
||||
if _, ok := self.storage[key]; !ok {
|
||||
if _, ok := self.cachedStorage[key]; !ok {
|
||||
cb(key, common.BytesToHash(it.Value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
checker "gopkg.in/check.v1"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
@@ -40,14 +41,14 @@ func (s *StateSuite) TestDump(c *checker.C) {
|
||||
obj1 := s.state.GetOrNewStateObject(toAddr([]byte{0x01}))
|
||||
obj1.AddBalance(big.NewInt(22))
|
||||
obj2 := s.state.GetOrNewStateObject(toAddr([]byte{0x01, 0x02}))
|
||||
obj2.SetCode([]byte{3, 3, 3, 3, 3, 3, 3})
|
||||
obj2.SetCode(crypto.Keccak256Hash([]byte{3, 3, 3, 3, 3, 3, 3}), []byte{3, 3, 3, 3, 3, 3, 3})
|
||||
obj3 := s.state.GetOrNewStateObject(toAddr([]byte{0x02}))
|
||||
obj3.SetBalance(big.NewInt(44))
|
||||
|
||||
// write some of them to the trie
|
||||
s.state.UpdateStateObject(obj1)
|
||||
s.state.UpdateStateObject(obj2)
|
||||
s.state.Commit()
|
||||
s.state.updateStateObject(obj1)
|
||||
s.state.updateStateObject(obj2)
|
||||
s.state.Commit(false)
|
||||
|
||||
// check that dump contains the state objects that are in trie
|
||||
got := string(s.state.Dump())
|
||||
@@ -99,7 +100,7 @@ func TestNull(t *testing.T) {
|
||||
//value := common.FromHex("0x823140710bf13990e4500136726d8b55")
|
||||
var value common.Hash
|
||||
state.SetState(address, common.Hash{}, value)
|
||||
state.Commit()
|
||||
state.Commit(false)
|
||||
value = state.GetState(address, common.Hash{})
|
||||
if !common.EmptyHash(value) {
|
||||
t.Errorf("expected empty hash. got %x", value)
|
||||
@@ -115,12 +116,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||
// set initial state object value
|
||||
s.state.SetState(stateobjaddr, storageaddr, data1)
|
||||
// get snapshot of current state
|
||||
snapshot := s.state.Copy()
|
||||
snapshot := s.state.Snapshot()
|
||||
|
||||
// set new state object value
|
||||
s.state.SetState(stateobjaddr, storageaddr, data2)
|
||||
// restore snapshot
|
||||
s.state.Set(snapshot)
|
||||
s.state.RevertToSnapshot(snapshot)
|
||||
|
||||
// get state storage value
|
||||
res := s.state.GetState(stateobjaddr, storageaddr)
|
||||
@@ -128,6 +129,12 @@ func (s *StateSuite) TestSnapshot(c *checker.C) {
|
||||
c.Assert(data1, checker.DeepEquals, res)
|
||||
}
|
||||
|
||||
func TestSnapshotEmpty(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state, _ := New(common.Hash{}, db)
|
||||
state.RevertToSnapshot(state.Snapshot())
|
||||
}
|
||||
|
||||
// use testing instead of checker because checker does not support
|
||||
// printing/logging in tests (-check.vv does not work)
|
||||
func TestSnapshot2(t *testing.T) {
|
||||
@@ -148,30 +155,30 @@ func TestSnapshot2(t *testing.T) {
|
||||
so0 := state.GetStateObject(stateobjaddr0)
|
||||
so0.SetBalance(big.NewInt(42))
|
||||
so0.SetNonce(43)
|
||||
so0.SetCode([]byte{'c', 'a', 'f', 'e'})
|
||||
so0.remove = false
|
||||
so0.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'})
|
||||
so0.suicided = false
|
||||
so0.deleted = false
|
||||
state.SetStateObject(so0)
|
||||
state.setStateObject(so0)
|
||||
|
||||
root, _ := state.Commit()
|
||||
root, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
// and one with deleted == true
|
||||
so1 := state.GetStateObject(stateobjaddr1)
|
||||
so1.SetBalance(big.NewInt(52))
|
||||
so1.SetNonce(53)
|
||||
so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'})
|
||||
so1.remove = true
|
||||
so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'})
|
||||
so1.suicided = true
|
||||
so1.deleted = true
|
||||
state.SetStateObject(so1)
|
||||
state.setStateObject(so1)
|
||||
|
||||
so1 = state.GetStateObject(stateobjaddr1)
|
||||
if so1 != nil {
|
||||
t.Fatalf("deleted object not nil when getting")
|
||||
}
|
||||
|
||||
snapshot := state.Copy()
|
||||
state.Set(snapshot)
|
||||
snapshot := state.Snapshot()
|
||||
state.RevertToSnapshot(snapshot)
|
||||
|
||||
so0Restored := state.GetStateObject(stateobjaddr0)
|
||||
// Update lazily-loaded values before comparing.
|
||||
@@ -207,22 +214,22 @@ func compareStateObjects(so0, so1 *StateObject, t *testing.T) {
|
||||
t.Fatalf("Code mismatch: have %v, want %v", so0.code, so1.code)
|
||||
}
|
||||
|
||||
if len(so1.storage) != len(so0.storage) {
|
||||
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.storage), len(so0.storage))
|
||||
if len(so1.cachedStorage) != len(so0.cachedStorage) {
|
||||
t.Errorf("Storage size mismatch: have %d, want %d", len(so1.cachedStorage), len(so0.cachedStorage))
|
||||
}
|
||||
for k, v := range so1.storage {
|
||||
if so0.storage[k] != v {
|
||||
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.storage[k], v)
|
||||
for k, v := range so1.cachedStorage {
|
||||
if so0.cachedStorage[k] != v {
|
||||
t.Errorf("Storage key %x mismatch: have %v, want %v", k, so0.cachedStorage[k], v)
|
||||
}
|
||||
}
|
||||
for k, v := range so0.storage {
|
||||
if so1.storage[k] != v {
|
||||
for k, v := range so0.cachedStorage {
|
||||
if so1.cachedStorage[k] != v {
|
||||
t.Errorf("Storage key %x mismatch: have %v, want none.", k, v)
|
||||
}
|
||||
}
|
||||
|
||||
if so0.remove != so1.remove {
|
||||
t.Fatalf("Remove mismatch: have %v, want %v", so0.remove, so1.remove)
|
||||
if so0.suicided != so1.suicided {
|
||||
t.Fatalf("suicided mismatch: have %v, want %v", so0.suicided, so1.suicided)
|
||||
}
|
||||
if so0.deleted != so1.deleted {
|
||||
t.Fatalf("Deleted mismatch: have %v, want %v", so0.deleted, so1.deleted)
|
||||
|
||||
@@ -20,10 +20,12 @@ package state
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
@@ -39,12 +41,20 @@ var StartingNonce uint64
|
||||
const (
|
||||
// Number of past tries to keep. The arbitrarily chosen value here
|
||||
// is max uncle depth + 1.
|
||||
maxJournalLength = 8
|
||||
maxPastTries = 8
|
||||
|
||||
// Trie cache generation limit.
|
||||
maxTrieCacheGen = 100
|
||||
|
||||
// Number of codehash->size associations to keep.
|
||||
codeSizeCacheSize = 100000
|
||||
)
|
||||
|
||||
type revision struct {
|
||||
id int
|
||||
journalIndex int
|
||||
}
|
||||
|
||||
// StateDBs within the ethereum protocol are used to store anything
|
||||
// within the merkle trie. StateDBs take care of caching and storing
|
||||
// nested states. It's the general query interface to retrieve:
|
||||
@@ -68,12 +78,18 @@ type StateDB struct {
|
||||
logs map[common.Hash]vm.Logs
|
||||
logSize uint
|
||||
|
||||
// Journal of state modifications. This is the backbone of
|
||||
// Snapshot and RevertToSnapshot.
|
||||
journal journal
|
||||
validRevisions []revision
|
||||
nextRevisionId int
|
||||
|
||||
lock sync.Mutex
|
||||
}
|
||||
|
||||
// Create a new state from a given trie
|
||||
func New(root common.Hash, db ethdb.Database) (*StateDB, error) {
|
||||
tr, err := trie.NewSecure(root, db)
|
||||
tr, err := trie.NewSecure(root, db, maxTrieCacheGen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -123,12 +139,12 @@ func (self *StateDB) Reset(root common.Hash) error {
|
||||
self.trie = tr
|
||||
self.stateObjects = make(map[common.Address]*StateObject)
|
||||
self.stateObjectsDirty = make(map[common.Address]struct{})
|
||||
self.refund = new(big.Int)
|
||||
self.thash = common.Hash{}
|
||||
self.bhash = common.Hash{}
|
||||
self.txIndex = 0
|
||||
self.logs = make(map[common.Hash]vm.Logs)
|
||||
self.logSize = 0
|
||||
self.clearJournalAndRefund()
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -142,14 +158,14 @@ func (self *StateDB) openTrie(root common.Hash) (*trie.SecureTrie, error) {
|
||||
return &tr, nil
|
||||
}
|
||||
}
|
||||
return trie.NewSecure(root, self.db)
|
||||
return trie.NewSecure(root, self.db, maxTrieCacheGen)
|
||||
}
|
||||
|
||||
func (self *StateDB) pushTrie(t *trie.SecureTrie) {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
|
||||
if len(self.pastTries) >= maxJournalLength {
|
||||
if len(self.pastTries) >= maxPastTries {
|
||||
copy(self.pastTries, self.pastTries[1:])
|
||||
self.pastTries[len(self.pastTries)-1] = t
|
||||
} else {
|
||||
@@ -164,6 +180,8 @@ func (self *StateDB) StartRecord(thash, bhash common.Hash, ti int) {
|
||||
}
|
||||
|
||||
func (self *StateDB) AddLog(log *vm.Log) {
|
||||
self.journal = append(self.journal, addLogChange{txhash: self.thash})
|
||||
|
||||
log.TxHash = self.thash
|
||||
log.BlockHash = self.bhash
|
||||
log.TxIndex = uint(self.txIndex)
|
||||
@@ -185,15 +203,19 @@ func (self *StateDB) Logs() vm.Logs {
|
||||
}
|
||||
|
||||
func (self *StateDB) AddRefund(gas *big.Int) {
|
||||
self.journal = append(self.journal, refundChange{prev: new(big.Int).Set(self.refund)})
|
||||
self.refund.Add(self.refund, gas)
|
||||
}
|
||||
|
||||
func (self *StateDB) HasAccount(addr common.Address) bool {
|
||||
// Exist reports whether the given account address exists in the state.
|
||||
// Notably this also returns true for suicided accounts.
|
||||
func (self *StateDB) Exist(addr common.Address) bool {
|
||||
return self.GetStateObject(addr) != nil
|
||||
}
|
||||
|
||||
func (self *StateDB) Exist(addr common.Address) bool {
|
||||
return self.GetStateObject(addr) != nil
|
||||
func (self *StateDB) Empty(addr common.Address) bool {
|
||||
so := self.GetStateObject(addr)
|
||||
return so == nil || so.empty()
|
||||
}
|
||||
|
||||
func (self *StateDB) GetAccount(addr common.Address) vm.Account {
|
||||
@@ -206,7 +228,6 @@ func (self *StateDB) GetBalance(addr common.Address) *big.Int {
|
||||
if stateObject != nil {
|
||||
return stateObject.Balance()
|
||||
}
|
||||
|
||||
return common.Big0
|
||||
}
|
||||
|
||||
@@ -246,6 +267,14 @@ func (self *StateDB) GetCodeSize(addr common.Address) int {
|
||||
return size
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCodeHash(addr common.Address) common.Hash {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject == nil {
|
||||
return common.Hash{}
|
||||
}
|
||||
return common.BytesToHash(stateObject.CodeHash())
|
||||
}
|
||||
|
||||
func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
||||
stateObject := self.GetStateObject(a)
|
||||
if stateObject != nil {
|
||||
@@ -254,10 +283,10 @@ func (self *StateDB) GetState(a common.Address, b common.Hash) common.Hash {
|
||||
return common.Hash{}
|
||||
}
|
||||
|
||||
func (self *StateDB) IsDeleted(addr common.Address) bool {
|
||||
func (self *StateDB) HasSuicided(addr common.Address) bool {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject != nil {
|
||||
return stateObject.remove
|
||||
return stateObject.suicided
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -273,6 +302,13 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetBalance(amount)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@@ -283,34 +319,43 @@ func (self *StateDB) SetNonce(addr common.Address, nonce uint64) {
|
||||
func (self *StateDB) SetCode(addr common.Address, code []byte) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetCode(code)
|
||||
stateObject.SetCode(crypto.Keccak256Hash(code), code)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateDB) SetState(addr common.Address, key common.Hash, value common.Hash) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SetState(key, value)
|
||||
stateObject.SetState(self.db, key, value)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateDB) Delete(addr common.Address) bool {
|
||||
// Suicide marks the given account as suicided.
|
||||
// This clears the account balance.
|
||||
//
|
||||
// The account's state object is still available until the state is committed,
|
||||
// GetStateObject will return a non-nil account after Suicide.
|
||||
func (self *StateDB) Suicide(addr common.Address) bool {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.MarkForDeletion()
|
||||
stateObject.data.Balance = new(big.Int)
|
||||
return true
|
||||
if stateObject == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
self.journal = append(self.journal, suicideChange{
|
||||
account: &addr,
|
||||
prev: stateObject.suicided,
|
||||
prevbalance: new(big.Int).Set(stateObject.Balance()),
|
||||
})
|
||||
stateObject.markSuicided()
|
||||
stateObject.data.Balance = new(big.Int)
|
||||
return true
|
||||
}
|
||||
|
||||
//
|
||||
// Setting, updating & deleting state object methods
|
||||
//
|
||||
|
||||
// Update the given state object and apply it to state trie
|
||||
func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
||||
// updateStateObject writes the given object to the trie.
|
||||
func (self *StateDB) updateStateObject(stateObject *StateObject) {
|
||||
addr := stateObject.Address()
|
||||
data, err := rlp.EncodeToBytes(stateObject)
|
||||
if err != nil {
|
||||
@@ -319,10 +364,9 @@ func (self *StateDB) UpdateStateObject(stateObject *StateObject) {
|
||||
self.trie.Update(addr[:], data)
|
||||
}
|
||||
|
||||
// Delete the given state object and delete it from the state trie
|
||||
func (self *StateDB) DeleteStateObject(stateObject *StateObject) {
|
||||
// deleteStateObject removes the given object from the state trie.
|
||||
func (self *StateDB) deleteStateObject(stateObject *StateObject) {
|
||||
stateObject.deleted = true
|
||||
|
||||
addr := stateObject.Address()
|
||||
self.trie.Delete(addr[:])
|
||||
}
|
||||
@@ -348,12 +392,12 @@ func (self *StateDB) GetStateObject(addr common.Address) (stateObject *StateObje
|
||||
return nil
|
||||
}
|
||||
// Insert into the live set.
|
||||
obj := NewObject(addr, data, self.MarkStateObjectDirty)
|
||||
self.SetStateObject(obj)
|
||||
obj := newObject(self, addr, data, self.MarkStateObjectDirty)
|
||||
self.setStateObject(obj)
|
||||
return obj
|
||||
}
|
||||
|
||||
func (self *StateDB) SetStateObject(object *StateObject) {
|
||||
func (self *StateDB) setStateObject(object *StateObject) {
|
||||
self.stateObjects[object.Address()] = object
|
||||
}
|
||||
|
||||
@@ -361,52 +405,55 @@ func (self *StateDB) SetStateObject(object *StateObject) {
|
||||
func (self *StateDB) GetOrNewStateObject(addr common.Address) *StateObject {
|
||||
stateObject := self.GetStateObject(addr)
|
||||
if stateObject == nil || stateObject.deleted {
|
||||
stateObject = self.CreateStateObject(addr)
|
||||
stateObject, _ = self.createObject(addr)
|
||||
}
|
||||
|
||||
return stateObject
|
||||
}
|
||||
|
||||
// NewStateObject create a state object whether it exist in the trie or not
|
||||
func (self *StateDB) newStateObject(addr common.Address) *StateObject {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("(+) %x\n", addr)
|
||||
}
|
||||
obj := NewObject(addr, Account{}, self.MarkStateObjectDirty)
|
||||
obj.SetNonce(StartingNonce) // sets the object to dirty
|
||||
self.stateObjects[addr] = obj
|
||||
return obj
|
||||
}
|
||||
|
||||
// MarkStateObjectDirty adds the specified object to the dirty map to avoid costly
|
||||
// state object cache iteration to find a handful of modified ones.
|
||||
func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
|
||||
self.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
|
||||
// Creates creates a new state object and takes ownership.
|
||||
func (self *StateDB) CreateStateObject(addr common.Address) *StateObject {
|
||||
// Get previous (if any)
|
||||
so := self.GetStateObject(addr)
|
||||
// Create a new one
|
||||
newSo := self.newStateObject(addr)
|
||||
|
||||
// If it existed set the balance to the new account
|
||||
if so != nil {
|
||||
newSo.data.Balance = so.data.Balance
|
||||
// createObject creates a new state object. If there is an existing account with
|
||||
// the given address, it is overwritten and returned as the second return value.
|
||||
func (self *StateDB) createObject(addr common.Address) (newobj, prev *StateObject) {
|
||||
prev = self.GetStateObject(addr)
|
||||
newobj = newObject(self, addr, Account{}, self.MarkStateObjectDirty)
|
||||
newobj.setNonce(StartingNonce) // sets the object to dirty
|
||||
if prev == nil {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("(+) %x\n", addr)
|
||||
}
|
||||
self.journal = append(self.journal, createObjectChange{account: &addr})
|
||||
} else {
|
||||
self.journal = append(self.journal, resetObjectChange{prev: prev})
|
||||
}
|
||||
|
||||
return newSo
|
||||
self.setStateObject(newobj)
|
||||
return newobj, prev
|
||||
}
|
||||
|
||||
// CreateAccount explicitly creates a state object. If a state object with the address
|
||||
// already exists the balance is carried over to the new account.
|
||||
//
|
||||
// CreateAccount is called during the EVM CREATE operation. The situation might arise that
|
||||
// a contract does the following:
|
||||
//
|
||||
// 1. sends funds to sha(account ++ (nonce + 1))
|
||||
// 2. tx_create(sha(account ++ nonce)) (note that this gets the address of 1)
|
||||
//
|
||||
// Carrying over the balance ensures that Ether doesn't disappear.
|
||||
func (self *StateDB) CreateAccount(addr common.Address) vm.Account {
|
||||
return self.CreateStateObject(addr)
|
||||
new, prev := self.createObject(addr)
|
||||
if prev != nil {
|
||||
new.setBalance(prev.data.Balance)
|
||||
}
|
||||
return new
|
||||
}
|
||||
|
||||
//
|
||||
// Setting, copying of the state methods
|
||||
//
|
||||
|
||||
// Copy creates a deep, independent copy of the state.
|
||||
// Snapshots of the copied state cannot be applied to the copy.
|
||||
func (self *StateDB) Copy() *StateDB {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
@@ -425,7 +472,7 @@ func (self *StateDB) Copy() *StateDB {
|
||||
}
|
||||
// Copy the dirty states and logs
|
||||
for addr, _ := range self.stateObjectsDirty {
|
||||
state.stateObjects[addr] = self.stateObjects[addr].Copy(self.db, state.MarkStateObjectDirty)
|
||||
state.stateObjects[addr] = self.stateObjects[addr].deepCopy(state, state.MarkStateObjectDirty)
|
||||
state.stateObjectsDirty[addr] = struct{}{}
|
||||
}
|
||||
for hash, logs := range self.logs {
|
||||
@@ -435,21 +482,38 @@ func (self *StateDB) Copy() *StateDB {
|
||||
return state
|
||||
}
|
||||
|
||||
func (self *StateDB) Set(state *StateDB) {
|
||||
self.lock.Lock()
|
||||
defer self.lock.Unlock()
|
||||
|
||||
self.db = state.db
|
||||
self.trie = state.trie
|
||||
self.pastTries = state.pastTries
|
||||
self.stateObjects = state.stateObjects
|
||||
self.stateObjectsDirty = state.stateObjectsDirty
|
||||
self.codeSizeCache = state.codeSizeCache
|
||||
self.refund = state.refund
|
||||
self.logs = state.logs
|
||||
self.logSize = state.logSize
|
||||
// Snapshot returns an identifier for the current revision of the state.
|
||||
func (self *StateDB) Snapshot() int {
|
||||
id := self.nextRevisionId
|
||||
self.nextRevisionId++
|
||||
self.validRevisions = append(self.validRevisions, revision{id, len(self.journal)})
|
||||
return id
|
||||
}
|
||||
|
||||
// RevertToSnapshot reverts all state changes made since the given revision.
|
||||
func (self *StateDB) RevertToSnapshot(revid int) {
|
||||
// Find the snapshot in the stack of valid snapshots.
|
||||
idx := sort.Search(len(self.validRevisions), func(i int) bool {
|
||||
return self.validRevisions[i].id >= revid
|
||||
})
|
||||
if idx == len(self.validRevisions) || self.validRevisions[idx].id != revid {
|
||||
panic(fmt.Errorf("revision id %v cannot be reverted", revid))
|
||||
}
|
||||
snapshot := self.validRevisions[idx].journalIndex
|
||||
|
||||
// Replay the journal to undo changes.
|
||||
for i := len(self.journal) - 1; i >= snapshot; i-- {
|
||||
self.journal[i].undo(self)
|
||||
}
|
||||
self.journal = self.journal[:snapshot]
|
||||
|
||||
// Remove invalidated snapshots from the stack.
|
||||
self.validRevisions = self.validRevisions[:idx]
|
||||
}
|
||||
|
||||
// GetRefund returns the current value of the refund counter.
|
||||
// The return value must not be modified by the caller and will become
|
||||
// invalid at the next call to AddRefund.
|
||||
func (self *StateDB) GetRefund() *big.Int {
|
||||
return self.refund
|
||||
}
|
||||
@@ -457,17 +521,18 @@ func (self *StateDB) GetRefund() *big.Int {
|
||||
// IntermediateRoot computes the current root hash of the state trie.
|
||||
// It is called in between transactions to get the root hash that
|
||||
// goes into transaction receipts.
|
||||
func (s *StateDB) IntermediateRoot() common.Hash {
|
||||
s.refund = new(big.Int)
|
||||
func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
|
||||
for addr, _ := range s.stateObjectsDirty {
|
||||
stateObject := s.stateObjects[addr]
|
||||
if stateObject.remove {
|
||||
s.DeleteStateObject(stateObject)
|
||||
if stateObject.suicided || (deleteEmptyObjects && stateObject.empty()) {
|
||||
s.deleteStateObject(stateObject)
|
||||
} else {
|
||||
stateObject.UpdateRoot(s.db)
|
||||
s.UpdateStateObject(stateObject)
|
||||
stateObject.updateRoot(s.db)
|
||||
s.updateStateObject(stateObject)
|
||||
}
|
||||
}
|
||||
// Invalidate journal because reverting across transactions is not allowed.
|
||||
s.clearJournalAndRefund()
|
||||
return s.trie.Hash()
|
||||
}
|
||||
|
||||
@@ -477,15 +542,15 @@ func (s *StateDB) IntermediateRoot() common.Hash {
|
||||
// DeleteSuicides should not be used for consensus related updates
|
||||
// under any circumstances.
|
||||
func (s *StateDB) DeleteSuicides() {
|
||||
// Reset refund so that any used-gas calculations can use
|
||||
// this method.
|
||||
s.refund = new(big.Int)
|
||||
// Reset refund so that any used-gas calculations can use this method.
|
||||
s.clearJournalAndRefund()
|
||||
|
||||
for addr, _ := range s.stateObjectsDirty {
|
||||
stateObject := s.stateObjects[addr]
|
||||
|
||||
// If the object has been removed by a suicide
|
||||
// flag the object as deleted.
|
||||
if stateObject.remove {
|
||||
if stateObject.suicided {
|
||||
stateObject.deleted = true
|
||||
}
|
||||
delete(s.stateObjectsDirty, addr)
|
||||
@@ -493,30 +558,39 @@ func (s *StateDB) DeleteSuicides() {
|
||||
}
|
||||
|
||||
// Commit commits all state changes to the database.
|
||||
func (s *StateDB) Commit() (root common.Hash, err error) {
|
||||
root, batch := s.CommitBatch()
|
||||
func (s *StateDB) Commit(deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
root, batch := s.CommitBatch(deleteEmptyObjects)
|
||||
return root, batch.Write()
|
||||
}
|
||||
|
||||
// CommitBatch commits all state changes to a write batch but does not
|
||||
// execute the batch. It is used to validate state changes against
|
||||
// the root hash stored in a block.
|
||||
func (s *StateDB) CommitBatch() (root common.Hash, batch ethdb.Batch) {
|
||||
func (s *StateDB) CommitBatch(deleteEmptyObjects bool) (root common.Hash, batch ethdb.Batch) {
|
||||
batch = s.db.NewBatch()
|
||||
root, _ = s.commit(batch)
|
||||
root, _ = s.commit(batch, deleteEmptyObjects)
|
||||
|
||||
return root, batch
|
||||
}
|
||||
|
||||
func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error) {
|
||||
func (s *StateDB) clearJournalAndRefund() {
|
||||
s.journal = nil
|
||||
s.validRevisions = s.validRevisions[:0]
|
||||
s.refund = new(big.Int)
|
||||
}
|
||||
|
||||
func (s *StateDB) commit(dbw trie.DatabaseWriter, deleteEmptyObjects bool) (root common.Hash, err error) {
|
||||
defer s.clearJournalAndRefund()
|
||||
|
||||
// Commit objects to the trie.
|
||||
for addr, stateObject := range s.stateObjects {
|
||||
if stateObject.remove {
|
||||
_, isDirty := s.stateObjectsDirty[addr]
|
||||
switch {
|
||||
case stateObject.suicided || (isDirty && deleteEmptyObjects && stateObject.empty()):
|
||||
// If the object has been removed, don't bother syncing it
|
||||
// and just mark it for deletion in the trie.
|
||||
s.DeleteStateObject(stateObject)
|
||||
} else if _, ok := s.stateObjectsDirty[addr]; ok {
|
||||
s.deleteStateObject(stateObject)
|
||||
case isDirty:
|
||||
// Write any contract code associated with the state object
|
||||
if stateObject.code != nil && stateObject.dirtyCode {
|
||||
if err := dbw.Put(stateObject.CodeHash(), stateObject.code); err != nil {
|
||||
@@ -529,7 +603,7 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
|
||||
return common.Hash{}, err
|
||||
}
|
||||
// Update the object in the main account trie.
|
||||
s.UpdateStateObject(stateObject)
|
||||
s.updateStateObject(stateObject)
|
||||
}
|
||||
delete(s.stateObjectsDirty, addr)
|
||||
}
|
||||
@@ -540,7 +614,3 @@ func (s *StateDB) commit(dbw trie.DatabaseWriter) (root common.Hash, err error)
|
||||
}
|
||||
return root, err
|
||||
}
|
||||
|
||||
func (self *StateDB) Refunds() *big.Int {
|
||||
return self.refund
|
||||
}
|
||||
|
||||
@@ -17,10 +17,19 @@
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"testing/quick"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
)
|
||||
|
||||
@@ -33,16 +42,16 @@ func TestUpdateLeaks(t *testing.T) {
|
||||
|
||||
// Update it with some accounts
|
||||
for i := byte(0); i < 255; i++ {
|
||||
obj := state.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
obj.AddBalance(big.NewInt(int64(11 * i)))
|
||||
obj.SetNonce(uint64(42 * i))
|
||||
addr := common.BytesToAddress([]byte{i})
|
||||
state.AddBalance(addr, big.NewInt(int64(11*i)))
|
||||
state.SetNonce(addr, uint64(42*i))
|
||||
if i%2 == 0 {
|
||||
obj.SetState(common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||
state.SetState(addr, common.BytesToHash([]byte{i, i, i}), common.BytesToHash([]byte{i, i, i, i}))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
obj.SetCode([]byte{i, i, i, i, i})
|
||||
state.SetCode(addr, []byte{i, i, i, i, i})
|
||||
}
|
||||
state.UpdateStateObject(obj)
|
||||
state.IntermediateRoot(false)
|
||||
}
|
||||
// Ensure that no data was leaked into the database
|
||||
for _, key := range db.Keys() {
|
||||
@@ -60,51 +69,38 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
transState, _ := New(common.Hash{}, transDb)
|
||||
finalState, _ := New(common.Hash{}, finalDb)
|
||||
|
||||
// Update the states with some objects
|
||||
for i := byte(0); i < 255; i++ {
|
||||
// Create a new state object with some data into the transition database
|
||||
obj := transState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
obj.SetBalance(big.NewInt(int64(11 * i)))
|
||||
obj.SetNonce(uint64(42 * i))
|
||||
modify := func(state *StateDB, addr common.Address, i, tweak byte) {
|
||||
state.SetBalance(addr, big.NewInt(int64(11*i)+int64(tweak)))
|
||||
state.SetNonce(addr, uint64(42*i+tweak))
|
||||
if i%2 == 0 {
|
||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0}))
|
||||
state.SetState(addr, common.Hash{i, i, i, 0}, common.Hash{})
|
||||
state.SetState(addr, common.Hash{i, i, i, tweak}, common.Hash{i, i, i, i, tweak})
|
||||
}
|
||||
if i%3 == 0 {
|
||||
obj.SetCode([]byte{i, i, i, i, i, 0})
|
||||
state.SetCode(addr, []byte{i, i, i, i, i, tweak})
|
||||
}
|
||||
transState.UpdateStateObject(obj)
|
||||
|
||||
// Overwrite all the data with new values in the transition database
|
||||
obj.SetBalance(big.NewInt(int64(11*i + 1)))
|
||||
obj.SetNonce(uint64(42*i + 1))
|
||||
if i%2 == 0 {
|
||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.Hash{})
|
||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
obj.SetCode([]byte{i, i, i, i, i, 1})
|
||||
}
|
||||
transState.UpdateStateObject(obj)
|
||||
|
||||
// Create the final state object directly in the final database
|
||||
obj = finalState.GetOrNewStateObject(common.BytesToAddress([]byte{i}))
|
||||
obj.SetBalance(big.NewInt(int64(11*i + 1)))
|
||||
obj.SetNonce(uint64(42*i + 1))
|
||||
if i%2 == 0 {
|
||||
obj.SetState(common.BytesToHash([]byte{i, i, i, 1}), common.BytesToHash([]byte{i, i, i, i, 1}))
|
||||
}
|
||||
if i%3 == 0 {
|
||||
obj.SetCode([]byte{i, i, i, i, i, 1})
|
||||
}
|
||||
finalState.UpdateStateObject(obj)
|
||||
}
|
||||
if _, err := transState.Commit(); err != nil {
|
||||
|
||||
// Modify the transient state.
|
||||
for i := byte(0); i < 255; i++ {
|
||||
modify(transState, common.Address{byte(i)}, i, 0)
|
||||
}
|
||||
// Write modifications to trie.
|
||||
transState.IntermediateRoot(false)
|
||||
|
||||
// Overwrite all the data with new values in the transient database.
|
||||
for i := byte(0); i < 255; i++ {
|
||||
modify(transState, common.Address{byte(i)}, i, 99)
|
||||
modify(finalState, common.Address{byte(i)}, i, 99)
|
||||
}
|
||||
|
||||
// Commit and cross check the databases.
|
||||
if _, err := transState.Commit(false); err != nil {
|
||||
t.Fatalf("failed to commit transition state: %v", err)
|
||||
}
|
||||
if _, err := finalState.Commit(); err != nil {
|
||||
if _, err := finalState.Commit(false); err != nil {
|
||||
t.Fatalf("failed to commit final state: %v", err)
|
||||
}
|
||||
// Cross check the databases to ensure they are the same
|
||||
for _, key := range finalDb.Keys() {
|
||||
if _, err := transDb.Get(key); err != nil {
|
||||
val, _ := finalDb.Get(key)
|
||||
@@ -118,3 +114,243 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSnapshotRandom(t *testing.T) {
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*snapshotTest).run, config)
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
test := cerr.In[0].(*snapshotTest)
|
||||
t.Errorf("%v:\n%s", test.err, test)
|
||||
} else if err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// A snapshotTest checks that reverting StateDB snapshots properly undoes all changes
|
||||
// captured by the snapshot. Instances of this test with pseudorandom content are created
|
||||
// by Generate.
|
||||
//
|
||||
// The test works as follows:
|
||||
//
|
||||
// A new state is created and all actions are applied to it. Several snapshots are taken
|
||||
// in between actions. The test then reverts each snapshot. For each snapshot the actions
|
||||
// leading up to it are replayed on a fresh, empty state. The behaviour of all public
|
||||
// accessor methods on the reverted state must match the return value of the equivalent
|
||||
// methods on the replayed state.
|
||||
type snapshotTest struct {
|
||||
addrs []common.Address // all account addresses
|
||||
actions []testAction // modifications to the state
|
||||
snapshots []int // actions indexes at which snapshot is taken
|
||||
err error // failure details are reported through this field
|
||||
}
|
||||
|
||||
type testAction struct {
|
||||
name string
|
||||
fn func(testAction, *StateDB)
|
||||
args []int64
|
||||
noAddr bool
|
||||
}
|
||||
|
||||
// newTestAction creates a random action that changes state.
|
||||
func newTestAction(addr common.Address, r *rand.Rand) testAction {
|
||||
actions := []testAction{
|
||||
{
|
||||
name: "SetBalance",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetBalance(addr, big.NewInt(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "AddBalance",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddBalance(addr, big.NewInt(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "SetNonce",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.SetNonce(addr, uint64(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
{
|
||||
name: "SetState",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
var key, val common.Hash
|
||||
binary.BigEndian.PutUint16(key[:], uint16(a.args[0]))
|
||||
binary.BigEndian.PutUint16(val[:], uint16(a.args[1]))
|
||||
s.SetState(addr, key, val)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
{
|
||||
name: "SetCode",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
code := make([]byte, 16)
|
||||
binary.BigEndian.PutUint64(code, uint64(a.args[0]))
|
||||
binary.BigEndian.PutUint64(code[8:], uint64(a.args[1]))
|
||||
s.SetCode(addr, code)
|
||||
},
|
||||
args: make([]int64, 2),
|
||||
},
|
||||
{
|
||||
name: "CreateAccount",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.CreateAccount(addr)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Suicide",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.Suicide(addr)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "AddRefund",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
s.AddRefund(big.NewInt(a.args[0]))
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
noAddr: true,
|
||||
},
|
||||
{
|
||||
name: "AddLog",
|
||||
fn: func(a testAction, s *StateDB) {
|
||||
data := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(data, uint16(a.args[0]))
|
||||
s.AddLog(&vm.Log{Address: addr, Data: data})
|
||||
},
|
||||
args: make([]int64, 1),
|
||||
},
|
||||
}
|
||||
action := actions[r.Intn(len(actions))]
|
||||
var nameargs []string
|
||||
if !action.noAddr {
|
||||
nameargs = append(nameargs, addr.Hex())
|
||||
}
|
||||
for _, i := range action.args {
|
||||
action.args[i] = rand.Int63n(100)
|
||||
nameargs = append(nameargs, fmt.Sprint(action.args[i]))
|
||||
}
|
||||
action.name += strings.Join(nameargs, ", ")
|
||||
return action
|
||||
}
|
||||
|
||||
// Generate returns a new snapshot test of the given size. All randomness is
|
||||
// derived from r.
|
||||
func (*snapshotTest) Generate(r *rand.Rand, size int) reflect.Value {
|
||||
// Generate random actions.
|
||||
addrs := make([]common.Address, 50)
|
||||
for i := range addrs {
|
||||
addrs[i][0] = byte(i)
|
||||
}
|
||||
actions := make([]testAction, size)
|
||||
for i := range actions {
|
||||
addr := addrs[r.Intn(len(addrs))]
|
||||
actions[i] = newTestAction(addr, r)
|
||||
}
|
||||
// Generate snapshot indexes.
|
||||
nsnapshots := int(math.Sqrt(float64(size)))
|
||||
if size > 0 && nsnapshots == 0 {
|
||||
nsnapshots = 1
|
||||
}
|
||||
snapshots := make([]int, nsnapshots)
|
||||
snaplen := len(actions) / nsnapshots
|
||||
for i := range snapshots {
|
||||
// Try to place the snapshots some number of actions apart from each other.
|
||||
snapshots[i] = (i * snaplen) + r.Intn(snaplen)
|
||||
}
|
||||
return reflect.ValueOf(&snapshotTest{addrs, actions, snapshots, nil})
|
||||
}
|
||||
|
||||
func (test *snapshotTest) String() string {
|
||||
out := new(bytes.Buffer)
|
||||
sindex := 0
|
||||
for i, action := range test.actions {
|
||||
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
||||
fmt.Fprintf(out, "---- snapshot %d ----\n", sindex)
|
||||
sindex++
|
||||
}
|
||||
fmt.Fprintf(out, "%4d: %s\n", i, action.name)
|
||||
}
|
||||
return out.String()
|
||||
}
|
||||
|
||||
func (test *snapshotTest) run() bool {
|
||||
// Run all actions and create snapshots.
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
state, _ = New(common.Hash{}, db)
|
||||
snapshotRevs = make([]int, len(test.snapshots))
|
||||
sindex = 0
|
||||
)
|
||||
for i, action := range test.actions {
|
||||
if len(test.snapshots) > sindex && i == test.snapshots[sindex] {
|
||||
snapshotRevs[sindex] = state.Snapshot()
|
||||
sindex++
|
||||
}
|
||||
action.fn(action, state)
|
||||
}
|
||||
|
||||
// Revert all snapshots in reverse order. Each revert must yield a state
|
||||
// that is equivalent to fresh state with all actions up the snapshot applied.
|
||||
for sindex--; sindex >= 0; sindex-- {
|
||||
checkstate, _ := New(common.Hash{}, db)
|
||||
for _, action := range test.actions[:test.snapshots[sindex]] {
|
||||
action.fn(action, checkstate)
|
||||
}
|
||||
state.RevertToSnapshot(snapshotRevs[sindex])
|
||||
if err := test.checkEqual(state, checkstate); err != nil {
|
||||
test.err = fmt.Errorf("state mismatch after revert to snapshot %d\n%v", sindex, err)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// checkEqual checks that methods of state and checkstate return the same values.
|
||||
func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
for _, addr := range test.addrs {
|
||||
var err error
|
||||
checkeq := func(op string, a, b interface{}) bool {
|
||||
if err == nil && !reflect.DeepEqual(a, b) {
|
||||
err = fmt.Errorf("got %s(%s) == %v, want %v", op, addr.Hex(), a, b)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
// Check basic accessor methods.
|
||||
checkeq("Exist", state.Exist(addr), checkstate.Exist(addr))
|
||||
checkeq("HasSuicided", state.HasSuicided(addr), checkstate.HasSuicided(addr))
|
||||
checkeq("GetBalance", state.GetBalance(addr), checkstate.GetBalance(addr))
|
||||
checkeq("GetNonce", state.GetNonce(addr), checkstate.GetNonce(addr))
|
||||
checkeq("GetCode", state.GetCode(addr), checkstate.GetCode(addr))
|
||||
checkeq("GetCodeHash", state.GetCodeHash(addr), checkstate.GetCodeHash(addr))
|
||||
checkeq("GetCodeSize", state.GetCodeSize(addr), checkstate.GetCodeSize(addr))
|
||||
// Check storage.
|
||||
if obj := state.GetStateObject(addr); obj != nil {
|
||||
obj.ForEachStorage(func(key, val common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", val, checkstate.GetState(addr, key))
|
||||
})
|
||||
checkobj := checkstate.GetStateObject(addr)
|
||||
checkobj.ForEachStorage(func(key, checkval common.Hash) bool {
|
||||
return checkeq("GetState("+key.Hex()+")", state.GetState(addr, key), checkval)
|
||||
})
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if state.GetRefund().Cmp(checkstate.GetRefund()) != 0 {
|
||||
return fmt.Errorf("got GetRefund() == %d, want GetRefund() == %d",
|
||||
state.GetRefund(), checkstate.GetRefund())
|
||||
}
|
||||
if !reflect.DeepEqual(state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{})) {
|
||||
return fmt.Errorf("got GetLogs(common.Hash{}) == %v, want GetLogs(common.Hash{}) == %v",
|
||||
state.GetLogs(common.Hash{}), checkstate.GetLogs(common.Hash{}))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -54,13 +54,13 @@ func makeTestState() (ethdb.Database, common.Hash, []*testAccount) {
|
||||
acc.nonce = uint64(42 * i)
|
||||
|
||||
if i%3 == 0 {
|
||||
obj.SetCode([]byte{i, i, i, i, i})
|
||||
obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i})
|
||||
acc.code = []byte{i, i, i, i, i}
|
||||
}
|
||||
state.UpdateStateObject(obj)
|
||||
state.updateStateObject(obj)
|
||||
accounts = append(accounts, acc)
|
||||
}
|
||||
root, _ := state.Commit()
|
||||
root, _ := state.Commit(false)
|
||||
|
||||
// Return the generated state
|
||||
return db, root, accounts
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -37,12 +38,12 @@ var (
|
||||
//
|
||||
// StateProcessor implements Processor.
|
||||
type StateProcessor struct {
|
||||
config *ChainConfig
|
||||
config *params.ChainConfig
|
||||
bc *BlockChain
|
||||
}
|
||||
|
||||
// NewStateProcessor initialises a new StateProcessor.
|
||||
func NewStateProcessor(config *ChainConfig, bc *BlockChain) *StateProcessor {
|
||||
func NewStateProcessor(config *params.ChainConfig, bc *BlockChain) *StateProcessor {
|
||||
return &StateProcessor{
|
||||
config: config,
|
||||
bc: bc,
|
||||
@@ -89,7 +90,18 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
//
|
||||
// ApplyTransactions returns the generated receipts and vm logs during the
|
||||
// execution of the state transition phase.
|
||||
func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, statedb *state.StateDB, header *types.Header, tx *types.Transaction, usedGas *big.Int, cfg vm.Config) (*types.Receipt, vm.Logs, *big.Int, error) {
|
||||
var signer types.Signer
|
||||
switch {
|
||||
case config.IsEIP155(header.Number):
|
||||
signer = types.NewEIP155Signer(config.ChainId)
|
||||
case config.IsHomestead(header.Number):
|
||||
signer = types.HomesteadSigner{}
|
||||
default:
|
||||
signer = types.FrontierSigner{}
|
||||
}
|
||||
tx.SetSigner(signer)
|
||||
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, tx, header, cfg), tx, gp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
@@ -97,7 +109,7 @@ func ApplyTransaction(config *ChainConfig, bc *BlockChain, gp *GasPool, statedb
|
||||
|
||||
// Update the state with pending changes
|
||||
usedGas.Add(usedGas, gas)
|
||||
receipt := types.NewReceipt(statedb.IntermediateRoot().Bytes(), usedGas)
|
||||
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas)
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = new(big.Int).Set(gas)
|
||||
if MessageCreatesContract(tx) {
|
||||
|
||||
@@ -138,7 +138,7 @@ func (self *StateTransition) from() (vm.Account, error) {
|
||||
f common.Address
|
||||
err error
|
||||
)
|
||||
if self.env.RuleSet().IsHomestead(self.env.BlockNumber()) {
|
||||
if self.env.ChainConfig().IsHomestead(self.env.BlockNumber()) {
|
||||
f, err = self.msg.From()
|
||||
} else {
|
||||
f, err = self.msg.FromFrontier()
|
||||
@@ -231,7 +231,7 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||
msg := self.msg
|
||||
sender, _ := self.from() // err checked in preCheck
|
||||
|
||||
homestead := self.env.RuleSet().IsHomestead(self.env.BlockNumber())
|
||||
homestead := self.env.ChainConfig().IsHomestead(self.env.BlockNumber())
|
||||
contractCreation := MessageCreatesContract(msg)
|
||||
// Pay intrinsic gas
|
||||
if err = self.useGas(IntrinsicGas(self.data, contractCreation, homestead)); err != nil {
|
||||
|
||||
342
core/tx_list.go
Normal file
342
core/tx_list.go
Normal file
@@ -0,0 +1,342 @@
|
||||
// Copyright 2016 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 core
|
||||
|
||||
import (
|
||||
"container/heap"
|
||||
"math"
|
||||
"math/big"
|
||||
"sort"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
)
|
||||
|
||||
// nonceHeap is a heap.Interface implementation over 64bit unsigned integers for
|
||||
// retrieving sorted transactions from the possibly gapped future queue.
|
||||
type nonceHeap []uint64
|
||||
|
||||
func (h nonceHeap) Len() int { return len(h) }
|
||||
func (h nonceHeap) Less(i, j int) bool { return h[i] < h[j] }
|
||||
func (h nonceHeap) Swap(i, j int) { h[i], h[j] = h[j], h[i] }
|
||||
|
||||
func (h *nonceHeap) Push(x interface{}) {
|
||||
*h = append(*h, x.(uint64))
|
||||
}
|
||||
|
||||
func (h *nonceHeap) Pop() interface{} {
|
||||
old := *h
|
||||
n := len(old)
|
||||
x := old[n-1]
|
||||
*h = old[0 : n-1]
|
||||
return x
|
||||
}
|
||||
|
||||
// txSortedMap is a nonce->transaction hash map with a heap based index to allow
|
||||
// iterating over the contents in a nonce-incrementing way.
|
||||
type txSortedMap struct {
|
||||
items map[uint64]*types.Transaction // Hash map storing the transaction data
|
||||
index *nonceHeap // Heap of nonces of all the stored transactions (non-strict mode)
|
||||
cache types.Transactions // Cache of the transactions already sorted
|
||||
}
|
||||
|
||||
// newTxSortedMap creates a new sorted transaction map.
|
||||
func newTxSortedMap() *txSortedMap {
|
||||
return &txSortedMap{
|
||||
items: make(map[uint64]*types.Transaction),
|
||||
index: &nonceHeap{},
|
||||
}
|
||||
}
|
||||
|
||||
// Get retrieves the current transactions associated with the given nonce.
|
||||
func (m *txSortedMap) Get(nonce uint64) *types.Transaction {
|
||||
return m.items[nonce]
|
||||
}
|
||||
|
||||
// Put inserts a new transaction into the map, also updating the map's nonce
|
||||
// index. If a transaction already exists with the same nonce, it's overwritten.
|
||||
func (m *txSortedMap) Put(tx *types.Transaction) {
|
||||
nonce := tx.Nonce()
|
||||
if m.items[nonce] == nil {
|
||||
heap.Push(m.index, nonce)
|
||||
}
|
||||
m.items[nonce], m.cache = tx, nil
|
||||
}
|
||||
|
||||
// Forward removes all transactions from the map with a nonce lower than the
|
||||
// provided threshold. Every removed transaction is returned for any post-removal
|
||||
// maintenance.
|
||||
func (m *txSortedMap) Forward(threshold uint64) types.Transactions {
|
||||
var removed types.Transactions
|
||||
|
||||
// Pop off heap items until the threshold is reached
|
||||
for m.index.Len() > 0 && (*m.index)[0] < threshold {
|
||||
nonce := heap.Pop(m.index).(uint64)
|
||||
removed = append(removed, m.items[nonce])
|
||||
delete(m.items, nonce)
|
||||
}
|
||||
// If we had a cached order, shift the front
|
||||
if m.cache != nil {
|
||||
m.cache = m.cache[len(removed):]
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
// Filter iterates over the list of transactions and removes all of them for which
|
||||
// the specified function evaluates to true.
|
||||
func (m *txSortedMap) Filter(filter func(*types.Transaction) bool) types.Transactions {
|
||||
var removed types.Transactions
|
||||
|
||||
// Collect all the transactions to filter out
|
||||
for nonce, tx := range m.items {
|
||||
if filter(tx) {
|
||||
removed = append(removed, tx)
|
||||
delete(m.items, nonce)
|
||||
}
|
||||
}
|
||||
// If transactions were removed, the heap and cache are ruined
|
||||
if len(removed) > 0 {
|
||||
*m.index = make([]uint64, 0, len(m.items))
|
||||
for nonce, _ := range m.items {
|
||||
*m.index = append(*m.index, nonce)
|
||||
}
|
||||
heap.Init(m.index)
|
||||
|
||||
m.cache = nil
|
||||
}
|
||||
return removed
|
||||
}
|
||||
|
||||
// Cap places a hard limit on the number of items, returning all transactions
|
||||
// exceeding that limit.
|
||||
func (m *txSortedMap) Cap(threshold int) types.Transactions {
|
||||
// Short circuit if the number of items is under the limit
|
||||
if len(m.items) <= threshold {
|
||||
return nil
|
||||
}
|
||||
// Otherwise gather and drop the highest nonce'd transactions
|
||||
var drops types.Transactions
|
||||
|
||||
sort.Sort(*m.index)
|
||||
for size := len(m.items); size > threshold; size-- {
|
||||
drops = append(drops, m.items[(*m.index)[size-1]])
|
||||
delete(m.items, (*m.index)[size-1])
|
||||
}
|
||||
*m.index = (*m.index)[:threshold]
|
||||
heap.Init(m.index)
|
||||
|
||||
// If we had a cache, shift the back
|
||||
if m.cache != nil {
|
||||
m.cache = m.cache[:len(m.cache)-len(drops)]
|
||||
}
|
||||
return drops
|
||||
}
|
||||
|
||||
// Remove deletes a transaction from the maintained map, returning whether the
|
||||
// transaction was found.
|
||||
func (m *txSortedMap) Remove(nonce uint64) bool {
|
||||
// Short circuit if no transaction is present
|
||||
_, ok := m.items[nonce]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
// Otherwise delete the transaction and fix the heap index
|
||||
for i := 0; i < m.index.Len(); i++ {
|
||||
if (*m.index)[i] == nonce {
|
||||
heap.Remove(m.index, i)
|
||||
break
|
||||
}
|
||||
}
|
||||
delete(m.items, nonce)
|
||||
m.cache = nil
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// Ready retrieves a sequentially increasing list of transactions starting at the
|
||||
// provided nonce that is ready for processing. The returned transactions will be
|
||||
// removed from the list.
|
||||
//
|
||||
// Note, all transactions with nonces lower than start will also be returned to
|
||||
// prevent getting into and invalid state. This is not something that should ever
|
||||
// happen but better to be self correcting than failing!
|
||||
func (m *txSortedMap) Ready(start uint64) types.Transactions {
|
||||
// Short circuit if no transactions are available
|
||||
if m.index.Len() == 0 || (*m.index)[0] > start {
|
||||
return nil
|
||||
}
|
||||
// Otherwise start accumulating incremental transactions
|
||||
var ready types.Transactions
|
||||
for next := (*m.index)[0]; m.index.Len() > 0 && (*m.index)[0] == next; next++ {
|
||||
ready = append(ready, m.items[next])
|
||||
delete(m.items, next)
|
||||
heap.Pop(m.index)
|
||||
}
|
||||
m.cache = nil
|
||||
|
||||
return ready
|
||||
}
|
||||
|
||||
// Len returns the length of the transaction map.
|
||||
func (m *txSortedMap) Len() int {
|
||||
return len(m.items)
|
||||
}
|
||||
|
||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (m *txSortedMap) Flatten() types.Transactions {
|
||||
// If the sorting was not cached yet, create and cache it
|
||||
if m.cache == nil {
|
||||
m.cache = make(types.Transactions, 0, len(m.items))
|
||||
for _, tx := range m.items {
|
||||
m.cache = append(m.cache, tx)
|
||||
}
|
||||
sort.Sort(types.TxByNonce(m.cache))
|
||||
}
|
||||
// Copy the cache to prevent accidental modifications
|
||||
txs := make(types.Transactions, len(m.cache))
|
||||
copy(txs, m.cache)
|
||||
return txs
|
||||
}
|
||||
|
||||
// txList is a "list" of transactions belonging to an account, sorted by account
|
||||
// nonce. The same type can be used both for storing contiguous transactions for
|
||||
// the executable/pending queue; and for storing gapped transactions for the non-
|
||||
// executable/future queue, with minor behavoiral changes.
|
||||
type txList struct {
|
||||
strict bool // Whether nonces are strictly continuous or not
|
||||
txs *txSortedMap // Heap indexed sorted hash map of the transactions
|
||||
costcap *big.Int // Price of the highest costing transaction (reset only if exceeds balance)
|
||||
}
|
||||
|
||||
// newTxList create a new transaction list for maintaining nonce-indexable fast,
|
||||
// gapped, sortable transaction lists.
|
||||
func newTxList(strict bool) *txList {
|
||||
return &txList{
|
||||
strict: strict,
|
||||
txs: newTxSortedMap(),
|
||||
costcap: new(big.Int),
|
||||
}
|
||||
}
|
||||
|
||||
// Add tries to insert a new transaction into the list, returning whether the
|
||||
// transaction was accepted, and if yes, any previous transaction it replaced.
|
||||
//
|
||||
// If the new transaction is accepted into the list, the lists' cost threshold
|
||||
// is also potentially updated.
|
||||
func (l *txList) Add(tx *types.Transaction) (bool, *types.Transaction) {
|
||||
// If there's an older better transaction, abort
|
||||
old := l.txs.Get(tx.Nonce())
|
||||
if old != nil && old.GasPrice().Cmp(tx.GasPrice()) >= 0 {
|
||||
return false, nil
|
||||
}
|
||||
// Otherwise overwrite the old transaction with the current one
|
||||
l.txs.Put(tx)
|
||||
if cost := tx.Cost(); l.costcap.Cmp(cost) < 0 {
|
||||
l.costcap = cost
|
||||
}
|
||||
return true, old
|
||||
}
|
||||
|
||||
// Forward removes all transactions from the list with a nonce lower than the
|
||||
// provided threshold. Every removed transaction is returned for any post-removal
|
||||
// maintenance.
|
||||
func (l *txList) Forward(threshold uint64) types.Transactions {
|
||||
return l.txs.Forward(threshold)
|
||||
}
|
||||
|
||||
// Filter removes all transactions from the list with a cost higher than the
|
||||
// provided threshold. Every removed transaction is returned for any post-removal
|
||||
// maintenance. Strict-mode invalidated transactions are also returned.
|
||||
//
|
||||
// This method uses the cached costcap to quickly decide if there's even a point
|
||||
// in calculating all the costs or if the balance covers all. If the threshold is
|
||||
// lower than the costcap, the costcap will be reset to a new high after removing
|
||||
// expensive the too transactions.
|
||||
func (l *txList) Filter(threshold *big.Int) (types.Transactions, types.Transactions) {
|
||||
// If all transactions are below the threshold, short circuit
|
||||
if l.costcap.Cmp(threshold) <= 0 {
|
||||
return nil, nil
|
||||
}
|
||||
l.costcap = new(big.Int).Set(threshold) // Lower the cap to the threshold
|
||||
|
||||
// Filter out all the transactions above the account's funds
|
||||
removed := l.txs.Filter(func(tx *types.Transaction) bool { return tx.Cost().Cmp(threshold) > 0 })
|
||||
|
||||
// If the list was strict, filter anything above the lowest nonce
|
||||
var invalids types.Transactions
|
||||
if l.strict && len(removed) > 0 {
|
||||
lowest := uint64(math.MaxUint64)
|
||||
for _, tx := range removed {
|
||||
if nonce := tx.Nonce(); lowest > nonce {
|
||||
lowest = nonce
|
||||
}
|
||||
}
|
||||
invalids = l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > lowest })
|
||||
}
|
||||
return removed, invalids
|
||||
}
|
||||
|
||||
// Cap places a hard limit on the number of items, returning all transactions
|
||||
// exceeding that limit.
|
||||
func (l *txList) Cap(threshold int) types.Transactions {
|
||||
return l.txs.Cap(threshold)
|
||||
}
|
||||
|
||||
// Remove deletes a transaction from the maintained list, returning whether the
|
||||
// transaction was found, and also returning any transaction invalidated due to
|
||||
// the deletion (strict mode only).
|
||||
func (l *txList) Remove(tx *types.Transaction) (bool, types.Transactions) {
|
||||
// Remove the transaction from the set
|
||||
nonce := tx.Nonce()
|
||||
if removed := l.txs.Remove(nonce); !removed {
|
||||
return false, nil
|
||||
}
|
||||
// In strict mode, filter out non-executable transactions
|
||||
if l.strict {
|
||||
return true, l.txs.Filter(func(tx *types.Transaction) bool { return tx.Nonce() > nonce })
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Ready retrieves a sequentially increasing list of transactions starting at the
|
||||
// provided nonce that is ready for processing. The returned transactions will be
|
||||
// removed from the list.
|
||||
//
|
||||
// Note, all transactions with nonces lower than start will also be returned to
|
||||
// prevent getting into and invalid state. This is not something that should ever
|
||||
// happen but better to be self correcting than failing!
|
||||
func (l *txList) Ready(start uint64) types.Transactions {
|
||||
return l.txs.Ready(start)
|
||||
}
|
||||
|
||||
// Len returns the length of the transaction list.
|
||||
func (l *txList) Len() int {
|
||||
return l.txs.Len()
|
||||
}
|
||||
|
||||
// Empty returns whether the list of transactions is empty or not.
|
||||
func (l *txList) Empty() bool {
|
||||
return l.Len() == 0
|
||||
}
|
||||
|
||||
// Flatten creates a nonce-sorted slice of transactions based on the loosely
|
||||
// sorted internal representation. The result of the sorting is cached in case
|
||||
// it's requested again before any modifications are made to the contents.
|
||||
func (l *txList) Flatten() types.Transactions {
|
||||
return l.txs.Flatten()
|
||||
}
|
||||
52
core/tx_list_test.go
Normal file
52
core/tx_list_test.go
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2016 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 core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
// Tests that transactions can be added to strict lists and list contents and
|
||||
// nonce boundaries are correctly maintained.
|
||||
func TestStrictTxListAdd(t *testing.T) {
|
||||
// Generate a list of transactions to insert
|
||||
key, _ := crypto.GenerateKey()
|
||||
|
||||
txs := make(types.Transactions, 1024)
|
||||
for i := 0; i < len(txs); i++ {
|
||||
txs[i] = transaction(uint64(i), new(big.Int), key)
|
||||
}
|
||||
// Insert the transactions in a random order
|
||||
list := newTxList(true)
|
||||
for _, v := range rand.Perm(len(txs)) {
|
||||
list.Add(txs[v])
|
||||
}
|
||||
// Verify internal state
|
||||
if len(list.txs.items) != len(txs) {
|
||||
t.Errorf("transaction count mismatch: have %d, want %d", len(list.txs.items), len(txs))
|
||||
}
|
||||
for i, tx := range txs {
|
||||
if list.txs.items[tx.Nonce()] != tx {
|
||||
t.Errorf("item %d: transaction mismatch: have %v, want %v", i, list.txs.items[tx.Nonce()], tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
695
core/tx_pool.go
695
core/tx_pool.go
@@ -30,6 +30,8 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"gopkg.in/karalabe/cookiejar.v2/collections/prque"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -45,8 +47,13 @@ var (
|
||||
ErrNegativeValue = errors.New("Negative value")
|
||||
)
|
||||
|
||||
const (
|
||||
maxQueued = 64 // max limit of queued txs per address
|
||||
var (
|
||||
minPendingPerAccount = uint64(16) // Min number of guaranteed transaction slots per address
|
||||
maxPendingTotal = uint64(4096) // Max limit of pending transactions from all accounts (soft)
|
||||
maxQueuedPerAccount = uint64(64) // Max limit of queued transactions per address
|
||||
maxQueuedInTotal = uint64(1024) // Max limit of queued transactions from all accounts
|
||||
maxQueuedLifetime = 3 * time.Hour // Max amount of time transactions from idle accounts are queued
|
||||
evictionInterval = time.Minute // Time interval to check for evictable transactions
|
||||
)
|
||||
|
||||
type stateFn func() (*state.StateDB, error)
|
||||
@@ -59,7 +66,9 @@ type stateFn func() (*state.StateDB, error)
|
||||
// current state) and future transactions. Transactions move between those
|
||||
// two states over time as they are received and processed.
|
||||
type TxPool struct {
|
||||
config *ChainConfig
|
||||
config *params.ChainConfig
|
||||
signer types.Signer
|
||||
|
||||
currentState stateFn // The state function which will allow us to do some pre checks
|
||||
pendingState *state.ManagedState
|
||||
gasLimit func() *big.Int // The current gas limit function callback
|
||||
@@ -68,19 +77,26 @@ type TxPool struct {
|
||||
events event.Subscription
|
||||
localTx *txSet
|
||||
mu sync.RWMutex
|
||||
pending map[common.Hash]*types.Transaction // processable transactions
|
||||
queue map[common.Address]map[common.Hash]*types.Transaction
|
||||
|
||||
wg sync.WaitGroup // for shutdown sync
|
||||
pending map[common.Address]*txList // All currently processable transactions
|
||||
queue map[common.Address]*txList // Queued but non-processable transactions
|
||||
all map[common.Hash]*types.Transaction // All transactions to allow lookups
|
||||
beats map[common.Address]time.Time // Last heartbeat from each known account
|
||||
|
||||
wg sync.WaitGroup // for shutdown sync
|
||||
quit chan struct{}
|
||||
|
||||
homestead bool
|
||||
}
|
||||
|
||||
func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
||||
func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentStateFn stateFn, gasLimitFn func() *big.Int) *TxPool {
|
||||
pool := &TxPool{
|
||||
config: config,
|
||||
pending: make(map[common.Hash]*types.Transaction),
|
||||
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
|
||||
signer: types.NewEIP155Signer(config.ChainId),
|
||||
pending: make(map[common.Address]*txList),
|
||||
queue: make(map[common.Address]*txList),
|
||||
all: make(map[common.Hash]*types.Transaction),
|
||||
beats: make(map[common.Address]time.Time),
|
||||
eventMux: eventMux,
|
||||
currentState: currentStateFn,
|
||||
gasLimit: gasLimitFn,
|
||||
@@ -88,10 +104,12 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
|
||||
pendingState: nil,
|
||||
localTx: newTxSet(),
|
||||
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
pool.wg.Add(1)
|
||||
pool.wg.Add(2)
|
||||
go pool.eventLoop()
|
||||
go pool.expirationLoop()
|
||||
|
||||
return pool
|
||||
}
|
||||
@@ -117,7 +135,7 @@ func (pool *TxPool) eventLoop() {
|
||||
pool.minGasPrice = ev.Price
|
||||
pool.mu.Unlock()
|
||||
case RemovedTransactionEvent:
|
||||
pool.AddTransactions(ev.Txs)
|
||||
pool.AddBatch(ev.Txs)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -125,12 +143,12 @@ func (pool *TxPool) eventLoop() {
|
||||
func (pool *TxPool) resetState() {
|
||||
currentState, err := pool.currentState()
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infoln("failed to get current state: %v", err)
|
||||
glog.V(logger.Error).Infof("Failed to get current state: %v", err)
|
||||
return
|
||||
}
|
||||
managedState := state.ManageState(currentState)
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infoln("failed to get managed state: %v", err)
|
||||
glog.V(logger.Error).Infof("Failed to get managed state: %v", err)
|
||||
return
|
||||
}
|
||||
pool.pendingState = managedState
|
||||
@@ -139,26 +157,21 @@ func (pool *TxPool) resetState() {
|
||||
// any transactions that have been included in the block or
|
||||
// have been invalidated because of another transaction (e.g.
|
||||
// higher gas price)
|
||||
pool.validatePool()
|
||||
pool.demoteUnexecutables()
|
||||
|
||||
// Loop over the pending transactions and base the nonce of the new
|
||||
// pending transaction set.
|
||||
for _, tx := range pool.pending {
|
||||
if addr, err := tx.From(); err == nil {
|
||||
// Set the nonce. Transaction nonce can never be lower
|
||||
// than the state nonce; validatePool took care of that.
|
||||
if pool.pendingState.GetNonce(addr) <= tx.Nonce() {
|
||||
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
||||
}
|
||||
}
|
||||
// Update all accounts to the latest known pending nonce
|
||||
for addr, list := range pool.pending {
|
||||
txs := list.Flatten() // Heavy but will be cached and is needed by the miner anyway
|
||||
pool.pendingState.SetNonce(addr, txs[len(txs)-1].Nonce()+1)
|
||||
}
|
||||
// Check the queue and move transactions over to the pending if possible
|
||||
// or remove those that have become invalid
|
||||
pool.checkQueue()
|
||||
pool.promoteExecutables()
|
||||
}
|
||||
|
||||
func (pool *TxPool) Stop() {
|
||||
pool.events.Unsubscribe()
|
||||
close(pool.quit)
|
||||
pool.wg.Wait()
|
||||
glog.V(logger.Info).Infoln("Transaction pool stopped")
|
||||
}
|
||||
@@ -170,47 +183,58 @@ func (pool *TxPool) State() *state.ManagedState {
|
||||
return pool.pendingState
|
||||
}
|
||||
|
||||
// Stats retrieves the current pool stats, namely the number of pending and the
|
||||
// number of queued (non-executable) transactions.
|
||||
func (pool *TxPool) Stats() (pending int, queued int) {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
pending = len(pool.pending)
|
||||
for _, txs := range pool.queue {
|
||||
queued += len(txs)
|
||||
for _, list := range pool.pending {
|
||||
pending += list.Len()
|
||||
}
|
||||
for _, list := range pool.queue {
|
||||
queued += list.Len()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Content retrieves the data content of the transaction pool, returning all the
|
||||
// pending as well as queued transactions, grouped by account and nonce.
|
||||
func (pool *TxPool) Content() (map[common.Address]map[uint64][]*types.Transaction, map[common.Address]map[uint64][]*types.Transaction) {
|
||||
// pending as well as queued transactions, grouped by account and sorted by nonce.
|
||||
func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common.Address]types.Transactions) {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
// Retrieve all the pending transactions and sort by account and by nonce
|
||||
pending := make(map[common.Address]map[uint64][]*types.Transaction)
|
||||
for _, tx := range pool.pending {
|
||||
account, _ := tx.From()
|
||||
|
||||
owned, ok := pending[account]
|
||||
if !ok {
|
||||
owned = make(map[uint64][]*types.Transaction)
|
||||
pending[account] = owned
|
||||
}
|
||||
owned[tx.Nonce()] = append(owned[tx.Nonce()], tx)
|
||||
pending := make(map[common.Address]types.Transactions)
|
||||
for addr, list := range pool.pending {
|
||||
pending[addr] = list.Flatten()
|
||||
}
|
||||
// Retrieve all the queued transactions and sort by account and by nonce
|
||||
queued := make(map[common.Address]map[uint64][]*types.Transaction)
|
||||
for account, txs := range pool.queue {
|
||||
owned := make(map[uint64][]*types.Transaction)
|
||||
for _, tx := range txs {
|
||||
owned[tx.Nonce()] = append(owned[tx.Nonce()], tx)
|
||||
}
|
||||
queued[account] = owned
|
||||
queued := make(map[common.Address]types.Transactions)
|
||||
for addr, list := range pool.queue {
|
||||
queued[addr] = list.Flatten()
|
||||
}
|
||||
return pending, queued
|
||||
}
|
||||
|
||||
// Pending retrieves all currently processable transactions, groupped by origin
|
||||
// account and sorted by nonce. The returned transaction set is a copy and can be
|
||||
// freely modified by calling code.
|
||||
func (pool *TxPool) Pending() map[common.Address]types.Transactions {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
// check queue first
|
||||
pool.promoteExecutables()
|
||||
|
||||
// invalidate any txs
|
||||
pool.demoteUnexecutables()
|
||||
|
||||
pending := make(map[common.Address]types.Transactions)
|
||||
for addr, list := range pool.pending {
|
||||
pending[addr] = list.Flatten()
|
||||
}
|
||||
return pending
|
||||
}
|
||||
|
||||
// SetLocal marks a transaction as local, skipping gas price
|
||||
// check against local miner minimum in the future
|
||||
func (pool *TxPool) SetLocal(tx *types.Transaction) {
|
||||
@@ -233,6 +257,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
||||
return err
|
||||
}
|
||||
|
||||
tx.SetSigner(pool.signer)
|
||||
from, err := tx.From()
|
||||
if err != nil {
|
||||
return ErrInvalidSender
|
||||
@@ -240,7 +265,7 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
||||
|
||||
// Make sure the account exist. Non existent accounts
|
||||
// haven't got funds and well therefor never pass.
|
||||
if !currentState.HasAccount(from) {
|
||||
if !currentState.Exist(from) {
|
||||
return ErrNonExistentAccount
|
||||
}
|
||||
|
||||
@@ -276,312 +301,400 @@ func (pool *TxPool) validateTx(tx *types.Transaction) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// validate and queue transactions.
|
||||
func (self *TxPool) add(tx *types.Transaction) error {
|
||||
// add validates a transaction and inserts it into the non-executable queue for
|
||||
// later pending promotion and execution.
|
||||
func (pool *TxPool) add(tx *types.Transaction) error {
|
||||
// If the transaction is alreayd known, discard it
|
||||
hash := tx.Hash()
|
||||
|
||||
if self.pending[hash] != nil {
|
||||
return fmt.Errorf("Known transaction (%x)", hash[:4])
|
||||
if pool.all[hash] != nil {
|
||||
return fmt.Errorf("Known transaction: %x", hash[:4])
|
||||
}
|
||||
err := self.validateTx(tx)
|
||||
if err != nil {
|
||||
// Otherwise ensure basic validation passes and queue it up
|
||||
if err := pool.validateTx(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
self.queueTx(hash, tx)
|
||||
pool.enqueueTx(hash, tx)
|
||||
|
||||
// Print a log message if low enough level is set
|
||||
if glog.V(logger.Debug) {
|
||||
var toname string
|
||||
rcpt := "[NEW_CONTRACT]"
|
||||
if to := tx.To(); to != nil {
|
||||
toname = common.Bytes2Hex(to[:4])
|
||||
} else {
|
||||
toname = "[NEW_CONTRACT]"
|
||||
rcpt = common.Bytes2Hex(to[:4])
|
||||
}
|
||||
// we can ignore the error here because From is
|
||||
// verified in ValidateTransaction.
|
||||
f, _ := tx.From()
|
||||
from := common.Bytes2Hex(f[:4])
|
||||
glog.Infof("(t) %x => %s (%v) %x\n", from, toname, tx.Value, hash)
|
||||
from, _ := tx.From() // from already verified during tx validation
|
||||
glog.Infof("(t) 0x%x => %s (%v) %x\n", from[:4], rcpt, tx.Value, hash)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// queueTx will queue an unknown transaction
|
||||
func (self *TxPool) queueTx(hash common.Hash, tx *types.Transaction) {
|
||||
// enqueueTx inserts a new transaction into the non-executable transaction queue.
|
||||
//
|
||||
// Note, this method assumes the pool lock is held!
|
||||
func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
|
||||
// Try to insert the transaction into the future queue
|
||||
from, _ := tx.From() // already validated
|
||||
if self.queue[from] == nil {
|
||||
self.queue[from] = make(map[common.Hash]*types.Transaction)
|
||||
if pool.queue[from] == nil {
|
||||
pool.queue[from] = newTxList(false)
|
||||
}
|
||||
self.queue[from][hash] = tx
|
||||
inserted, old := pool.queue[from].Add(tx)
|
||||
if !inserted {
|
||||
return // An older transaction was better, discard this
|
||||
}
|
||||
// Discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
}
|
||||
pool.all[hash] = tx
|
||||
}
|
||||
|
||||
// addTx will add a transaction to the pending (processable queue) list of transactions
|
||||
func (pool *TxPool) addTx(hash common.Hash, addr common.Address, tx *types.Transaction) {
|
||||
// init delayed since tx pool could have been started before any state sync
|
||||
// promoteTx adds a transaction to the pending (processable) list of transactions.
|
||||
//
|
||||
// Note, this method assumes the pool lock is held!
|
||||
func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) {
|
||||
// Init delayed since tx pool could have been started before any state sync
|
||||
if pool.pendingState == nil {
|
||||
pool.resetState()
|
||||
}
|
||||
|
||||
if _, ok := pool.pending[hash]; !ok {
|
||||
pool.pending[hash] = tx
|
||||
|
||||
// Increment the nonce on the pending state. This can only happen if
|
||||
// the nonce is +1 to the previous one.
|
||||
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
||||
// Notify the subscribers. This event is posted in a goroutine
|
||||
// because it's possible that somewhere during the post "Remove transaction"
|
||||
// gets called which will then wait for the global tx pool lock and deadlock.
|
||||
go pool.eventMux.Post(TxPreEvent{tx})
|
||||
// Try to insert the transaction into the pending queue
|
||||
if pool.pending[addr] == nil {
|
||||
pool.pending[addr] = newTxList(true)
|
||||
}
|
||||
list := pool.pending[addr]
|
||||
|
||||
inserted, old := list.Add(tx)
|
||||
if !inserted {
|
||||
// An older transaction was better, discard this
|
||||
delete(pool.all, hash)
|
||||
return
|
||||
}
|
||||
// Otherwise discard any previous transaction and mark this
|
||||
if old != nil {
|
||||
delete(pool.all, old.Hash())
|
||||
}
|
||||
pool.all[hash] = tx // Failsafe to work around direct pending inserts (tests)
|
||||
|
||||
// Set the potentially new pending nonce and notify any subsystems of the new tx
|
||||
pool.beats[addr] = time.Now()
|
||||
pool.pendingState.SetNonce(addr, tx.Nonce()+1)
|
||||
go pool.eventMux.Post(TxPreEvent{tx})
|
||||
}
|
||||
|
||||
// Add queues a single transaction in the pool if it is valid.
|
||||
func (self *TxPool) Add(tx *types.Transaction) error {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
if err := self.add(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
self.checkQueue()
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddTransactions attempts to queue all valid transactions in txs.
|
||||
func (self *TxPool) AddTransactions(txs []*types.Transaction) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
if err := self.add(tx); err != nil {
|
||||
glog.V(logger.Debug).Infoln("tx error:", err)
|
||||
} else {
|
||||
h := tx.Hash()
|
||||
glog.V(logger.Debug).Infof("tx %x\n", h[:4])
|
||||
}
|
||||
}
|
||||
|
||||
// check and validate the queue
|
||||
self.checkQueue()
|
||||
}
|
||||
|
||||
// GetTransaction returns a transaction if it is contained in the pool
|
||||
// and nil otherwise.
|
||||
func (tp *TxPool) GetTransaction(hash common.Hash) *types.Transaction {
|
||||
tp.mu.RLock()
|
||||
defer tp.mu.RUnlock()
|
||||
|
||||
// check the txs first
|
||||
if tx, ok := tp.pending[hash]; ok {
|
||||
return tx
|
||||
}
|
||||
// check queue
|
||||
for _, txs := range tp.queue {
|
||||
if tx, ok := txs[hash]; ok {
|
||||
return tx
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTransactions returns all currently processable transactions.
|
||||
// The returned slice may be modified by the caller.
|
||||
func (self *TxPool) GetTransactions() (txs types.Transactions) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
|
||||
// check queue first
|
||||
self.checkQueue()
|
||||
// invalidate any txs
|
||||
self.validatePool()
|
||||
|
||||
txs = make(types.Transactions, len(self.pending))
|
||||
i := 0
|
||||
for _, tx := range self.pending {
|
||||
txs[i] = tx
|
||||
i++
|
||||
}
|
||||
return txs
|
||||
}
|
||||
|
||||
// GetQueuedTransactions returns all non-processable transactions.
|
||||
func (self *TxPool) GetQueuedTransactions() types.Transactions {
|
||||
self.mu.RLock()
|
||||
defer self.mu.RUnlock()
|
||||
|
||||
var ret types.Transactions
|
||||
for _, txs := range self.queue {
|
||||
for _, tx := range txs {
|
||||
ret = append(ret, tx)
|
||||
}
|
||||
}
|
||||
sort.Sort(types.TxByNonce(ret))
|
||||
return ret
|
||||
}
|
||||
|
||||
// RemoveTransactions removes all given transactions from the pool.
|
||||
func (self *TxPool) RemoveTransactions(txs types.Transactions) {
|
||||
self.mu.Lock()
|
||||
defer self.mu.Unlock()
|
||||
for _, tx := range txs {
|
||||
self.removeTx(tx.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// RemoveTx removes the transaction with the given hash from the pool.
|
||||
func (pool *TxPool) RemoveTx(hash common.Hash) {
|
||||
func (pool *TxPool) Add(tx *types.Transaction) error {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
if err := pool.add(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddBatch attempts to queue a batch of transactions.
|
||||
func (pool *TxPool) AddBatch(txs []*types.Transaction) {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
if err := pool.add(tx); err != nil {
|
||||
glog.V(logger.Debug).Infoln("tx error:", err)
|
||||
}
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
}
|
||||
|
||||
// Get returns a transaction if it is contained in the pool
|
||||
// and nil otherwise.
|
||||
func (pool *TxPool) Get(hash common.Hash) *types.Transaction {
|
||||
pool.mu.RLock()
|
||||
defer pool.mu.RUnlock()
|
||||
|
||||
return pool.all[hash]
|
||||
}
|
||||
|
||||
// Remove removes the transaction with the given hash from the pool.
|
||||
func (pool *TxPool) Remove(hash common.Hash) {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
pool.removeTx(hash)
|
||||
}
|
||||
|
||||
// RemoveBatch removes all given transactions from the pool.
|
||||
func (pool *TxPool) RemoveBatch(txs types.Transactions) {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
for _, tx := range txs {
|
||||
pool.removeTx(tx.Hash())
|
||||
}
|
||||
}
|
||||
|
||||
// removeTx removes a single transaction from the queue, moving all subsequent
|
||||
// transactions back to the future queue.
|
||||
func (pool *TxPool) removeTx(hash common.Hash) {
|
||||
// delete from pending pool
|
||||
delete(pool.pending, hash)
|
||||
// delete from queue
|
||||
for address, txs := range pool.queue {
|
||||
if _, ok := txs[hash]; ok {
|
||||
if len(txs) == 1 {
|
||||
// if only one tx, remove entire address entry.
|
||||
delete(pool.queue, address)
|
||||
// Fetch the transaction we wish to delete
|
||||
tx, ok := pool.all[hash]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
addr, _ := tx.From() // already validated during insertion
|
||||
|
||||
// Remove it from the list of known transactions
|
||||
delete(pool.all, hash)
|
||||
|
||||
// Remove the transaction from the pending lists and reset the account nonce
|
||||
if pending := pool.pending[addr]; pending != nil {
|
||||
if removed, invalids := pending.Remove(tx); removed {
|
||||
// If no more transactions are left, remove the list
|
||||
if pending.Empty() {
|
||||
delete(pool.pending, addr)
|
||||
delete(pool.beats, addr)
|
||||
} else {
|
||||
delete(txs, hash)
|
||||
// Otherwise postpone any invalidated transactions
|
||||
for _, tx := range invalids {
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
}
|
||||
}
|
||||
break
|
||||
// Update the account nonce if needed
|
||||
if nonce := tx.Nonce(); pool.pendingState.GetNonce(addr) > nonce {
|
||||
pool.pendingState.SetNonce(addr, tx.Nonce())
|
||||
}
|
||||
}
|
||||
}
|
||||
// Transaction is in the future queue
|
||||
if future := pool.queue[addr]; future != nil {
|
||||
future.Remove(tx)
|
||||
if future.Empty() {
|
||||
delete(pool.queue, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// checkQueue moves transactions that have become processable to main pool.
|
||||
func (pool *TxPool) checkQueue() {
|
||||
// init delayed since tx pool could have been started before any state sync
|
||||
// promoteExecutables moves transactions that have become processable from the
|
||||
// future queue to the set of pending transactions. During this process, all
|
||||
// invalidated transactions (low nonce, low balance) are deleted.
|
||||
func (pool *TxPool) promoteExecutables() {
|
||||
// Init delayed since tx pool could have been started before any state sync
|
||||
if pool.pendingState == nil {
|
||||
pool.resetState()
|
||||
}
|
||||
|
||||
var promote txQueue
|
||||
for address, txs := range pool.queue {
|
||||
currentState, err := pool.currentState()
|
||||
if err != nil {
|
||||
glog.Errorf("could not get current state: %v", err)
|
||||
return
|
||||
// Retrieve the current state to allow nonce and balance checking
|
||||
state, err := pool.currentState()
|
||||
if err != nil {
|
||||
glog.Errorf("Could not get current state: %v", err)
|
||||
return
|
||||
}
|
||||
// Iterate over all accounts and promote any executable transactions
|
||||
queued := uint64(0)
|
||||
for addr, list := range pool.queue {
|
||||
// Drop all transactions that are deemed too old (low nonce)
|
||||
for _, tx := range list.Forward(state.GetNonce(addr)) {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Removed old queued transaction: %v", tx)
|
||||
}
|
||||
delete(pool.all, tx.Hash())
|
||||
}
|
||||
balance := currentState.GetBalance(address)
|
||||
// Drop all transactions that are too costly (low balance)
|
||||
drops, _ := list.Filter(state.GetBalance(addr))
|
||||
for _, tx := range drops {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Removed unpayable queued transaction: %v", tx)
|
||||
}
|
||||
delete(pool.all, tx.Hash())
|
||||
}
|
||||
// Gather all executable transactions and promote them
|
||||
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Promoting queued transaction: %v", tx)
|
||||
}
|
||||
pool.promoteTx(addr, tx.Hash(), tx)
|
||||
}
|
||||
// Drop all transactions over the allowed limit
|
||||
for _, tx := range list.Cap(int(maxQueuedPerAccount)) {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Removed cap-exceeding queued transaction: %v", tx)
|
||||
}
|
||||
delete(pool.all, tx.Hash())
|
||||
}
|
||||
queued += uint64(list.Len())
|
||||
|
||||
var (
|
||||
guessedNonce = pool.pendingState.GetNonce(address) // nonce currently kept by the tx pool (pending state)
|
||||
trueNonce = currentState.GetNonce(address) // nonce known by the last state
|
||||
)
|
||||
promote = promote[:0]
|
||||
for hash, tx := range txs {
|
||||
// Drop processed or out of fund transactions
|
||||
if tx.Nonce() < trueNonce || balance.Cmp(tx.Cost()) < 0 {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("removed tx (%v) from pool queue: low tx nonce or out of funds\n", tx)
|
||||
// Delete the entire queue entry if it became empty.
|
||||
if list.Empty() {
|
||||
delete(pool.queue, addr)
|
||||
}
|
||||
}
|
||||
// If the pending limit is overflown, start equalizing allowances
|
||||
pending := uint64(0)
|
||||
for _, list := range pool.pending {
|
||||
pending += uint64(list.Len())
|
||||
}
|
||||
if pending > maxPendingTotal {
|
||||
// Assemble a spam order to penalize large transactors first
|
||||
spammers := prque.New()
|
||||
for addr, list := range pool.pending {
|
||||
// Only evict transactions from high rollers
|
||||
if uint64(list.Len()) > minPendingPerAccount {
|
||||
// Skip local accounts as pools should maintain backlogs for themselves
|
||||
for _, tx := range list.txs.items {
|
||||
if !pool.localTx.contains(tx.Hash()) {
|
||||
spammers.Push(addr, float32(list.Len()))
|
||||
}
|
||||
break // Checking on transaction for locality is enough
|
||||
}
|
||||
delete(txs, hash)
|
||||
}
|
||||
}
|
||||
// Gradually drop transactions from offenders
|
||||
offenders := []common.Address{}
|
||||
for pending > maxPendingTotal && !spammers.Empty() {
|
||||
// Retrieve the next offender if not local address
|
||||
offender, _ := spammers.Pop()
|
||||
offenders = append(offenders, offender.(common.Address))
|
||||
|
||||
// Equalize balances until all the same or below threshold
|
||||
if len(offenders) > 1 {
|
||||
// Calculate the equalization threshold for all current offenders
|
||||
threshold := pool.pending[offender.(common.Address)].Len()
|
||||
|
||||
// Iteratively reduce all offenders until below limit or threshold reached
|
||||
for pending > maxPendingTotal && pool.pending[offenders[len(offenders)-2]].Len() > threshold {
|
||||
for i := 0; i < len(offenders)-1; i++ {
|
||||
list := pool.pending[offenders[i]]
|
||||
list.Cap(list.Len() - 1)
|
||||
pending--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If still above threshold, reduce to limit or min allowance
|
||||
if pending > maxPendingTotal && len(offenders) > 0 {
|
||||
for pending > maxPendingTotal && uint64(pool.pending[offenders[len(offenders)-1]].Len()) > minPendingPerAccount {
|
||||
for _, addr := range offenders {
|
||||
list := pool.pending[addr]
|
||||
list.Cap(list.Len() - 1)
|
||||
pending--
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If we've queued more transactions than the hard limit, drop oldest ones
|
||||
if queued > maxQueuedInTotal {
|
||||
// Sort all accounts with queued transactions by heartbeat
|
||||
addresses := make(addresssByHeartbeat, 0, len(pool.queue))
|
||||
for addr, _ := range pool.queue {
|
||||
addresses = append(addresses, addressByHeartbeat{addr, pool.beats[addr]})
|
||||
}
|
||||
sort.Sort(addresses)
|
||||
|
||||
// Drop transactions until the total is below the limit
|
||||
for drop := queued - maxQueuedInTotal; drop > 0; {
|
||||
addr := addresses[len(addresses)-1]
|
||||
list := pool.queue[addr.address]
|
||||
|
||||
addresses = addresses[:len(addresses)-1]
|
||||
|
||||
// Drop all transactions if they are less than the overflow
|
||||
if size := uint64(list.Len()); size <= drop {
|
||||
for _, tx := range list.Flatten() {
|
||||
pool.removeTx(tx.Hash())
|
||||
}
|
||||
drop -= size
|
||||
continue
|
||||
}
|
||||
// Collect the remaining transactions for the next pass.
|
||||
promote = append(promote, txQueueEntry{hash, address, tx})
|
||||
}
|
||||
// Find the next consecutive nonce range starting at the current account nonce,
|
||||
// pushing the guessed nonce forward if we add consecutive transactions.
|
||||
sort.Sort(promote)
|
||||
for i, entry := range promote {
|
||||
// If we reached a gap in the nonces, enforce transaction limit and stop
|
||||
if entry.Nonce() > guessedNonce {
|
||||
if len(promote)-i > maxQueued {
|
||||
if glog.V(logger.Debug) {
|
||||
glog.Infof("Queued tx limit exceeded for %s. Tx %s removed\n", common.PP(address[:]), common.PP(entry.hash[:]))
|
||||
}
|
||||
for _, drop := range promote[i+maxQueued:] {
|
||||
delete(txs, drop.hash)
|
||||
}
|
||||
}
|
||||
break
|
||||
// Otherwise drop only last few transactions
|
||||
txs := list.Flatten()
|
||||
for i := len(txs) - 1; i >= 0 && drop > 0; i-- {
|
||||
pool.removeTx(txs[i].Hash())
|
||||
drop--
|
||||
}
|
||||
// Otherwise promote the transaction and move the guess nonce if needed
|
||||
pool.addTx(entry.hash, address, entry.Transaction)
|
||||
delete(txs, entry.hash)
|
||||
|
||||
if entry.Nonce() == guessedNonce {
|
||||
guessedNonce++
|
||||
}
|
||||
}
|
||||
// Delete the entire queue entry if it became empty.
|
||||
if len(txs) == 0 {
|
||||
delete(pool.queue, address)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// validatePool removes invalid and processed transactions from the main pool.
|
||||
// If a transaction is removed for being invalid (e.g. out of funds), all sub-
|
||||
// sequent (Still valid) transactions are moved back into the future queue. This
|
||||
// is important to prevent a drained account from DOSing the network with non
|
||||
// executable transactions.
|
||||
func (pool *TxPool) validatePool() {
|
||||
// demoteUnexecutables removes invalid and processed transactions from the pools
|
||||
// executable/pending queue and any subsequent transactions that become unexecutable
|
||||
// are moved back into the future queue.
|
||||
func (pool *TxPool) demoteUnexecutables() {
|
||||
// Retrieve the current state to allow nonce and balance checking
|
||||
state, err := pool.currentState()
|
||||
if err != nil {
|
||||
glog.V(logger.Info).Infoln("failed to get current state: %v", err)
|
||||
return
|
||||
}
|
||||
balanceCache := make(map[common.Address]*big.Int)
|
||||
// Iterate over all accounts and demote any non-executable transactions
|
||||
for addr, list := range pool.pending {
|
||||
nonce := state.GetNonce(addr)
|
||||
|
||||
// Clean up the pending pool, accumulating invalid nonces
|
||||
gaps := make(map[common.Address]uint64)
|
||||
|
||||
for hash, tx := range pool.pending {
|
||||
sender, _ := tx.From() // err already checked
|
||||
|
||||
// Perform light nonce and balance validation
|
||||
balance := balanceCache[sender]
|
||||
if balance == nil {
|
||||
balance = state.GetBalance(sender)
|
||||
balanceCache[sender] = balance
|
||||
}
|
||||
if past := state.GetNonce(sender) > tx.Nonce(); past || balance.Cmp(tx.Cost()) < 0 {
|
||||
// Remove an already past it invalidated transaction
|
||||
// Drop all transactions that are deemed too old (low nonce)
|
||||
for _, tx := range list.Forward(nonce) {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("removed tx (%v) from pool: low tx nonce or out of funds\n", tx)
|
||||
}
|
||||
delete(pool.pending, hash)
|
||||
|
||||
// Track the smallest invalid nonce to postpone subsequent transactions
|
||||
if !past {
|
||||
if prev, ok := gaps[sender]; !ok || tx.Nonce() < prev {
|
||||
gaps[sender] = tx.Nonce()
|
||||
}
|
||||
glog.Infof("Removed old pending transaction: %v", tx)
|
||||
}
|
||||
delete(pool.all, tx.Hash())
|
||||
}
|
||||
}
|
||||
// Move all transactions after a gap back to the future queue
|
||||
if len(gaps) > 0 {
|
||||
for hash, tx := range pool.pending {
|
||||
sender, _ := tx.From()
|
||||
if gap, ok := gaps[sender]; ok && tx.Nonce() >= gap {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("postponed tx (%v) due to introduced gap\n", tx)
|
||||
}
|
||||
pool.queueTx(hash, tx)
|
||||
delete(pool.pending, hash)
|
||||
// Drop all transactions that are too costly (low balance), and queue any invalids back for later
|
||||
drops, invalids := list.Filter(state.GetBalance(addr))
|
||||
for _, tx := range drops {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Removed unpayable pending transaction: %v", tx)
|
||||
}
|
||||
delete(pool.all, tx.Hash())
|
||||
}
|
||||
for _, tx := range invalids {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("Demoting pending transaction: %v", tx)
|
||||
}
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
}
|
||||
// Delete the entire queue entry if it became empty.
|
||||
if list.Empty() {
|
||||
delete(pool.pending, addr)
|
||||
delete(pool.beats, addr)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type txQueue []txQueueEntry
|
||||
// expirationLoop is a loop that periodically iterates over all accounts with
|
||||
// queued transactions and drop all that have been inactive for a prolonged amount
|
||||
// of time.
|
||||
func (pool *TxPool) expirationLoop() {
|
||||
defer pool.wg.Done()
|
||||
|
||||
type txQueueEntry struct {
|
||||
hash common.Hash
|
||||
addr common.Address
|
||||
*types.Transaction
|
||||
evict := time.NewTicker(evictionInterval)
|
||||
defer evict.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-evict.C:
|
||||
pool.mu.Lock()
|
||||
for addr := range pool.queue {
|
||||
if time.Since(pool.beats[addr]) > maxQueuedLifetime {
|
||||
for _, tx := range pool.queue[addr].Flatten() {
|
||||
pool.removeTx(tx.Hash())
|
||||
}
|
||||
}
|
||||
}
|
||||
pool.mu.Unlock()
|
||||
|
||||
case <-pool.quit:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (q txQueue) Len() int { return len(q) }
|
||||
func (q txQueue) Swap(i, j int) { q[i], q[j] = q[j], q[i] }
|
||||
func (q txQueue) Less(i, j int) bool { return q[i].Nonce() < q[j].Nonce() }
|
||||
// addressByHeartbeat is an account address tagged with its last activity timestamp.
|
||||
type addressByHeartbeat struct {
|
||||
address common.Address
|
||||
heartbeat time.Time
|
||||
}
|
||||
|
||||
type addresssByHeartbeat []addressByHeartbeat
|
||||
|
||||
func (a addresssByHeartbeat) Len() int { return len(a) }
|
||||
func (a addresssByHeartbeat) Less(i, j int) bool { return a[i].heartbeat.Before(a[j].heartbeat) }
|
||||
func (a addresssByHeartbeat) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||
|
||||
// txSet represents a set of transaction hashes in which entries
|
||||
// are automatically dropped after txSetDuration time
|
||||
|
||||
@@ -19,7 +19,9 @@ package core
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
@@ -38,10 +40,10 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
var m event.TypeMux
|
||||
key, _ := crypto.GenerateKey()
|
||||
newPool := NewTxPool(testChainConfig(), &m, func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
newPool.resetState()
|
||||
|
||||
return newPool, key
|
||||
}
|
||||
|
||||
@@ -91,9 +93,9 @@ func TestTransactionQueue(t *testing.T) {
|
||||
from, _ := tx.From()
|
||||
currentState, _ := pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1000))
|
||||
pool.queueTx(tx.Hash(), tx)
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
|
||||
pool.checkQueue()
|
||||
pool.promoteExecutables()
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected valid txs to be 1 is", len(pool.pending))
|
||||
}
|
||||
@@ -101,14 +103,14 @@ func TestTransactionQueue(t *testing.T) {
|
||||
tx = transaction(1, big.NewInt(100), key)
|
||||
from, _ = tx.From()
|
||||
currentState.SetNonce(from, 2)
|
||||
pool.queueTx(tx.Hash(), tx)
|
||||
pool.checkQueue()
|
||||
if _, ok := pool.pending[tx.Hash()]; ok {
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
pool.promoteExecutables()
|
||||
if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok {
|
||||
t.Error("expected transaction to be in tx pool")
|
||||
}
|
||||
|
||||
if len(pool.queue[from]) > 0 {
|
||||
t.Error("expected transaction queue to be empty. is", len(pool.queue[from]))
|
||||
if len(pool.queue) > 0 {
|
||||
t.Error("expected transaction queue to be empty. is", len(pool.queue))
|
||||
}
|
||||
|
||||
pool, key = setupTxPool()
|
||||
@@ -118,17 +120,17 @@ func TestTransactionQueue(t *testing.T) {
|
||||
from, _ = tx1.From()
|
||||
currentState, _ = pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1000))
|
||||
pool.queueTx(tx1.Hash(), tx1)
|
||||
pool.queueTx(tx2.Hash(), tx2)
|
||||
pool.queueTx(tx3.Hash(), tx3)
|
||||
pool.enqueueTx(tx1.Hash(), tx1)
|
||||
pool.enqueueTx(tx2.Hash(), tx2)
|
||||
pool.enqueueTx(tx3.Hash(), tx3)
|
||||
|
||||
pool.checkQueue()
|
||||
pool.promoteExecutables()
|
||||
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected tx pool to be 1, got", len(pool.pending))
|
||||
}
|
||||
if len(pool.queue[from]) != 2 {
|
||||
t.Error("expected len(queue) == 2, got", len(pool.queue[from]))
|
||||
if pool.queue[from].Len() != 2 {
|
||||
t.Error("expected len(queue) == 2, got", pool.queue[from].Len())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,24 +140,21 @@ func TestRemoveTx(t *testing.T) {
|
||||
from, _ := tx.From()
|
||||
currentState, _ := pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1))
|
||||
pool.queueTx(tx.Hash(), tx)
|
||||
pool.addTx(tx.Hash(), from, tx)
|
||||
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
pool.promoteTx(from, tx.Hash(), tx)
|
||||
if len(pool.queue) != 1 {
|
||||
t.Error("expected queue to be 1, got", len(pool.queue))
|
||||
}
|
||||
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected txs to be 1, got", len(pool.pending))
|
||||
t.Error("expected pending to be 1, got", len(pool.pending))
|
||||
}
|
||||
|
||||
pool.RemoveTx(tx.Hash())
|
||||
|
||||
pool.Remove(tx.Hash())
|
||||
if len(pool.queue) > 0 {
|
||||
t.Error("expected queue to be 0, got", len(pool.queue))
|
||||
}
|
||||
|
||||
if len(pool.pending) > 0 {
|
||||
t.Error("expected txs to be 0, got", len(pool.pending))
|
||||
t.Error("expected pending to be 0, got", len(pool.pending))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,7 +187,7 @@ func TestTransactionChainFork(t *testing.T) {
|
||||
if err := pool.add(tx); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
pool.RemoveTransactions([]*types.Transaction{tx})
|
||||
pool.RemoveBatch([]*types.Transaction{tx})
|
||||
|
||||
// reset the pool's internal state
|
||||
resetState()
|
||||
@@ -210,18 +209,38 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
}
|
||||
resetState()
|
||||
|
||||
tx := transaction(0, big.NewInt(100000), key)
|
||||
tx2 := transaction(0, big.NewInt(1000000), key)
|
||||
if err := pool.add(tx); err != nil {
|
||||
tx1, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(100000), big.NewInt(1), nil).SignECDSA(key)
|
||||
tx2, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(2), nil).SignECDSA(key)
|
||||
tx3, _ := types.NewTransaction(0, common.Address{}, big.NewInt(100), big.NewInt(1000000), big.NewInt(1), nil).SignECDSA(key)
|
||||
|
||||
// Add the first two transaction, ensure higher priced stays only
|
||||
if err := pool.add(tx1); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
if err := pool.add(tx2); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
|
||||
pool.checkQueue()
|
||||
if len(pool.pending) != 2 {
|
||||
t.Error("expected 2 pending txs. Got", len(pool.pending))
|
||||
pool.promoteExecutables()
|
||||
if pool.pending[addr].Len() != 1 {
|
||||
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
|
||||
}
|
||||
if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
|
||||
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
|
||||
}
|
||||
// Add the thid transaction and ensure it's not saved (smaller price)
|
||||
if err := pool.add(tx3); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
if pool.pending[addr].Len() != 1 {
|
||||
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
|
||||
}
|
||||
if tx := pool.pending[addr].txs.items[0]; tx.Hash() != tx2.Hash() {
|
||||
t.Errorf("transaction mismatch: have %x, want %x", tx.Hash(), tx2.Hash())
|
||||
}
|
||||
// Ensure the total transaction count is correct
|
||||
if len(pool.all) != 1 {
|
||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -237,8 +256,11 @@ func TestMissingNonce(t *testing.T) {
|
||||
if len(pool.pending) != 0 {
|
||||
t.Error("expected 0 pending transactions, got", len(pool.pending))
|
||||
}
|
||||
if len(pool.queue[addr]) != 1 {
|
||||
t.Error("expected 1 queued transaction, got", len(pool.queue[addr]))
|
||||
if pool.queue[addr].Len() != 1 {
|
||||
t.Error("expected 1 queued transaction, got", pool.queue[addr].Len())
|
||||
}
|
||||
if len(pool.all) != 1 {
|
||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,8 +292,11 @@ func TestRemovedTxEvent(t *testing.T) {
|
||||
currentState.AddBalance(from, big.NewInt(1000000000000))
|
||||
pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}})
|
||||
pool.eventMux.Post(ChainHeadEvent{nil})
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected 1 pending tx, got", len(pool.pending))
|
||||
if pool.pending[from].Len() != 1 {
|
||||
t.Error("expected 1 pending tx, got", pool.pending[from].Len())
|
||||
}
|
||||
if len(pool.all) != 1 {
|
||||
t.Error("expected 1 total transactions, got", len(pool.all))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,41 +317,50 @@ func TestTransactionDropping(t *testing.T) {
|
||||
tx10 = transaction(10, big.NewInt(100), key)
|
||||
tx11 = transaction(11, big.NewInt(200), key)
|
||||
)
|
||||
pool.addTx(tx0.Hash(), account, tx0)
|
||||
pool.addTx(tx1.Hash(), account, tx1)
|
||||
pool.queueTx(tx10.Hash(), tx10)
|
||||
pool.queueTx(tx11.Hash(), tx11)
|
||||
pool.promoteTx(account, tx0.Hash(), tx0)
|
||||
pool.promoteTx(account, tx1.Hash(), tx1)
|
||||
pool.enqueueTx(tx10.Hash(), tx10)
|
||||
pool.enqueueTx(tx11.Hash(), tx11)
|
||||
|
||||
// Check that pre and post validations leave the pool as is
|
||||
if len(pool.pending) != 2 {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), 2)
|
||||
if pool.pending[account].Len() != 2 {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
|
||||
}
|
||||
if len(pool.queue[account]) != 2 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 2)
|
||||
if pool.queue[account].Len() != 2 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
|
||||
}
|
||||
if len(pool.all) != 4 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
|
||||
}
|
||||
pool.resetState()
|
||||
if len(pool.pending) != 2 {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), 2)
|
||||
if pool.pending[account].Len() != 2 {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), 2)
|
||||
}
|
||||
if len(pool.queue[account]) != 2 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 2)
|
||||
if pool.queue[account].Len() != 2 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 2)
|
||||
}
|
||||
if len(pool.all) != 4 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 4)
|
||||
}
|
||||
// Reduce the balance of the account, and check that invalidated transactions are dropped
|
||||
state.AddBalance(account, big.NewInt(-750))
|
||||
pool.resetState()
|
||||
|
||||
if _, ok := pool.pending[tx0.Hash()]; !ok {
|
||||
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
|
||||
t.Errorf("funded pending transaction missing: %v", tx0)
|
||||
}
|
||||
if _, ok := pool.pending[tx1.Hash()]; ok {
|
||||
if _, ok := pool.pending[account].txs.items[tx1.Nonce()]; ok {
|
||||
t.Errorf("out-of-fund pending transaction present: %v", tx1)
|
||||
}
|
||||
if _, ok := pool.queue[account][tx10.Hash()]; !ok {
|
||||
if _, ok := pool.queue[account].txs.items[tx10.Nonce()]; !ok {
|
||||
t.Errorf("funded queued transaction missing: %v", tx10)
|
||||
}
|
||||
if _, ok := pool.queue[account][tx11.Hash()]; ok {
|
||||
if _, ok := pool.queue[account].txs.items[tx11.Nonce()]; ok {
|
||||
t.Errorf("out-of-fund queued transaction present: %v", tx11)
|
||||
}
|
||||
if len(pool.all) != 2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), 2)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if a transaction is dropped from the current pending pool (e.g. out
|
||||
@@ -349,55 +383,64 @@ func TestTransactionPostponing(t *testing.T) {
|
||||
} else {
|
||||
tx = transaction(uint64(i), big.NewInt(500), key)
|
||||
}
|
||||
pool.addTx(tx.Hash(), account, tx)
|
||||
pool.promoteTx(account, tx.Hash(), tx)
|
||||
txns = append(txns, tx)
|
||||
}
|
||||
// Check that pre and post validations leave the pool as is
|
||||
if len(pool.pending) != len(txns) {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), len(txns))
|
||||
if pool.pending[account].Len() != len(txns) {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns))
|
||||
}
|
||||
if len(pool.queue[account]) != 0 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 0)
|
||||
if len(pool.queue) != 0 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0)
|
||||
}
|
||||
if len(pool.all) != len(txns) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns))
|
||||
}
|
||||
pool.resetState()
|
||||
if len(pool.pending) != len(txns) {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", len(pool.pending), len(txns))
|
||||
if pool.pending[account].Len() != len(txns) {
|
||||
t.Errorf("pending transaction mismatch: have %d, want %d", pool.pending[account].Len(), len(txns))
|
||||
}
|
||||
if len(pool.queue[account]) != 0 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", len(pool.queue), 0)
|
||||
if len(pool.queue) != 0 {
|
||||
t.Errorf("queued transaction mismatch: have %d, want %d", pool.queue[account].Len(), 0)
|
||||
}
|
||||
if len(pool.all) != len(txns) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns))
|
||||
}
|
||||
// Reduce the balance of the account, and check that transactions are reorganised
|
||||
state.AddBalance(account, big.NewInt(-750))
|
||||
pool.resetState()
|
||||
|
||||
if _, ok := pool.pending[txns[0].Hash()]; !ok {
|
||||
if _, ok := pool.pending[account].txs.items[txns[0].Nonce()]; !ok {
|
||||
t.Errorf("tx %d: valid and funded transaction missing from pending pool: %v", 0, txns[0])
|
||||
}
|
||||
if _, ok := pool.queue[account][txns[0].Hash()]; ok {
|
||||
if _, ok := pool.queue[account].txs.items[txns[0].Nonce()]; ok {
|
||||
t.Errorf("tx %d: valid and funded transaction present in future queue: %v", 0, txns[0])
|
||||
}
|
||||
for i, tx := range txns[1:] {
|
||||
if i%2 == 1 {
|
||||
if _, ok := pool.pending[tx.Hash()]; ok {
|
||||
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok {
|
||||
t.Errorf("tx %d: valid but future transaction present in pending pool: %v", i+1, tx)
|
||||
}
|
||||
if _, ok := pool.queue[account][tx.Hash()]; !ok {
|
||||
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; !ok {
|
||||
t.Errorf("tx %d: valid but future transaction missing from future queue: %v", i+1, tx)
|
||||
}
|
||||
} else {
|
||||
if _, ok := pool.pending[tx.Hash()]; ok {
|
||||
if _, ok := pool.pending[account].txs.items[tx.Nonce()]; ok {
|
||||
t.Errorf("tx %d: out-of-fund transaction present in pending pool: %v", i+1, tx)
|
||||
}
|
||||
if _, ok := pool.queue[account][tx.Hash()]; ok {
|
||||
if _, ok := pool.queue[account].txs.items[tx.Nonce()]; ok {
|
||||
t.Errorf("tx %d: out-of-fund transaction present in future queue: %v", i+1, tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pool.all) != len(txns)/2 {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), len(txns)/2)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if the transaction count belonging to a single account goes above
|
||||
// some threshold, the higher transactions are dropped to prevent DOS attacks.
|
||||
func TestTransactionQueueLimiting(t *testing.T) {
|
||||
func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||
// Create a test account and fund it
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
@@ -406,23 +449,104 @@ func TestTransactionQueueLimiting(t *testing.T) {
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||
for i := uint64(1); i <= maxQueued+5; i++ {
|
||||
for i := uint64(1); i <= maxQueuedPerAccount+5; i++ {
|
||||
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
if len(pool.pending) != 0 {
|
||||
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), 0)
|
||||
}
|
||||
if i <= maxQueued {
|
||||
if len(pool.queue[account]) != int(i) {
|
||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, len(pool.queue[account]), i)
|
||||
if i <= maxQueuedPerAccount {
|
||||
if pool.queue[account].Len() != int(i) {
|
||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), i)
|
||||
}
|
||||
} else {
|
||||
if len(pool.queue[account]) != maxQueued {
|
||||
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, len(pool.queue[account]), maxQueued)
|
||||
if pool.queue[account].Len() != int(maxQueuedPerAccount) {
|
||||
t.Errorf("tx %d: queue limit mismatch: have %d, want %d", i, pool.queue[account].Len(), maxQueuedPerAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
if len(pool.all) != int(maxQueuedPerAccount) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if the transaction count belonging to multiple accounts go above
|
||||
// some threshold, the higher transactions are dropped to prevent DOS attacks.
|
||||
func TestTransactionQueueGlobalLimiting(t *testing.T) {
|
||||
// Reduce the queue limits to shorten test time
|
||||
defer func(old uint64) { maxQueuedInTotal = old }(maxQueuedInTotal)
|
||||
maxQueuedInTotal = maxQueuedPerAccount * 3
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
state, _ := pool.currentState()
|
||||
|
||||
keys := make([]*ecdsa.PrivateKey, 5)
|
||||
for i := 0; i < len(keys); i++ {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||
}
|
||||
// Generate and queue a batch of transactions
|
||||
nonces := make(map[common.Address]uint64)
|
||||
|
||||
txs := make(types.Transactions, 0, 3*maxQueuedInTotal)
|
||||
for len(txs) < cap(txs) {
|
||||
key := keys[rand.Intn(len(keys))]
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
|
||||
txs = append(txs, transaction(nonces[addr]+1, big.NewInt(100000), key))
|
||||
nonces[addr]++
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
pool.AddBatch(txs)
|
||||
|
||||
queued := 0
|
||||
for addr, list := range pool.queue {
|
||||
if list.Len() > int(maxQueuedPerAccount) {
|
||||
t.Errorf("addr %x: queued accounts overflown allowance: %d > %d", addr, list.Len(), maxQueuedPerAccount)
|
||||
}
|
||||
queued += list.Len()
|
||||
}
|
||||
if queued > int(maxQueuedInTotal) {
|
||||
t.Fatalf("total transactions overflow allowance: %d > %d", queued, maxQueuedInTotal)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if an account remains idle for a prolonged amount of time, any
|
||||
// non-executable transactions queued up are dropped to prevent wasting resources
|
||||
// on shuffling them around.
|
||||
func TestTransactionQueueTimeLimiting(t *testing.T) {
|
||||
// Reduce the queue limits to shorten test time
|
||||
defer func(old time.Duration) { maxQueuedLifetime = old }(maxQueuedLifetime)
|
||||
defer func(old time.Duration) { evictionInterval = old }(evictionInterval)
|
||||
maxQueuedLifetime = time.Second
|
||||
evictionInterval = time.Second
|
||||
|
||||
// Create a test account and fund it
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
|
||||
state, _ := pool.currentState()
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// Queue up a batch of transactions
|
||||
for i := uint64(1); i <= maxQueuedPerAccount; i++ {
|
||||
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
}
|
||||
// Wait until at least two expiration cycles hit and make sure the transactions are gone
|
||||
time.Sleep(2 * evictionInterval)
|
||||
if len(pool.queue) > 0 {
|
||||
t.Fatalf("old transactions remained after eviction")
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that even if the transaction count belonging to a single account goes
|
||||
@@ -437,17 +561,20 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||
for i := uint64(0); i < maxQueued+5; i++ {
|
||||
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
|
||||
if err := pool.Add(transaction(i, big.NewInt(100000), key)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
if len(pool.pending) != int(i)+1 {
|
||||
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, len(pool.pending), i+1)
|
||||
if pool.pending[account].Len() != int(i)+1 {
|
||||
t.Errorf("tx %d: pending pool size mismatch: have %d, want %d", i, pool.pending[account].Len(), i+1)
|
||||
}
|
||||
if len(pool.queue[account]) != 0 {
|
||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, len(pool.queue[account]), 0)
|
||||
if len(pool.queue) != 0 {
|
||||
t.Errorf("tx %d: queue size mismatch: have %d, want %d", i, pool.queue[account].Len(), 0)
|
||||
}
|
||||
}
|
||||
if len(pool.all) != int(maxQueuedPerAccount+5) {
|
||||
t.Errorf("total transaction mismatch: have %d, want %d", len(pool.all), maxQueuedPerAccount+5)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that the transaction limits are enforced the same way irrelevant whether
|
||||
@@ -462,39 +589,132 @@ func testTransactionLimitingEquivalency(t *testing.T, origin uint64) {
|
||||
state1, _ := pool1.currentState()
|
||||
state1.AddBalance(account1, big.NewInt(1000000))
|
||||
|
||||
for i := uint64(0); i < maxQueued+5; i++ {
|
||||
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
|
||||
if err := pool1.Add(transaction(origin+i, big.NewInt(100000), key1)); err != nil {
|
||||
t.Fatalf("tx %d: failed to add transaction: %v", i, err)
|
||||
}
|
||||
}
|
||||
// Add a batch of transactions to a pool in one bit batch
|
||||
// Add a batch of transactions to a pool in one big batch
|
||||
pool2, key2 := setupTxPool()
|
||||
account2, _ := transaction(0, big.NewInt(0), key2).From()
|
||||
state2, _ := pool2.currentState()
|
||||
state2.AddBalance(account2, big.NewInt(1000000))
|
||||
|
||||
txns := []*types.Transaction{}
|
||||
for i := uint64(0); i < maxQueued+5; i++ {
|
||||
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
|
||||
txns = append(txns, transaction(origin+i, big.NewInt(100000), key2))
|
||||
}
|
||||
pool2.AddTransactions(txns)
|
||||
pool2.AddBatch(txns)
|
||||
|
||||
// Ensure the batch optimization honors the same pool mechanics
|
||||
if len(pool1.pending) != len(pool2.pending) {
|
||||
t.Errorf("pending transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.pending), len(pool2.pending))
|
||||
}
|
||||
if len(pool1.queue[account1]) != len(pool2.queue[account2]) {
|
||||
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue[account1]), len(pool2.queue[account2]))
|
||||
if len(pool1.queue) != len(pool2.queue) {
|
||||
t.Errorf("queued transaction count mismatch: one-by-one algo: %d, batch algo: %d", len(pool1.queue), len(pool2.queue))
|
||||
}
|
||||
if len(pool1.all) != len(pool2.all) {
|
||||
t.Errorf("total transaction count mismatch: one-by-one algo %d, batch algo %d", len(pool1.all), len(pool2.all))
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if the transaction count belonging to multiple accounts go above
|
||||
// some hard threshold, the higher transactions are dropped to prevent DOS
|
||||
// attacks.
|
||||
func TestTransactionPendingGlobalLimiting(t *testing.T) {
|
||||
// Reduce the queue limits to shorten test time
|
||||
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
|
||||
maxPendingTotal = minPendingPerAccount * 10
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
state, _ := pool.currentState()
|
||||
|
||||
keys := make([]*ecdsa.PrivateKey, 5)
|
||||
for i := 0; i < len(keys); i++ {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||
}
|
||||
// Generate and queue a batch of transactions
|
||||
nonces := make(map[common.Address]uint64)
|
||||
|
||||
txs := types.Transactions{}
|
||||
for _, key := range keys {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
for j := 0; j < int(maxPendingTotal)/len(keys)*2; j++ {
|
||||
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
|
||||
nonces[addr]++
|
||||
}
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
pool.AddBatch(txs)
|
||||
|
||||
pending := 0
|
||||
for _, list := range pool.pending {
|
||||
pending += list.Len()
|
||||
}
|
||||
if pending > int(maxPendingTotal) {
|
||||
t.Fatalf("total pending transactions overflow allowance: %d > %d", pending, maxPendingTotal)
|
||||
}
|
||||
}
|
||||
|
||||
// Tests that if the transaction count belonging to multiple accounts go above
|
||||
// some hard threshold, if they are under the minimum guaranteed slot count then
|
||||
// the transactions are still kept.
|
||||
func TestTransactionPendingMinimumAllowance(t *testing.T) {
|
||||
// Reduce the queue limits to shorten test time
|
||||
defer func(old uint64) { maxPendingTotal = old }(maxPendingTotal)
|
||||
maxPendingTotal = 0
|
||||
|
||||
// Create the pool to test the limit enforcement with
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
statedb, _ := state.New(common.Hash{}, db)
|
||||
|
||||
pool := NewTxPool(testChainConfig(), new(event.TypeMux), func() (*state.StateDB, error) { return statedb, nil }, func() *big.Int { return big.NewInt(1000000) })
|
||||
pool.resetState()
|
||||
|
||||
// Create a number of test accounts and fund them
|
||||
state, _ := pool.currentState()
|
||||
|
||||
keys := make([]*ecdsa.PrivateKey, 5)
|
||||
for i := 0; i < len(keys); i++ {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
state.AddBalance(crypto.PubkeyToAddress(keys[i].PublicKey), big.NewInt(1000000))
|
||||
}
|
||||
// Generate and queue a batch of transactions
|
||||
nonces := make(map[common.Address]uint64)
|
||||
|
||||
txs := types.Transactions{}
|
||||
for _, key := range keys {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
for j := 0; j < int(minPendingPerAccount)*2; j++ {
|
||||
txs = append(txs, transaction(nonces[addr], big.NewInt(100000), key))
|
||||
nonces[addr]++
|
||||
}
|
||||
}
|
||||
// Import the batch and verify that limits have been enforced
|
||||
pool.AddBatch(txs)
|
||||
|
||||
for addr, list := range pool.pending {
|
||||
if list.Len() != int(minPendingPerAccount) {
|
||||
t.Errorf("addr %x: total pending transactions mismatch: have %d, want %d", addr, list.Len(), minPendingPerAccount)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the speed of validating the contents of the pending queue of the
|
||||
// transaction pool.
|
||||
func BenchmarkValidatePool100(b *testing.B) { benchmarkValidatePool(b, 100) }
|
||||
func BenchmarkValidatePool1000(b *testing.B) { benchmarkValidatePool(b, 1000) }
|
||||
func BenchmarkValidatePool10000(b *testing.B) { benchmarkValidatePool(b, 10000) }
|
||||
func BenchmarkPendingDemotion100(b *testing.B) { benchmarkPendingDemotion(b, 100) }
|
||||
func BenchmarkPendingDemotion1000(b *testing.B) { benchmarkPendingDemotion(b, 1000) }
|
||||
func BenchmarkPendingDemotion10000(b *testing.B) { benchmarkPendingDemotion(b, 10000) }
|
||||
|
||||
func benchmarkValidatePool(b *testing.B, size int) {
|
||||
func benchmarkPendingDemotion(b *testing.B, size int) {
|
||||
// Add a batch of transactions to a pool one by one
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
@@ -503,22 +723,22 @@ func benchmarkValidatePool(b *testing.B, size int) {
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
tx := transaction(uint64(i), big.NewInt(100000), key)
|
||||
pool.addTx(tx.Hash(), account, tx)
|
||||
pool.promoteTx(account, tx.Hash(), tx)
|
||||
}
|
||||
// Benchmark the speed of pool validation
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pool.validatePool()
|
||||
pool.demoteUnexecutables()
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the speed of scheduling the contents of the future queue of the
|
||||
// transaction pool.
|
||||
func BenchmarkCheckQueue100(b *testing.B) { benchmarkCheckQueue(b, 100) }
|
||||
func BenchmarkCheckQueue1000(b *testing.B) { benchmarkCheckQueue(b, 1000) }
|
||||
func BenchmarkCheckQueue10000(b *testing.B) { benchmarkCheckQueue(b, 10000) }
|
||||
func BenchmarkFuturePromotion100(b *testing.B) { benchmarkFuturePromotion(b, 100) }
|
||||
func BenchmarkFuturePromotion1000(b *testing.B) { benchmarkFuturePromotion(b, 1000) }
|
||||
func BenchmarkFuturePromotion10000(b *testing.B) { benchmarkFuturePromotion(b, 10000) }
|
||||
|
||||
func benchmarkCheckQueue(b *testing.B, size int) {
|
||||
func benchmarkFuturePromotion(b *testing.B, size int) {
|
||||
// Add a batch of transactions to a pool one by one
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
@@ -527,11 +747,56 @@ func benchmarkCheckQueue(b *testing.B, size int) {
|
||||
|
||||
for i := 0; i < size; i++ {
|
||||
tx := transaction(uint64(1+i), big.NewInt(100000), key)
|
||||
pool.queueTx(tx.Hash(), tx)
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
}
|
||||
// Benchmark the speed of pool validation
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pool.checkQueue()
|
||||
pool.promoteExecutables()
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the speed of iterative transaction insertion.
|
||||
func BenchmarkPoolInsert(b *testing.B) {
|
||||
// Generate a batch of transactions to enqueue into the pool
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
state, _ := pool.currentState()
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
txs := make(types.Transactions, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
txs[i] = transaction(uint64(i), big.NewInt(100000), key)
|
||||
}
|
||||
// Benchmark importing the transactions into the queue
|
||||
b.ResetTimer()
|
||||
for _, tx := range txs {
|
||||
pool.Add(tx)
|
||||
}
|
||||
}
|
||||
|
||||
// Benchmarks the speed of batched transaction insertion.
|
||||
func BenchmarkPoolBatchInsert100(b *testing.B) { benchmarkPoolBatchInsert(b, 100) }
|
||||
func BenchmarkPoolBatchInsert1000(b *testing.B) { benchmarkPoolBatchInsert(b, 1000) }
|
||||
func BenchmarkPoolBatchInsert10000(b *testing.B) { benchmarkPoolBatchInsert(b, 10000) }
|
||||
|
||||
func benchmarkPoolBatchInsert(b *testing.B, size int) {
|
||||
// Generate a batch of transactions to enqueue into the pool
|
||||
pool, key := setupTxPool()
|
||||
account, _ := transaction(0, big.NewInt(0), key).From()
|
||||
state, _ := pool.currentState()
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
|
||||
batches := make([]types.Transactions, b.N)
|
||||
for i := 0; i < b.N; i++ {
|
||||
batches[i] = make(types.Transactions, size)
|
||||
for j := 0; j < size; j++ {
|
||||
batches[i][j] = transaction(uint64(size*i+j), big.NewInt(100000), key)
|
||||
}
|
||||
}
|
||||
// Benchmark importing the transactions into the queue
|
||||
b.ResetTimer()
|
||||
for _, batch := range batches {
|
||||
pool.AddBatch(batch)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,19 +23,18 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var ErrInvalidSig = errors.New("invalid v, r, s values")
|
||||
|
||||
type Transaction struct {
|
||||
signer Signer
|
||||
|
||||
data txdata
|
||||
// caches
|
||||
hash atomic.Value
|
||||
@@ -49,24 +48,27 @@ type txdata struct {
|
||||
Recipient *common.Address `rlp:"nil"` // nil means contract creation
|
||||
Amount *big.Int
|
||||
Payload []byte
|
||||
V byte // signature
|
||||
R, S *big.Int // signature
|
||||
V, R, S *big.Int // signature
|
||||
}
|
||||
|
||||
func NewContractCreation(nonce uint64, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
if len(data) > 0 {
|
||||
data = common.CopyBytes(data)
|
||||
}
|
||||
return &Transaction{data: txdata{
|
||||
AccountNonce: nonce,
|
||||
Recipient: nil,
|
||||
Amount: new(big.Int).Set(amount),
|
||||
GasLimit: new(big.Int).Set(gasLimit),
|
||||
Price: new(big.Int).Set(gasPrice),
|
||||
Payload: data,
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}}
|
||||
return &Transaction{
|
||||
signer: HomesteadSigner{},
|
||||
data: txdata{
|
||||
AccountNonce: nonce,
|
||||
Recipient: nil,
|
||||
Amount: new(big.Int).Set(amount),
|
||||
GasLimit: new(big.Int).Set(gasLimit),
|
||||
Price: new(big.Int).Set(gasPrice),
|
||||
Payload: data,
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
@@ -80,6 +82,7 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
|
||||
Amount: new(big.Int),
|
||||
GasLimit: new(big.Int),
|
||||
Price: new(big.Int),
|
||||
V: new(big.Int),
|
||||
R: new(big.Int),
|
||||
S: new(big.Int),
|
||||
}
|
||||
@@ -92,7 +95,24 @@ func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice
|
||||
if gasPrice != nil {
|
||||
d.Price.Set(gasPrice)
|
||||
}
|
||||
return &Transaction{data: d}
|
||||
return &Transaction{signer: HomesteadSigner{}, data: d}
|
||||
}
|
||||
|
||||
func (tx *Transaction) SetSigner(s Signer) {
|
||||
// reset the cached value incase another value was cached
|
||||
tx.from.Store((*common.Address)(nil))
|
||||
|
||||
tx.signer = s
|
||||
}
|
||||
|
||||
// ChainId returns which chain id this transaction was signed for (if at all)
|
||||
func (tx *Transaction) ChainId() *big.Int {
|
||||
return deriveChainId(tx.data.V)
|
||||
}
|
||||
|
||||
// Protected returns whether the transaction is pretected from replay protection
|
||||
func (tx *Transaction) Protected() bool {
|
||||
return isProtectedV(tx.data.V)
|
||||
}
|
||||
|
||||
func (tx *Transaction) EncodeRLP(w io.Writer) error {
|
||||
@@ -105,6 +125,7 @@ func (tx *Transaction) DecodeRLP(s *rlp.Stream) error {
|
||||
if err == nil {
|
||||
tx.size.Store(common.StorageSize(rlp.ListSize(size)))
|
||||
}
|
||||
tx.signer = HomesteadSigner{}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -137,14 +158,7 @@ func (tx *Transaction) Hash() common.Hash {
|
||||
// SigHash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (tx *Transaction) SigHash() common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
})
|
||||
return tx.signer.Hash(tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) Size() common.StorageSize {
|
||||
@@ -195,15 +209,17 @@ func (tx *Transaction) FromFrontier() (common.Address, error) {
|
||||
|
||||
func doFrom(tx *Transaction, homestead bool) (common.Address, error) {
|
||||
if from := tx.from.Load(); from != nil {
|
||||
return from.(common.Address), nil
|
||||
if faddr := from.(*common.Address); faddr != nil {
|
||||
return *faddr, nil
|
||||
}
|
||||
}
|
||||
pubkey, err := tx.publicKey(homestead)
|
||||
pubkey, err := tx.signer.PublicKey(tx)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
var addr common.Address
|
||||
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
tx.from.Store(addr)
|
||||
tx.from.Store(&addr)
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
@@ -215,52 +231,20 @@ func (tx *Transaction) Cost() *big.Int {
|
||||
}
|
||||
|
||||
func (tx *Transaction) SignatureValues() (v byte, r *big.Int, s *big.Int) {
|
||||
return tx.data.V, new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
|
||||
return SignatureValues(tx.signer, tx)
|
||||
}
|
||||
|
||||
func (tx *Transaction) publicKey(homestead bool) ([]byte, error) {
|
||||
if !crypto.ValidateSignatureValues(tx.data.V, tx.data.R, tx.data.S, homestead) {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
|
||||
// encode the signature in uncompressed format
|
||||
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
copy(sig[32-len(r):32], r)
|
||||
copy(sig[64-len(s):64], s)
|
||||
sig[64] = tx.data.V - 27
|
||||
|
||||
// recover the public key from the signature
|
||||
hash := tx.SigHash()
|
||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
||||
if err != nil {
|
||||
glog.V(logger.Error).Infof("Could not get pubkey from signature: ", err)
|
||||
return nil, err
|
||||
}
|
||||
if len(pub) == 0 || pub[0] != 4 {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return pub, nil
|
||||
func (tx *Transaction) RawSignatureValues() (v *big.Int, r *big.Int, s *big.Int) {
|
||||
return tx.data.V, tx.data.R, tx.data.S
|
||||
}
|
||||
|
||||
func (tx *Transaction) WithSignature(sig []byte) (*Transaction, error) {
|
||||
if len(sig) != 65 {
|
||||
panic(fmt.Sprintf("wrong size for signature: got %d, want 65", len(sig)))
|
||||
}
|
||||
cpy := &Transaction{data: tx.data}
|
||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
||||
cpy.data.V = sig[64] + 27
|
||||
return cpy, nil
|
||||
return tx.signer.WithSignature(tx, sig)
|
||||
}
|
||||
|
||||
func (tx *Transaction) SignECDSA(prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := tx.SigHash()
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return tx.WithSignature(sig)
|
||||
tx, err := tx.signer.SignECDSA(tx, prv)
|
||||
return tx, err
|
||||
}
|
||||
|
||||
func (tx *Transaction) String() string {
|
||||
@@ -369,49 +353,58 @@ func (s *TxByPrice) Pop() interface{} {
|
||||
return x
|
||||
}
|
||||
|
||||
// SortByPriceAndNonce sorts the transactions by price in such a way that the
|
||||
// nonce orderings within a single account are maintained.
|
||||
// TransactionsByPriceAndNonce represents a set of transactions that can return
|
||||
// transactions in a profit-maximising sorted order, while supporting removing
|
||||
// entire batches of transactions for non-executable accounts.
|
||||
type TransactionsByPriceAndNonce struct {
|
||||
txs map[common.Address]Transactions // Per account nonce-sorted list of transactions
|
||||
heads TxByPrice // Next transaction for each unique account (price heap)
|
||||
}
|
||||
|
||||
// NewTransactionsByPriceAndNonce creates a transaction set that can retrieve
|
||||
// price sorted transactions in a nonce-honouring way.
|
||||
//
|
||||
// Note, this is not as trivial as it seems from the first look as there are three
|
||||
// different criteria that need to be taken into account (price, nonce, account
|
||||
// match), which cannot be done with any plain sorting method, as certain items
|
||||
// cannot be compared without context.
|
||||
//
|
||||
// This method first sorts the separates the list of transactions into individual
|
||||
// sender accounts and sorts them by nonce. After the account nonce ordering is
|
||||
// satisfied, the results are merged back together by price, always comparing only
|
||||
// the head transaction from each account. This is done via a heap to keep it fast.
|
||||
func SortByPriceAndNonce(txs []*Transaction) {
|
||||
// Separate the transactions by account and sort by nonce
|
||||
byNonce := make(map[common.Address][]*Transaction)
|
||||
for _, tx := range txs {
|
||||
acc, _ := tx.From() // we only sort valid txs so this cannot fail
|
||||
byNonce[acc] = append(byNonce[acc], tx)
|
||||
}
|
||||
for _, accTxs := range byNonce {
|
||||
sort.Sort(TxByNonce(accTxs))
|
||||
}
|
||||
// Note, the input map is reowned so the caller should not interact any more with
|
||||
// if after providng it to the constructor.
|
||||
func NewTransactionsByPriceAndNonce(txs map[common.Address]Transactions) *TransactionsByPriceAndNonce {
|
||||
// Initialize a price based heap with the head transactions
|
||||
byPrice := make(TxByPrice, 0, len(byNonce))
|
||||
for acc, accTxs := range byNonce {
|
||||
byPrice = append(byPrice, accTxs[0])
|
||||
byNonce[acc] = accTxs[1:]
|
||||
heads := make(TxByPrice, 0, len(txs))
|
||||
for acc, accTxs := range txs {
|
||||
heads = append(heads, accTxs[0])
|
||||
txs[acc] = accTxs[1:]
|
||||
}
|
||||
heap.Init(&byPrice)
|
||||
heap.Init(&heads)
|
||||
|
||||
// Merge by replacing the best with the next from the same account
|
||||
txs = txs[:0]
|
||||
for len(byPrice) > 0 {
|
||||
// Retrieve the next best transaction by price
|
||||
best := heap.Pop(&byPrice).(*Transaction)
|
||||
|
||||
// Push in its place the next transaction from the same account
|
||||
acc, _ := best.From() // we only sort valid txs so this cannot fail
|
||||
if accTxs, ok := byNonce[acc]; ok && len(accTxs) > 0 {
|
||||
heap.Push(&byPrice, accTxs[0])
|
||||
byNonce[acc] = accTxs[1:]
|
||||
}
|
||||
// Accumulate the best priced transaction
|
||||
txs = append(txs, best)
|
||||
// Assemble and return the transaction set
|
||||
return &TransactionsByPriceAndNonce{
|
||||
txs: txs,
|
||||
heads: heads,
|
||||
}
|
||||
}
|
||||
|
||||
// Peek returns the next transaction by price.
|
||||
func (t *TransactionsByPriceAndNonce) Peek() *Transaction {
|
||||
if len(t.heads) == 0 {
|
||||
return nil
|
||||
}
|
||||
return t.heads[0]
|
||||
}
|
||||
|
||||
// Shift replaces the current best head with the next one from the same account.
|
||||
func (t *TransactionsByPriceAndNonce) Shift() {
|
||||
acc, _ := t.heads[0].From() // we only sort valid txs so this cannot fail
|
||||
|
||||
if txs, ok := t.txs[acc]; ok && len(txs) > 0 {
|
||||
t.heads[0], t.txs[acc] = txs[0], txs[1:]
|
||||
heap.Fix(&t.heads, 0)
|
||||
} else {
|
||||
heap.Pop(&t.heads)
|
||||
}
|
||||
}
|
||||
|
||||
// Pop removes the best transaction, *not* replacing it with the next one from
|
||||
// the same account. This should be used when a transaction cannot be executed
|
||||
// and hence all subsequent ones should be discarded from the same account.
|
||||
func (t *TransactionsByPriceAndNonce) Pop() {
|
||||
heap.Pop(&t.heads)
|
||||
}
|
||||
|
||||
376
core/types/transaction_signing.go
Normal file
376
core/types/transaction_signing.go
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright 2016 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 types
|
||||
|
||||
import (
|
||||
"crypto/ecdsa"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var ErrInvalidChainId = errors.New("invalid chain id for signer")
|
||||
|
||||
// deriveSigner makes a *best* guess about which signer to use.
|
||||
func deriveSigner(V *big.Int) Signer {
|
||||
if V.BitLen() > 0 && isProtectedV(V) {
|
||||
return EIP155Signer{chainId: deriveChainId(V)}
|
||||
} else {
|
||||
return HomesteadSigner{}
|
||||
}
|
||||
}
|
||||
|
||||
func pickSigner(rules params.Rules) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case rules.IsEIP155:
|
||||
signer = NewEIP155Signer(rules.ChainId)
|
||||
case rules.IsHomestead:
|
||||
signer = HomesteadSigner{}
|
||||
default:
|
||||
signer = FrontierSigner{}
|
||||
}
|
||||
return signer
|
||||
}
|
||||
|
||||
func isProtectedV(V *big.Int) bool {
|
||||
if V.BitLen() <= 8 {
|
||||
v := V.Uint64()
|
||||
return v != 27 && v != 28
|
||||
}
|
||||
// anything not 27 or 28 are considered unprotected
|
||||
return true
|
||||
}
|
||||
|
||||
// MakeSigner returns a Signer based on the given chain config and block number.
|
||||
func MakeSigner(config *params.ChainConfig, blockNumber *big.Int) Signer {
|
||||
var signer Signer
|
||||
switch {
|
||||
case config.IsEIP155(blockNumber):
|
||||
signer = NewEIP155Signer(config.ChainId)
|
||||
case config.IsHomestead(blockNumber):
|
||||
signer = HomesteadSigner{}
|
||||
default:
|
||||
signer = FrontierSigner{}
|
||||
}
|
||||
return signer
|
||||
}
|
||||
|
||||
// SignECDSA signs the transaction using the given signer and private key
|
||||
func SignECDSA(s Signer, tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WithSignature(tx, sig)
|
||||
}
|
||||
|
||||
// From derives the sender from the tx using the signer derivation
|
||||
// functions.
|
||||
|
||||
// From returns the address derived from the signature (V, R, S) using secp256k1
|
||||
// elliptic curve and an error if it failed deriving or upon an incorrect
|
||||
// signature.
|
||||
//
|
||||
// From may cache the address, allowing it to be used regardless of
|
||||
// signing method.
|
||||
func From(signer Signer, tx *Transaction, cache bool) (common.Address, error) {
|
||||
if from := tx.from.Load(); from != nil {
|
||||
return from.(common.Address), nil
|
||||
}
|
||||
|
||||
pubkey, err := signer.PublicKey(tx)
|
||||
if err != nil {
|
||||
return common.Address{}, err
|
||||
}
|
||||
var addr common.Address
|
||||
copy(addr[:], crypto.Keccak256(pubkey[1:])[12:])
|
||||
if cache {
|
||||
tx.from.Store(addr)
|
||||
}
|
||||
return addr, nil
|
||||
}
|
||||
|
||||
// SignatureValues returns the ECDSA signature values contained in the transaction.
|
||||
func SignatureValues(signer Signer, tx *Transaction) (v byte, r *big.Int, s *big.Int) {
|
||||
return normaliseV(signer, tx.data.V), new(big.Int).Set(tx.data.R), new(big.Int).Set(tx.data.S)
|
||||
}
|
||||
|
||||
type Signer interface {
|
||||
// Hash returns the rlp encoded hash for signatures
|
||||
Hash(tx *Transaction) common.Hash
|
||||
// PubilcKey returns the public key derived from the signature
|
||||
PublicKey(tx *Transaction) ([]byte, error)
|
||||
// SignECDSA signs the transaction with the given and returns a copy of the tx
|
||||
SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error)
|
||||
// WithSignature returns a copy of the transaction with the given signature
|
||||
WithSignature(tx *Transaction, sig []byte) (*Transaction, error)
|
||||
}
|
||||
|
||||
// EIP155Transaction implements TransactionInterface using the
|
||||
// EIP155 rules
|
||||
type EIP155Signer struct {
|
||||
HomesteadSigner
|
||||
|
||||
chainId, chainIdMul *big.Int
|
||||
}
|
||||
|
||||
func NewEIP155Signer(chainId *big.Int) EIP155Signer {
|
||||
return EIP155Signer{
|
||||
chainId: chainId,
|
||||
chainIdMul: new(big.Int).Mul(chainId, big.NewInt(2)),
|
||||
}
|
||||
}
|
||||
|
||||
func (s EIP155Signer) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
return SignECDSA(s, tx, prv)
|
||||
}
|
||||
|
||||
func (s EIP155Signer) PublicKey(tx *Transaction) ([]byte, error) {
|
||||
// if the transaction is not protected fall back to homestead signer
|
||||
if !tx.Protected() {
|
||||
return (HomesteadSigner{}).PublicKey(tx)
|
||||
}
|
||||
if tx.ChainId().Cmp(s.chainId) != 0 {
|
||||
return nil, ErrInvalidChainId
|
||||
}
|
||||
|
||||
V := normaliseV(s, tx.data.V)
|
||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
|
||||
// encode the signature in uncompressed format
|
||||
R, S := tx.data.R.Bytes(), tx.data.S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
copy(sig[32-len(R):32], R)
|
||||
copy(sig[64-len(S):64], S)
|
||||
sig[64] = V - 27
|
||||
|
||||
// recover the public key from the signature
|
||||
hash := s.Hash(tx)
|
||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pub) == 0 || pub[0] != 4 {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// WithSignature returns a new transaction with the given signature.
|
||||
// This signature needs to be formatted as described in the yellow paper (v+27).
|
||||
func (s EIP155Signer) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
||||
if len(sig) != 65 {
|
||||
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
|
||||
}
|
||||
|
||||
cpy := &Transaction{signer: tx.signer, data: tx.data}
|
||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64]})
|
||||
if s.chainId.BitLen() > 0 {
|
||||
cpy.data.V = big.NewInt(int64(sig[64] + 35))
|
||||
cpy.data.V.Add(cpy.data.V, s.chainIdMul)
|
||||
}
|
||||
return cpy, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be signed by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (s EIP155Signer) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
s.chainId, uint(0), uint(0),
|
||||
})
|
||||
}
|
||||
|
||||
func (s EIP155Signer) SigECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := s.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.WithSignature(tx, sig)
|
||||
}
|
||||
|
||||
// HomesteadTransaction implements TransactionInterface using the
|
||||
// homestead rules.
|
||||
type HomesteadSigner struct{}
|
||||
|
||||
// WithSignature returns a new transaction with the given snature.
|
||||
// This snature needs to be formatted as described in the yellow paper (v+27).
|
||||
func (hs HomesteadSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
||||
if len(sig) != 65 {
|
||||
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
|
||||
}
|
||||
cpy := &Transaction{signer: tx.signer, data: tx.data}
|
||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
return cpy, nil
|
||||
}
|
||||
|
||||
func (hs HomesteadSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := hs.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return hs.WithSignature(tx, sig)
|
||||
}
|
||||
|
||||
func (hs HomesteadSigner) PublicKey(tx *Transaction) ([]byte, error) {
|
||||
if tx.data.V.BitLen() > 8 {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
V := byte(tx.data.V.Uint64())
|
||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, true) {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
// encode the snature in uncompressed format
|
||||
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
copy(sig[32-len(r):32], r)
|
||||
copy(sig[64-len(s):64], s)
|
||||
sig[64] = V - 27
|
||||
|
||||
// recover the public key from the snature
|
||||
hash := hs.Hash(tx)
|
||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pub) == 0 || pub[0] != 4 {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// Hash returns the hash to be sned by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (hs HomesteadSigner) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
})
|
||||
}
|
||||
|
||||
type FrontierSigner struct{}
|
||||
|
||||
// WithSignature returns a new transaction with the given snature.
|
||||
// This snature needs to be formatted as described in the yellow paper (v+27).
|
||||
func (fs FrontierSigner) WithSignature(tx *Transaction, sig []byte) (*Transaction, error) {
|
||||
if len(sig) != 65 {
|
||||
panic(fmt.Sprintf("wrong size for snature: got %d, want 65", len(sig)))
|
||||
}
|
||||
cpy := &Transaction{signer: tx.signer, data: tx.data}
|
||||
cpy.data.R = new(big.Int).SetBytes(sig[:32])
|
||||
cpy.data.S = new(big.Int).SetBytes(sig[32:64])
|
||||
cpy.data.V = new(big.Int).SetBytes([]byte{sig[64] + 27})
|
||||
return cpy, nil
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) SignECDSA(tx *Transaction, prv *ecdsa.PrivateKey) (*Transaction, error) {
|
||||
h := fs.Hash(tx)
|
||||
sig, err := crypto.Sign(h[:], prv)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return fs.WithSignature(tx, sig)
|
||||
}
|
||||
|
||||
// Hash returns the hash to be sned by the sender.
|
||||
// It does not uniquely identify the transaction.
|
||||
func (fs FrontierSigner) Hash(tx *Transaction) common.Hash {
|
||||
return rlpHash([]interface{}{
|
||||
tx.data.AccountNonce,
|
||||
tx.data.Price,
|
||||
tx.data.GasLimit,
|
||||
tx.data.Recipient,
|
||||
tx.data.Amount,
|
||||
tx.data.Payload,
|
||||
})
|
||||
}
|
||||
|
||||
func (fs FrontierSigner) PublicKey(tx *Transaction) ([]byte, error) {
|
||||
if tx.data.V.BitLen() > 8 {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
|
||||
V := byte(tx.data.V.Uint64())
|
||||
if !crypto.ValidateSignatureValues(V, tx.data.R, tx.data.S, false) {
|
||||
return nil, ErrInvalidSig
|
||||
}
|
||||
// encode the snature in uncompressed format
|
||||
r, s := tx.data.R.Bytes(), tx.data.S.Bytes()
|
||||
sig := make([]byte, 65)
|
||||
copy(sig[32-len(r):32], r)
|
||||
copy(sig[64-len(s):64], s)
|
||||
sig[64] = V - 27
|
||||
|
||||
// recover the public key from the snature
|
||||
hash := fs.Hash(tx)
|
||||
pub, err := crypto.Ecrecover(hash[:], sig)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(pub) == 0 || pub[0] != 4 {
|
||||
return nil, errors.New("invalid public key")
|
||||
}
|
||||
return pub, nil
|
||||
}
|
||||
|
||||
// normaliseV returns the Ethereum version of the V parameter
|
||||
func normaliseV(s Signer, v *big.Int) byte {
|
||||
if s, ok := s.(EIP155Signer); ok {
|
||||
stdV := v.BitLen() <= 8 && (v.Uint64() == 27 || v.Uint64() == 28)
|
||||
if s.chainId.BitLen() > 0 && !stdV {
|
||||
nv := byte((new(big.Int).Sub(v, s.chainIdMul).Uint64()) - 35 + 27)
|
||||
return nv
|
||||
}
|
||||
}
|
||||
return byte(v.Uint64())
|
||||
}
|
||||
|
||||
// deriveChainId derives the chain id from the given v parameter
|
||||
func deriveChainId(v *big.Int) *big.Int {
|
||||
if v.BitLen() <= 64 {
|
||||
v := v.Uint64()
|
||||
if v == 27 || v == 28 {
|
||||
return new(big.Int)
|
||||
}
|
||||
return new(big.Int).SetUint64((v - 35) / 2)
|
||||
}
|
||||
v = new(big.Int).Sub(v, big.NewInt(35))
|
||||
return v.Div(v, big.NewInt(2))
|
||||
}
|
||||
33
core/types/transaction_signing_test.go
Normal file
33
core/types/transaction_signing_test.go
Normal file
@@ -0,0 +1,33 @@
|
||||
package types
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func TestChainId(t *testing.T) {
|
||||
key, _ := defaultTestKey()
|
||||
|
||||
tx := NewTransaction(0, common.Address{}, new(big.Int), new(big.Int), new(big.Int), nil)
|
||||
tx.SetSigner(NewEIP155Signer(big.NewInt(1)))
|
||||
|
||||
var err error
|
||||
tx, err = tx.SignECDSA(key)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
tx.SetSigner(NewEIP155Signer(big.NewInt(2)))
|
||||
_, err = tx.From()
|
||||
if err != ErrInvalidChainId {
|
||||
t.Error("expected error:", ErrInvalidChainId)
|
||||
}
|
||||
|
||||
tx.SetSigner(NewEIP155Signer(big.NewInt(1)))
|
||||
_, err = tx.From()
|
||||
if err != nil {
|
||||
t.Error("expected no error")
|
||||
}
|
||||
}
|
||||
@@ -128,15 +128,25 @@ func TestTransactionPriceNonceSort(t *testing.T) {
|
||||
keys[i], _ = crypto.GenerateKey()
|
||||
}
|
||||
// Generate a batch of transactions with overlapping values, but shifted nonces
|
||||
txs := []*Transaction{}
|
||||
groups := map[common.Address]Transactions{}
|
||||
for start, key := range keys {
|
||||
addr := crypto.PubkeyToAddress(key.PublicKey)
|
||||
for i := 0; i < 25; i++ {
|
||||
tx, _ := NewTransaction(uint64(start+i), common.Address{}, big.NewInt(100), big.NewInt(100), big.NewInt(int64(start+i)), nil).SignECDSA(key)
|
||||
txs = append(txs, tx)
|
||||
groups[addr] = append(groups[addr], tx)
|
||||
}
|
||||
}
|
||||
// Sort the transactions and cross check the nonce ordering
|
||||
SortByPriceAndNonce(txs)
|
||||
txset := NewTransactionsByPriceAndNonce(groups)
|
||||
|
||||
txs := Transactions{}
|
||||
for {
|
||||
if tx := txset.Peek(); tx != nil {
|
||||
txs = append(txs, tx)
|
||||
txset.Shift()
|
||||
}
|
||||
break
|
||||
}
|
||||
for i, txi := range txs {
|
||||
fromi, _ := txi.From()
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ type ContractRef interface {
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
Address() common.Address
|
||||
Value() *big.Int
|
||||
SetCode([]byte)
|
||||
SetCode(common.Hash, []byte)
|
||||
ForEachStorage(callback func(key, value common.Hash) bool)
|
||||
}
|
||||
|
||||
@@ -44,8 +44,9 @@ type Contract struct {
|
||||
jumpdests destinations // result of JUMPDEST analysis.
|
||||
|
||||
Code []byte
|
||||
Input []byte
|
||||
CodeHash common.Hash
|
||||
CodeAddr *common.Address
|
||||
Input []byte
|
||||
|
||||
value, Gas, UsedGas, Price *big.Int
|
||||
|
||||
@@ -143,14 +144,16 @@ func (c *Contract) Value() *big.Int {
|
||||
}
|
||||
|
||||
// SetCode sets the code to the contract
|
||||
func (self *Contract) SetCode(code []byte) {
|
||||
func (self *Contract) SetCode(hash common.Hash, code []byte) {
|
||||
self.Code = code
|
||||
self.CodeHash = hash
|
||||
}
|
||||
|
||||
// SetCallCode sets the code of the contract and address of the backing data
|
||||
// object
|
||||
func (self *Contract) SetCallCode(addr *common.Address, code []byte) {
|
||||
func (self *Contract) SetCallCode(addr *common.Address, hash common.Hash, code []byte) {
|
||||
self.Code = code
|
||||
self.CodeHash = hash
|
||||
self.CodeAddr = addr
|
||||
}
|
||||
|
||||
|
||||
@@ -20,25 +20,20 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// RuleSet is an interface that defines the current rule set during the
|
||||
// execution of the EVM instructions (e.g. whether it's homestead)
|
||||
type RuleSet interface {
|
||||
IsHomestead(*big.Int) bool
|
||||
}
|
||||
|
||||
// Environment is an EVM requirement and helper which allows access to outside
|
||||
// information such as states.
|
||||
type Environment interface {
|
||||
// The current ruleset
|
||||
RuleSet() RuleSet
|
||||
ChainConfig() *params.ChainConfig
|
||||
// The state database
|
||||
Db() Database
|
||||
// Creates a restorable snapshot
|
||||
MakeSnapshot() Database
|
||||
SnapshotDatabase() int
|
||||
// Set database to previous snapshot
|
||||
SetSnapshot(Database)
|
||||
RevertToSnapshot(int)
|
||||
// Address of the original invoker (first occurrence of the VM invoker)
|
||||
Origin() common.Address
|
||||
// The block number this VM is invoked on
|
||||
@@ -94,6 +89,7 @@ type Database interface {
|
||||
GetNonce(common.Address) uint64
|
||||
SetNonce(common.Address, uint64)
|
||||
|
||||
GetCodeHash(common.Address) common.Hash
|
||||
GetCodeSize(common.Address) int
|
||||
GetCode(common.Address) []byte
|
||||
SetCode(common.Address, []byte)
|
||||
@@ -104,9 +100,13 @@ type Database interface {
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
Delete(common.Address) bool
|
||||
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
|
||||
IsDeleted(common.Address) bool
|
||||
Empty(common.Address) bool
|
||||
}
|
||||
|
||||
// Account represents a contract or basic ethereum account.
|
||||
@@ -118,7 +118,7 @@ type Account interface {
|
||||
Balance() *big.Int
|
||||
Address() common.Address
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
SetCode([]byte)
|
||||
SetCode(common.Hash, []byte)
|
||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||
Value() *big.Int
|
||||
}
|
||||
|
||||
@@ -35,8 +35,27 @@ var (
|
||||
GasStop = big.NewInt(0)
|
||||
|
||||
GasContractByte = big.NewInt(200)
|
||||
|
||||
n64 = big.NewInt(64)
|
||||
)
|
||||
|
||||
// calcGas returns the actual gas cost of the call.
|
||||
//
|
||||
// The cost of gas was changed during the homestead price change HF. To allow for EIP150
|
||||
// to be implemented. The returned gas is gas - base * 63 / 64.
|
||||
func callGas(gasTable params.GasTable, availableGas, base, callCost *big.Int) *big.Int {
|
||||
if gasTable.CreateBySuicide != nil {
|
||||
availableGas = new(big.Int).Sub(availableGas, base)
|
||||
g := new(big.Int).Div(availableGas, n64)
|
||||
g.Sub(availableGas, g)
|
||||
|
||||
if g.Cmp(callCost) < 0 {
|
||||
return g
|
||||
}
|
||||
}
|
||||
return callCost
|
||||
}
|
||||
|
||||
// baseCheck checks for any stack error underflows
|
||||
func baseCheck(op OpCode, stack *stack, gas *big.Int) error {
|
||||
// PUSH and DUP are a bit special. They all cost the same but we do want to have checking on stack push limit
|
||||
@@ -127,18 +146,19 @@ var _baseCheck = map[OpCode]req{
|
||||
MSIZE: {0, GasQuickStep, 1},
|
||||
GAS: {0, GasQuickStep, 1},
|
||||
BLOCKHASH: {1, GasExtStep, 1},
|
||||
BALANCE: {1, GasExtStep, 1},
|
||||
EXTCODESIZE: {1, GasExtStep, 1},
|
||||
EXTCODECOPY: {4, GasExtStep, 0},
|
||||
BALANCE: {1, Zero, 1},
|
||||
EXTCODESIZE: {1, Zero, 1},
|
||||
EXTCODECOPY: {4, Zero, 0},
|
||||
SLOAD: {1, params.SloadGas, 1},
|
||||
SSTORE: {2, Zero, 0},
|
||||
SHA3: {2, params.Sha3Gas, 1},
|
||||
CREATE: {3, params.CreateGas, 1},
|
||||
CALL: {7, params.CallGas, 1},
|
||||
CALLCODE: {7, params.CallGas, 1},
|
||||
DELEGATECALL: {6, params.CallGas, 1},
|
||||
JUMPDEST: {0, params.JumpdestGas, 0},
|
||||
// Zero is calculated in the gasSwitch
|
||||
CALL: {7, Zero, 1},
|
||||
CALLCODE: {7, Zero, 1},
|
||||
DELEGATECALL: {6, Zero, 1},
|
||||
SUICIDE: {1, Zero, 0},
|
||||
JUMPDEST: {0, params.JumpdestGas, 0},
|
||||
RETURN: {2, Zero, 0},
|
||||
PUSH1: {0, GasFastestStep, 1},
|
||||
DUP1: {0, Zero, 1},
|
||||
|
||||
@@ -514,13 +514,18 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
input = memory.Get(offset.Int64(), size.Int64())
|
||||
gas = new(big.Int).Set(contract.Gas)
|
||||
)
|
||||
contract.UseGas(contract.Gas)
|
||||
if env.ChainConfig().IsEIP150(env.BlockNumber()) {
|
||||
gas.Div(gas, n64)
|
||||
gas = gas.Sub(contract.Gas, gas)
|
||||
}
|
||||
|
||||
contract.UseGas(gas)
|
||||
_, addr, suberr := env.Create(contract, input, gas, contract.Price, value)
|
||||
// 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 env.RuleSet().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
|
||||
if env.ChainConfig().IsHomestead(env.BlockNumber()) && suberr == CodeStoreOutOfGasError {
|
||||
stack.push(new(big.Int))
|
||||
} else if suberr != nil && suberr != CodeStoreOutOfGasError {
|
||||
stack.push(new(big.Int))
|
||||
@@ -614,7 +619,7 @@ func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contrac
|
||||
balance := env.Db().GetBalance(contract.Address())
|
||||
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
|
||||
env.Db().Delete(contract.Address())
|
||||
env.Db().Suicide(contract.Address())
|
||||
}
|
||||
|
||||
// following functions are used by the instruction jump table
|
||||
|
||||
@@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *stack, env
|
||||
}()
|
||||
}
|
||||
|
||||
homestead := env.RuleSet().IsHomestead(env.BlockNumber())
|
||||
homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
|
||||
for pc < uint64(len(program.instructions)) {
|
||||
instrCount++
|
||||
|
||||
@@ -425,7 +425,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !statedb.IsDeleted(contract.Address()) {
|
||||
if !statedb.HasSuicided(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
const maxRun = 1000
|
||||
@@ -135,7 +136,7 @@ func (account) SetNonce(uint64) {}
|
||||
func (account) Balance() *big.Int { return nil }
|
||||
func (account) Address() common.Address { return common.Address{} }
|
||||
func (account) ReturnGas(*big.Int, *big.Int) {}
|
||||
func (account) SetCode([]byte) {}
|
||||
func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
func runVmBench(test vmBench, b *testing.B) {
|
||||
@@ -175,7 +176,9 @@ func NewEnv(noJit, forceJit bool) *Env {
|
||||
return env
|
||||
}
|
||||
|
||||
func (self *Env) RuleSet() RuleSet { return ruleSet{new(big.Int)} }
|
||||
func (self *Env) ChainConfig() *params.ChainConfig {
|
||||
return params.TestChainConfig
|
||||
}
|
||||
func (self *Env) Vm() Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return common.Address{} }
|
||||
func (self *Env) BlockNumber() *big.Int { return big.NewInt(0) }
|
||||
@@ -187,8 +190,8 @@ func (self *Env) StructLogs() []StructLog {
|
||||
|
||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||
func (self *Env) MakeSnapshot() Database { return nil }
|
||||
func (self *Env) SetSnapshot(Database) {}
|
||||
func (self *Env) SnapshotDatabase() int { return 0 }
|
||||
func (self *Env) RevertToSnapshot(int) {}
|
||||
func (self *Env) Time() *big.Int { return big.NewInt(time.Now().Unix()) }
|
||||
func (self *Env) Difficulty() *big.Int { return big.NewInt(0) }
|
||||
func (self *Env) Db() Database { return nil }
|
||||
|
||||
@@ -16,7 +16,11 @@
|
||||
|
||||
package vm
|
||||
|
||||
import "math/big"
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type jumpPtr struct {
|
||||
fn instrFn
|
||||
@@ -25,7 +29,7 @@ type jumpPtr struct {
|
||||
|
||||
type vmJumpTable [256]jumpPtr
|
||||
|
||||
func newJumpTable(ruleset RuleSet, blockNumber *big.Int) vmJumpTable {
|
||||
func newJumpTable(ruleset *params.ChainConfig, blockNumber *big.Int) vmJumpTable {
|
||||
var jumpTable vmJumpTable
|
||||
|
||||
// when initialising a new VM execution we must first check the homestead
|
||||
|
||||
@@ -19,16 +19,18 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
func TestInit(t *testing.T) {
|
||||
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(0))
|
||||
jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(0))
|
||||
if jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL not to be present")
|
||||
}
|
||||
|
||||
for _, n := range []int64{1, 2, 100} {
|
||||
jumpTable := newJumpTable(ruleSet{big.NewInt(1)}, big.NewInt(n))
|
||||
jumpTable := newJumpTable(¶ms.ChainConfig{HomesteadBlock: big.NewInt(1)}, big.NewInt(n))
|
||||
if !jumpTable[DELEGATECALL].valid {
|
||||
t.Error("Expected DELEGATECALL to be present for block", n)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ type dummyContractRef struct {
|
||||
func (dummyContractRef) ReturnGas(*big.Int, *big.Int) {}
|
||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||
func (dummyContractRef) SetCode([]byte) {}
|
||||
func (dummyContractRef) SetCode(common.Hash, []byte) {}
|
||||
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
||||
d.calledForEach = true
|
||||
}
|
||||
|
||||
@@ -23,13 +23,14 @@ import (
|
||||
"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/params"
|
||||
)
|
||||
|
||||
// Env is a basic runtime environment required for running the EVM.
|
||||
type Env struct {
|
||||
ruleSet vm.RuleSet
|
||||
depth int
|
||||
state *state.StateDB
|
||||
chainConfig *params.ChainConfig
|
||||
depth int
|
||||
state *state.StateDB
|
||||
|
||||
origin common.Address
|
||||
coinbase common.Address
|
||||
@@ -49,14 +50,14 @@ type Env struct {
|
||||
// NewEnv returns a new vm.Environment
|
||||
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
||||
env := &Env{
|
||||
ruleSet: cfg.RuleSet,
|
||||
state: state,
|
||||
origin: cfg.Origin,
|
||||
coinbase: cfg.Coinbase,
|
||||
number: cfg.BlockNumber,
|
||||
time: cfg.Time,
|
||||
difficulty: cfg.Difficulty,
|
||||
gasLimit: cfg.GasLimit,
|
||||
chainConfig: cfg.ChainConfig,
|
||||
state: state,
|
||||
origin: cfg.Origin,
|
||||
coinbase: cfg.Coinbase,
|
||||
number: cfg.BlockNumber,
|
||||
time: cfg.Time,
|
||||
difficulty: cfg.Difficulty,
|
||||
gasLimit: cfg.GasLimit,
|
||||
}
|
||||
env.evm = vm.New(env, vm.Config{
|
||||
Debug: cfg.Debug,
|
||||
@@ -79,16 +80,16 @@ func (self *Env) AddStructLog(log vm.StructLog) {
|
||||
self.logs = append(self.logs, log)
|
||||
}
|
||||
|
||||
func (self *Env) RuleSet() vm.RuleSet { return self.ruleSet }
|
||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return self.origin }
|
||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
||||
func (self *Env) Coinbase() common.Address { return self.coinbase }
|
||||
func (self *Env) Time() *big.Int { return self.time }
|
||||
func (self *Env) Difficulty() *big.Int { return self.difficulty }
|
||||
func (self *Env) Db() vm.Database { return self.state }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *Env) ChainConfig() *params.ChainConfig { return self.chainConfig }
|
||||
func (self *Env) Vm() vm.Vm { return self.evm }
|
||||
func (self *Env) Origin() common.Address { return self.origin }
|
||||
func (self *Env) BlockNumber() *big.Int { return self.number }
|
||||
func (self *Env) Coinbase() common.Address { return self.coinbase }
|
||||
func (self *Env) Time() *big.Int { return self.time }
|
||||
func (self *Env) Difficulty() *big.Int { return self.difficulty }
|
||||
func (self *Env) Db() vm.Database { return self.state }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) VmType() vm.Type { return vm.StdVmTy }
|
||||
func (self *Env) GetHash(n uint64) common.Hash {
|
||||
return self.getHashFn(n)
|
||||
}
|
||||
@@ -100,11 +101,11 @@ func (self *Env) SetDepth(i int) { self.depth = i }
|
||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
func (self *Env) MakeSnapshot() vm.Database {
|
||||
return self.state.Copy()
|
||||
func (self *Env) SnapshotDatabase() int {
|
||||
return self.state.Snapshot()
|
||||
}
|
||||
func (self *Env) SetSnapshot(copy vm.Database) {
|
||||
self.state.Set(copy.(*state.StateDB))
|
||||
func (self *Env) RevertToSnapshot(snapshot int) {
|
||||
self.state.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
||||
|
||||
@@ -22,20 +22,23 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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"
|
||||
)
|
||||
|
||||
// The default, always homestead, rule set for the vm env
|
||||
type ruleSet struct{}
|
||||
|
||||
func (ruleSet) IsHomestead(*big.Int) bool { return true }
|
||||
func (ruleSet) GasTable(*big.Int) params.GasTable {
|
||||
return params.GasTableHomesteadGasRepriceFork
|
||||
}
|
||||
|
||||
// Config is a basic type specifying certain configuration flags for running
|
||||
// the EVM.
|
||||
type Config struct {
|
||||
RuleSet vm.RuleSet
|
||||
ChainConfig *params.ChainConfig
|
||||
Difficulty *big.Int
|
||||
Origin common.Address
|
||||
Coinbase common.Address
|
||||
@@ -53,8 +56,8 @@ type Config struct {
|
||||
|
||||
// sets defaults on the config
|
||||
func setDefaults(cfg *Config) {
|
||||
if cfg.RuleSet == nil {
|
||||
cfg.RuleSet = ruleSet{}
|
||||
if cfg.ChainConfig == nil {
|
||||
cfg.ChainConfig = params.TestChainConfig
|
||||
}
|
||||
|
||||
if cfg.Difficulty == nil {
|
||||
@@ -104,7 +107,7 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
receiver = cfg.State.CreateAccount(common.StringToAddress("contract"))
|
||||
)
|
||||
// set the receiver's (the executing contract) code for execution.
|
||||
receiver.SetCode(code)
|
||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
ret, err := vmenv.Call(
|
||||
|
||||
@@ -16,10 +16,17 @@
|
||||
|
||||
package vm
|
||||
|
||||
import "math/big"
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type ruleSet struct {
|
||||
hs *big.Int
|
||||
}
|
||||
|
||||
func (r ruleSet) IsHomestead(n *big.Int) bool { return n.Cmp(r.hs) >= 0 }
|
||||
func (r ruleSet) GasTable(*big.Int) params.GasTable {
|
||||
return params.GasTableHomestead
|
||||
}
|
||||
|
||||
120
core/vm/vm.go
120
core/vm/vm.go
@@ -44,8 +44,8 @@ type EVM struct {
|
||||
env Environment
|
||||
jumpTable vmJumpTable
|
||||
cfg Config
|
||||
|
||||
logger *Logger
|
||||
logger *Logger
|
||||
gasTable params.GasTable
|
||||
}
|
||||
|
||||
// New returns a new instance of the EVM.
|
||||
@@ -57,9 +57,10 @@ func New(env Environment, cfg Config) *EVM {
|
||||
|
||||
return &EVM{
|
||||
env: env,
|
||||
jumpTable: newJumpTable(env.RuleSet(), env.BlockNumber()),
|
||||
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
|
||||
cfg: cfg,
|
||||
logger: logger,
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,10 +80,11 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
var (
|
||||
codehash = crypto.Keccak256Hash(contract.Code) // codehash is used when doing jump dest caching
|
||||
program *Program
|
||||
)
|
||||
codehash := contract.CodeHash // codehash is used when doing jump dest caching
|
||||
if codehash == (common.Hash{}) {
|
||||
codehash = crypto.Keccak256Hash(contract.Code)
|
||||
}
|
||||
var program *Program
|
||||
if evm.cfg.EnableJit {
|
||||
// If the JIT is enabled check the status of the JIT program,
|
||||
// if it doesn't exist compile a new program in a separate
|
||||
@@ -175,8 +177,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
|
||||
// Get the memory location of pc
|
||||
op = contract.GetOp(pc)
|
||||
//fmt.Printf("OP %d %v\n", op, op)
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err = calculateGasAndSize(evm.env, contract, caller, op, statedb, mem, stack)
|
||||
newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -241,7 +244,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
|
||||
// calculateGasAndSize calculates the required given the opcode and stack items calculates the new memorysize for
|
||||
// the operation. This does not reduce gas or resizes the memory.
|
||||
func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
||||
func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Contract, caller ContractRef, op OpCode, statedb Database, mem *Memory, stack *stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
@@ -253,6 +256,34 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
||||
|
||||
// stack Check, memory resize & gas phase
|
||||
switch op {
|
||||
case SUICIDE:
|
||||
// EIP150 homestead gas reprice fork:
|
||||
if gasTable.CreateBySuicide != nil {
|
||||
gas.Set(gasTable.Suicide)
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-1])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
||||
)
|
||||
|
||||
if eip158 {
|
||||
// if empty and transfers value
|
||||
if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
} else if !env.Db().Exist(address) {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
}
|
||||
|
||||
if !statedb.HasSuicided(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case EXTCODESIZE:
|
||||
gas.Set(gasTable.ExtcodeSize)
|
||||
case BALANCE:
|
||||
gas.Set(gasTable.Balance)
|
||||
case SLOAD:
|
||||
gas.Set(gasTable.SLoad)
|
||||
case SWAP1, SWAP2, SWAP3, SWAP4, SWAP5, SWAP6, SWAP7, SWAP8, SWAP9, SWAP10, SWAP11, SWAP12, SWAP13, SWAP14, SWAP15, SWAP16:
|
||||
n := int(op - SWAP1 + 2)
|
||||
err := stack.require(n)
|
||||
@@ -281,8 +312,11 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
||||
gas.Add(gas, new(big.Int).Mul(mSize, params.LogDataGas))
|
||||
|
||||
newMemSize = calcMemSize(mStart, mSize)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case EXP:
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(int64(len(stack.data[stack.len()-2].Bytes()))), params.ExpByteGas))
|
||||
expByteLen := int64((stack.data[stack.len()-2].BitLen() + 7) / 8)
|
||||
gas.Add(gas, new(big.Int).Mul(big.NewInt(expByteLen), gasTable.ExpByte))
|
||||
case SSTORE:
|
||||
err := stack.require(2)
|
||||
if err != nil {
|
||||
@@ -309,67 +343,109 @@ func calculateGasAndSize(env Environment, contract *Contract, caller ContractRef
|
||||
g = params.SstoreClearGas
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !statedb.IsDeleted(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case MSTORE8:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(1))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case MSTORE:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case RETURN:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case SHA3:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-2])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-2])
|
||||
gas.Add(gas, words.Mul(words, params.Sha3WordGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CALLDATACOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CODECOPY:
|
||||
newMemSize = calcMemSize(stack.peek(), stack.data[stack.len()-3])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-3])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case EXTCODECOPY:
|
||||
gas.Set(gasTable.ExtcodeCopy)
|
||||
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-4])
|
||||
|
||||
words := toWordSize(stack.data[stack.len()-4])
|
||||
gas.Add(gas, words.Mul(words, params.CopyGas))
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CREATE:
|
||||
newMemSize = calcMemSize(stack.data[stack.len()-2], stack.data[stack.len()-3])
|
||||
case CALL, CALLCODE:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
case CALL, CALLCODE:
|
||||
gas.Set(gasTable.Calls)
|
||||
|
||||
transfersValue := stack.data[len(stack.data)-3].BitLen() > 0
|
||||
if op == CALL {
|
||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-2])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
||||
)
|
||||
if eip158 {
|
||||
if env.Db().Empty(address) && transfersValue {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
} else if !env.Db().Exist(address) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack.data[stack.len()-3].Bytes()) > 0 {
|
||||
if transfersValue {
|
||||
gas.Add(gas, params.CallValueTransferGas)
|
||||
}
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-6], stack.data[stack.len()-7])
|
||||
y := calcMemSize(stack.data[stack.len()-4], stack.data[stack.len()-5])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called. This information is otherwise lost due to the dependency on *current*
|
||||
// available gas.
|
||||
stack.data[stack.len()-1] = cg
|
||||
gas.Add(gas, cg)
|
||||
|
||||
case DELEGATECALL:
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
gas.Set(gasTable.Calls)
|
||||
|
||||
x := calcMemSize(stack.data[stack.len()-5], stack.data[stack.len()-6])
|
||||
y := calcMemSize(stack.data[stack.len()-3], stack.data[stack.len()-4])
|
||||
|
||||
newMemSize = common.BigMax(x, y)
|
||||
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
cg := callGas(gasTable, contract.Gas, gas, stack.data[stack.len()-1])
|
||||
// Replace the stack item with the new gas calculation. This means that
|
||||
// either the original item is left on the stack or the item is replaced by:
|
||||
// (availableGas - gas) * 63 / 64
|
||||
// We replace the stack item so that it's available when the opCall instruction is
|
||||
// called.
|
||||
stack.data[stack.len()-1] = cg
|
||||
gas.Add(gas, cg)
|
||||
|
||||
}
|
||||
quadMemGas(mem, newMemSize, gas)
|
||||
|
||||
return newMemSize, gas, nil
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"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/params"
|
||||
)
|
||||
|
||||
// GetHashFn returns a function for which the VM env can query block hashes through
|
||||
@@ -41,11 +42,11 @@ func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
|
||||
}
|
||||
|
||||
type VMEnv struct {
|
||||
chainConfig *ChainConfig // Chain configuration
|
||||
state *state.StateDB // State to use for executing
|
||||
evm *vm.EVM // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
chainConfig *params.ChainConfig // Chain configuration
|
||||
state *state.StateDB // State to use for executing
|
||||
evm *vm.EVM // The Ethereum Virtual Machine
|
||||
depth int // Current execution depth
|
||||
msg Message // Message appliod
|
||||
|
||||
header *types.Header // Header information
|
||||
chain *BlockChain // Blockchain handle
|
||||
@@ -53,7 +54,7 @@ type VMEnv struct {
|
||||
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
|
||||
func NewEnv(state *state.StateDB, chainConfig *params.ChainConfig, chain *BlockChain, msg Message, header *types.Header, cfg vm.Config) *VMEnv {
|
||||
env := &VMEnv{
|
||||
chainConfig: chainConfig,
|
||||
chain: chain,
|
||||
@@ -72,18 +73,18 @@ func NewEnv(state *state.StateDB, chainConfig *ChainConfig, chain *BlockChain, m
|
||||
return env
|
||||
}
|
||||
|
||||
func (self *VMEnv) RuleSet() vm.RuleSet { return self.chainConfig }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
|
||||
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
|
||||
func (self *VMEnv) Time() *big.Int { return self.header.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Origin() common.Address { f, _ := self.msg.From(); return f }
|
||||
func (self *VMEnv) BlockNumber() *big.Int { return self.header.Number }
|
||||
func (self *VMEnv) Coinbase() common.Address { return self.header.Coinbase }
|
||||
func (self *VMEnv) Time() *big.Int { return self.header.Time }
|
||||
func (self *VMEnv) Difficulty() *big.Int { return self.header.Difficulty }
|
||||
func (self *VMEnv) GasLimit() *big.Int { return self.header.GasLimit }
|
||||
func (self *VMEnv) Value() *big.Int { return self.msg.Value() }
|
||||
func (self *VMEnv) Db() vm.Database { return self.state }
|
||||
func (self *VMEnv) Depth() int { return self.depth }
|
||||
func (self *VMEnv) SetDepth(i int) { self.depth = i }
|
||||
func (self *VMEnv) GetHash(n uint64) common.Hash {
|
||||
return self.getHashFn(n)
|
||||
}
|
||||
@@ -95,12 +96,12 @@ func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
|
||||
func (self *VMEnv) MakeSnapshot() vm.Database {
|
||||
return self.state.Copy()
|
||||
func (self *VMEnv) SnapshotDatabase() int {
|
||||
return self.state.Snapshot()
|
||||
}
|
||||
|
||||
func (self *VMEnv) SetSnapshot(copy vm.Database) {
|
||||
self.state.Set(copy.(*state.StateDB))
|
||||
func (self *VMEnv) RevertToSnapshot(snapshot int) {
|
||||
self.state.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64 appengine gccgo
|
||||
|
||||
package sha3
|
||||
|
||||
// rc stores the round constants for use in the ι step.
|
||||
|
||||
13
crypto/sha3/keccakf_amd64.go
Normal file
13
crypto/sha3/keccakf_amd64.go
Normal file
@@ -0,0 +1,13 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64,!appengine,!gccgo
|
||||
|
||||
package sha3
|
||||
|
||||
// This function is implemented in keccakf_amd64.s.
|
||||
|
||||
//go:noescape
|
||||
|
||||
func keccakF1600(state *[25]uint64)
|
||||
392
crypto/sha3/keccakf_amd64.s
Normal file
392
crypto/sha3/keccakf_amd64.s
Normal file
@@ -0,0 +1,392 @@
|
||||
// Copyright 2015 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64,!appengine,!gccgo
|
||||
|
||||
// This code was translated into a form compatible with 6a from the public
|
||||
// domain sources at https://github.com/gvanas/KeccakCodePackage
|
||||
|
||||
// Offsets in state
|
||||
#define _ba (0*8)
|
||||
#define _be (1*8)
|
||||
#define _bi (2*8)
|
||||
#define _bo (3*8)
|
||||
#define _bu (4*8)
|
||||
#define _ga (5*8)
|
||||
#define _ge (6*8)
|
||||
#define _gi (7*8)
|
||||
#define _go (8*8)
|
||||
#define _gu (9*8)
|
||||
#define _ka (10*8)
|
||||
#define _ke (11*8)
|
||||
#define _ki (12*8)
|
||||
#define _ko (13*8)
|
||||
#define _ku (14*8)
|
||||
#define _ma (15*8)
|
||||
#define _me (16*8)
|
||||
#define _mi (17*8)
|
||||
#define _mo (18*8)
|
||||
#define _mu (19*8)
|
||||
#define _sa (20*8)
|
||||
#define _se (21*8)
|
||||
#define _si (22*8)
|
||||
#define _so (23*8)
|
||||
#define _su (24*8)
|
||||
|
||||
// Temporary registers
|
||||
#define rT1 AX
|
||||
|
||||
// Round vars
|
||||
#define rpState DI
|
||||
#define rpStack SP
|
||||
|
||||
#define rDa BX
|
||||
#define rDe CX
|
||||
#define rDi DX
|
||||
#define rDo R8
|
||||
#define rDu R9
|
||||
|
||||
#define rBa R10
|
||||
#define rBe R11
|
||||
#define rBi R12
|
||||
#define rBo R13
|
||||
#define rBu R14
|
||||
|
||||
#define rCa SI
|
||||
#define rCe BP
|
||||
#define rCi rBi
|
||||
#define rCo rBo
|
||||
#define rCu R15
|
||||
|
||||
#define MOVQ_RBI_RCE MOVQ rBi, rCe
|
||||
#define XORQ_RT1_RCA XORQ rT1, rCa
|
||||
#define XORQ_RT1_RCE XORQ rT1, rCe
|
||||
#define XORQ_RBA_RCU XORQ rBa, rCu
|
||||
#define XORQ_RBE_RCU XORQ rBe, rCu
|
||||
#define XORQ_RDU_RCU XORQ rDu, rCu
|
||||
#define XORQ_RDA_RCA XORQ rDa, rCa
|
||||
#define XORQ_RDE_RCE XORQ rDe, rCe
|
||||
|
||||
#define mKeccakRound(iState, oState, rc, B_RBI_RCE, G_RT1_RCA, G_RT1_RCE, G_RBA_RCU, K_RT1_RCA, K_RT1_RCE, K_RBA_RCU, M_RT1_RCA, M_RT1_RCE, M_RBE_RCU, S_RDU_RCU, S_RDA_RCA, S_RDE_RCE) \
|
||||
/* Prepare round */ \
|
||||
MOVQ rCe, rDa; \
|
||||
ROLQ $1, rDa; \
|
||||
\
|
||||
MOVQ _bi(iState), rCi; \
|
||||
XORQ _gi(iState), rDi; \
|
||||
XORQ rCu, rDa; \
|
||||
XORQ _ki(iState), rCi; \
|
||||
XORQ _mi(iState), rDi; \
|
||||
XORQ rDi, rCi; \
|
||||
\
|
||||
MOVQ rCi, rDe; \
|
||||
ROLQ $1, rDe; \
|
||||
\
|
||||
MOVQ _bo(iState), rCo; \
|
||||
XORQ _go(iState), rDo; \
|
||||
XORQ rCa, rDe; \
|
||||
XORQ _ko(iState), rCo; \
|
||||
XORQ _mo(iState), rDo; \
|
||||
XORQ rDo, rCo; \
|
||||
\
|
||||
MOVQ rCo, rDi; \
|
||||
ROLQ $1, rDi; \
|
||||
\
|
||||
MOVQ rCu, rDo; \
|
||||
XORQ rCe, rDi; \
|
||||
ROLQ $1, rDo; \
|
||||
\
|
||||
MOVQ rCa, rDu; \
|
||||
XORQ rCi, rDo; \
|
||||
ROLQ $1, rDu; \
|
||||
\
|
||||
/* Result b */ \
|
||||
MOVQ _ba(iState), rBa; \
|
||||
MOVQ _ge(iState), rBe; \
|
||||
XORQ rCo, rDu; \
|
||||
MOVQ _ki(iState), rBi; \
|
||||
MOVQ _mo(iState), rBo; \
|
||||
MOVQ _su(iState), rBu; \
|
||||
XORQ rDe, rBe; \
|
||||
ROLQ $44, rBe; \
|
||||
XORQ rDi, rBi; \
|
||||
XORQ rDa, rBa; \
|
||||
ROLQ $43, rBi; \
|
||||
\
|
||||
MOVQ rBe, rCa; \
|
||||
MOVQ rc, rT1; \
|
||||
ORQ rBi, rCa; \
|
||||
XORQ rBa, rT1; \
|
||||
XORQ rT1, rCa; \
|
||||
MOVQ rCa, _ba(oState); \
|
||||
\
|
||||
XORQ rDu, rBu; \
|
||||
ROLQ $14, rBu; \
|
||||
MOVQ rBa, rCu; \
|
||||
ANDQ rBe, rCu; \
|
||||
XORQ rBu, rCu; \
|
||||
MOVQ rCu, _bu(oState); \
|
||||
\
|
||||
XORQ rDo, rBo; \
|
||||
ROLQ $21, rBo; \
|
||||
MOVQ rBo, rT1; \
|
||||
ANDQ rBu, rT1; \
|
||||
XORQ rBi, rT1; \
|
||||
MOVQ rT1, _bi(oState); \
|
||||
\
|
||||
NOTQ rBi; \
|
||||
ORQ rBa, rBu; \
|
||||
ORQ rBo, rBi; \
|
||||
XORQ rBo, rBu; \
|
||||
XORQ rBe, rBi; \
|
||||
MOVQ rBu, _bo(oState); \
|
||||
MOVQ rBi, _be(oState); \
|
||||
B_RBI_RCE; \
|
||||
\
|
||||
/* Result g */ \
|
||||
MOVQ _gu(iState), rBe; \
|
||||
XORQ rDu, rBe; \
|
||||
MOVQ _ka(iState), rBi; \
|
||||
ROLQ $20, rBe; \
|
||||
XORQ rDa, rBi; \
|
||||
ROLQ $3, rBi; \
|
||||
MOVQ _bo(iState), rBa; \
|
||||
MOVQ rBe, rT1; \
|
||||
ORQ rBi, rT1; \
|
||||
XORQ rDo, rBa; \
|
||||
MOVQ _me(iState), rBo; \
|
||||
MOVQ _si(iState), rBu; \
|
||||
ROLQ $28, rBa; \
|
||||
XORQ rBa, rT1; \
|
||||
MOVQ rT1, _ga(oState); \
|
||||
G_RT1_RCA; \
|
||||
\
|
||||
XORQ rDe, rBo; \
|
||||
ROLQ $45, rBo; \
|
||||
MOVQ rBi, rT1; \
|
||||
ANDQ rBo, rT1; \
|
||||
XORQ rBe, rT1; \
|
||||
MOVQ rT1, _ge(oState); \
|
||||
G_RT1_RCE; \
|
||||
\
|
||||
XORQ rDi, rBu; \
|
||||
ROLQ $61, rBu; \
|
||||
MOVQ rBu, rT1; \
|
||||
ORQ rBa, rT1; \
|
||||
XORQ rBo, rT1; \
|
||||
MOVQ rT1, _go(oState); \
|
||||
\
|
||||
ANDQ rBe, rBa; \
|
||||
XORQ rBu, rBa; \
|
||||
MOVQ rBa, _gu(oState); \
|
||||
NOTQ rBu; \
|
||||
G_RBA_RCU; \
|
||||
\
|
||||
ORQ rBu, rBo; \
|
||||
XORQ rBi, rBo; \
|
||||
MOVQ rBo, _gi(oState); \
|
||||
\
|
||||
/* Result k */ \
|
||||
MOVQ _be(iState), rBa; \
|
||||
MOVQ _gi(iState), rBe; \
|
||||
MOVQ _ko(iState), rBi; \
|
||||
MOVQ _mu(iState), rBo; \
|
||||
MOVQ _sa(iState), rBu; \
|
||||
XORQ rDi, rBe; \
|
||||
ROLQ $6, rBe; \
|
||||
XORQ rDo, rBi; \
|
||||
ROLQ $25, rBi; \
|
||||
MOVQ rBe, rT1; \
|
||||
ORQ rBi, rT1; \
|
||||
XORQ rDe, rBa; \
|
||||
ROLQ $1, rBa; \
|
||||
XORQ rBa, rT1; \
|
||||
MOVQ rT1, _ka(oState); \
|
||||
K_RT1_RCA; \
|
||||
\
|
||||
XORQ rDu, rBo; \
|
||||
ROLQ $8, rBo; \
|
||||
MOVQ rBi, rT1; \
|
||||
ANDQ rBo, rT1; \
|
||||
XORQ rBe, rT1; \
|
||||
MOVQ rT1, _ke(oState); \
|
||||
K_RT1_RCE; \
|
||||
\
|
||||
XORQ rDa, rBu; \
|
||||
ROLQ $18, rBu; \
|
||||
NOTQ rBo; \
|
||||
MOVQ rBo, rT1; \
|
||||
ANDQ rBu, rT1; \
|
||||
XORQ rBi, rT1; \
|
||||
MOVQ rT1, _ki(oState); \
|
||||
\
|
||||
MOVQ rBu, rT1; \
|
||||
ORQ rBa, rT1; \
|
||||
XORQ rBo, rT1; \
|
||||
MOVQ rT1, _ko(oState); \
|
||||
\
|
||||
ANDQ rBe, rBa; \
|
||||
XORQ rBu, rBa; \
|
||||
MOVQ rBa, _ku(oState); \
|
||||
K_RBA_RCU; \
|
||||
\
|
||||
/* Result m */ \
|
||||
MOVQ _ga(iState), rBe; \
|
||||
XORQ rDa, rBe; \
|
||||
MOVQ _ke(iState), rBi; \
|
||||
ROLQ $36, rBe; \
|
||||
XORQ rDe, rBi; \
|
||||
MOVQ _bu(iState), rBa; \
|
||||
ROLQ $10, rBi; \
|
||||
MOVQ rBe, rT1; \
|
||||
MOVQ _mi(iState), rBo; \
|
||||
ANDQ rBi, rT1; \
|
||||
XORQ rDu, rBa; \
|
||||
MOVQ _so(iState), rBu; \
|
||||
ROLQ $27, rBa; \
|
||||
XORQ rBa, rT1; \
|
||||
MOVQ rT1, _ma(oState); \
|
||||
M_RT1_RCA; \
|
||||
\
|
||||
XORQ rDi, rBo; \
|
||||
ROLQ $15, rBo; \
|
||||
MOVQ rBi, rT1; \
|
||||
ORQ rBo, rT1; \
|
||||
XORQ rBe, rT1; \
|
||||
MOVQ rT1, _me(oState); \
|
||||
M_RT1_RCE; \
|
||||
\
|
||||
XORQ rDo, rBu; \
|
||||
ROLQ $56, rBu; \
|
||||
NOTQ rBo; \
|
||||
MOVQ rBo, rT1; \
|
||||
ORQ rBu, rT1; \
|
||||
XORQ rBi, rT1; \
|
||||
MOVQ rT1, _mi(oState); \
|
||||
\
|
||||
ORQ rBa, rBe; \
|
||||
XORQ rBu, rBe; \
|
||||
MOVQ rBe, _mu(oState); \
|
||||
\
|
||||
ANDQ rBa, rBu; \
|
||||
XORQ rBo, rBu; \
|
||||
MOVQ rBu, _mo(oState); \
|
||||
M_RBE_RCU; \
|
||||
\
|
||||
/* Result s */ \
|
||||
MOVQ _bi(iState), rBa; \
|
||||
MOVQ _go(iState), rBe; \
|
||||
MOVQ _ku(iState), rBi; \
|
||||
XORQ rDi, rBa; \
|
||||
MOVQ _ma(iState), rBo; \
|
||||
ROLQ $62, rBa; \
|
||||
XORQ rDo, rBe; \
|
||||
MOVQ _se(iState), rBu; \
|
||||
ROLQ $55, rBe; \
|
||||
\
|
||||
XORQ rDu, rBi; \
|
||||
MOVQ rBa, rDu; \
|
||||
XORQ rDe, rBu; \
|
||||
ROLQ $2, rBu; \
|
||||
ANDQ rBe, rDu; \
|
||||
XORQ rBu, rDu; \
|
||||
MOVQ rDu, _su(oState); \
|
||||
\
|
||||
ROLQ $39, rBi; \
|
||||
S_RDU_RCU; \
|
||||
NOTQ rBe; \
|
||||
XORQ rDa, rBo; \
|
||||
MOVQ rBe, rDa; \
|
||||
ANDQ rBi, rDa; \
|
||||
XORQ rBa, rDa; \
|
||||
MOVQ rDa, _sa(oState); \
|
||||
S_RDA_RCA; \
|
||||
\
|
||||
ROLQ $41, rBo; \
|
||||
MOVQ rBi, rDe; \
|
||||
ORQ rBo, rDe; \
|
||||
XORQ rBe, rDe; \
|
||||
MOVQ rDe, _se(oState); \
|
||||
S_RDE_RCE; \
|
||||
\
|
||||
MOVQ rBo, rDi; \
|
||||
MOVQ rBu, rDo; \
|
||||
ANDQ rBu, rDi; \
|
||||
ORQ rBa, rDo; \
|
||||
XORQ rBi, rDi; \
|
||||
XORQ rBo, rDo; \
|
||||
MOVQ rDi, _si(oState); \
|
||||
MOVQ rDo, _so(oState) \
|
||||
|
||||
// func keccakF1600(state *[25]uint64)
|
||||
TEXT ·keccakF1600(SB), 0, $200-8
|
||||
MOVQ state+0(FP), rpState
|
||||
SUBQ $(8*25), SP
|
||||
|
||||
// Convert the user state into an internal state
|
||||
NOTQ _be(rpState)
|
||||
NOTQ _bi(rpState)
|
||||
NOTQ _go(rpState)
|
||||
NOTQ _ki(rpState)
|
||||
NOTQ _mi(rpState)
|
||||
NOTQ _sa(rpState)
|
||||
|
||||
// Execute the KeccakF permutation
|
||||
MOVQ _ba(rpState), rCa
|
||||
MOVQ _be(rpState), rCe
|
||||
MOVQ _bu(rpState), rCu
|
||||
|
||||
XORQ _ga(rpState), rCa
|
||||
XORQ _ge(rpState), rCe
|
||||
XORQ _gu(rpState), rCu
|
||||
|
||||
XORQ _ka(rpState), rCa
|
||||
XORQ _ke(rpState), rCe
|
||||
XORQ _ku(rpState), rCu
|
||||
|
||||
XORQ _ma(rpState), rCa
|
||||
XORQ _me(rpState), rCe
|
||||
XORQ _mu(rpState), rCu
|
||||
|
||||
XORQ _sa(rpState), rCa
|
||||
XORQ _se(rpState), rCe
|
||||
MOVQ _si(rpState), rDi
|
||||
MOVQ _so(rpState), rDo
|
||||
XORQ _su(rpState), rCu
|
||||
|
||||
mKeccakRound(rpState, rpStack, $0x0000000000000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x0000000000008082, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x800000000000808a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000080008000, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x000000000000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000000008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x000000000000008a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x0000000000000088, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x0000000080008009, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x000000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x000000008000808b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x800000000000008b, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x8000000000008089, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000000008003, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x8000000000008002, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000000000080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x000000000000800a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x800000008000000a, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x8000000080008081, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000000008080, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpState, rpStack, $0x0000000080000001, MOVQ_RBI_RCE, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBA_RCU, XORQ_RT1_RCA, XORQ_RT1_RCE, XORQ_RBE_RCU, XORQ_RDU_RCU, XORQ_RDA_RCA, XORQ_RDE_RCE)
|
||||
mKeccakRound(rpStack, rpState, $0x8000000080008008, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP)
|
||||
|
||||
// Revert the internal state to the user state
|
||||
NOTQ _be(rpState)
|
||||
NOTQ _bi(rpState)
|
||||
NOTQ _go(rpState)
|
||||
NOTQ _ki(rpState)
|
||||
NOTQ _mi(rpState)
|
||||
NOTQ _sa(rpState)
|
||||
|
||||
ADDQ $(8*25), SP
|
||||
RET
|
||||
@@ -42,7 +42,7 @@ type state struct {
|
||||
storage [maxRate]byte
|
||||
|
||||
// Specific to SHA-3 and SHAKE.
|
||||
fixedOutput bool // whether this is a fixed-ouput-length instance
|
||||
fixedOutput bool // whether this is a fixed-output-length instance
|
||||
outputLen int // the default output size in bytes
|
||||
state spongeDirection // whether the sponge is absorbing or squeezing
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !amd64,!386 appengine
|
||||
// +build !amd64,!386,!ppc64le appengine
|
||||
|
||||
package sha3
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build amd64 386
|
||||
// +build amd64 386 ppc64le
|
||||
// +build !appengine
|
||||
|
||||
package sha3
|
||||
|
||||
118
eth/api.go
118
eth/api.go
@@ -46,6 +46,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
"github.com/syndtr/goleveldb/leveldb"
|
||||
@@ -324,22 +325,18 @@ func (s *PublicTxPoolAPI) Content() map[string]map[string]map[string][]*RPCTrans
|
||||
// Flatten the pending transactions
|
||||
for account, batches := range pending {
|
||||
dump := make(map[string][]*RPCTransaction)
|
||||
for nonce, txs := range batches {
|
||||
nonce := fmt.Sprintf("%d", nonce)
|
||||
for _, tx := range txs {
|
||||
dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
|
||||
}
|
||||
for _, tx := range batches {
|
||||
nonce := fmt.Sprintf("%d", tx.Nonce())
|
||||
dump[nonce] = []*RPCTransaction{newRPCPendingTransaction(tx)}
|
||||
}
|
||||
content["pending"][account.Hex()] = dump
|
||||
}
|
||||
// Flatten the queued transactions
|
||||
for account, batches := range queue {
|
||||
dump := make(map[string][]*RPCTransaction)
|
||||
for nonce, txs := range batches {
|
||||
nonce := fmt.Sprintf("%d", nonce)
|
||||
for _, tx := range txs {
|
||||
dump[nonce] = append(dump[nonce], newRPCPendingTransaction(tx))
|
||||
}
|
||||
for _, tx := range batches {
|
||||
nonce := fmt.Sprintf("%d", tx.Nonce())
|
||||
dump[nonce] = []*RPCTransaction{newRPCPendingTransaction(tx)}
|
||||
}
|
||||
content["queued"][account.Hex()] = dump
|
||||
}
|
||||
@@ -374,22 +371,18 @@ func (s *PublicTxPoolAPI) Inspect() map[string]map[string]map[string][]string {
|
||||
// Flatten the pending transactions
|
||||
for account, batches := range pending {
|
||||
dump := make(map[string][]string)
|
||||
for nonce, txs := range batches {
|
||||
nonce := fmt.Sprintf("%d", nonce)
|
||||
for _, tx := range txs {
|
||||
dump[nonce] = append(dump[nonce], format(tx))
|
||||
}
|
||||
for _, tx := range batches {
|
||||
nonce := fmt.Sprintf("%d", tx.Nonce())
|
||||
dump[nonce] = []string{format(tx)}
|
||||
}
|
||||
content["pending"][account.Hex()] = dump
|
||||
}
|
||||
// Flatten the queued transactions
|
||||
for account, batches := range queue {
|
||||
dump := make(map[string][]string)
|
||||
for nonce, txs := range batches {
|
||||
nonce := fmt.Sprintf("%d", nonce)
|
||||
for _, tx := range txs {
|
||||
dump[nonce] = append(dump[nonce], format(tx))
|
||||
}
|
||||
for _, tx := range batches {
|
||||
nonce := fmt.Sprintf("%d", tx.Nonce())
|
||||
dump[nonce] = []string{format(tx)}
|
||||
}
|
||||
content["queued"][account.Hex()] = dump
|
||||
}
|
||||
@@ -416,6 +409,7 @@ func (s *PublicAccountAPI) Accounts() []accounts.Account {
|
||||
// It offers methods to create, (un)lock en list accounts. Some methods accept
|
||||
// passwords and are therefore considered private by default.
|
||||
type PrivateAccountAPI struct {
|
||||
bc *core.BlockChain
|
||||
am *accounts.Manager
|
||||
txPool *core.TxPool
|
||||
txMu *sync.Mutex
|
||||
@@ -425,6 +419,7 @@ type PrivateAccountAPI struct {
|
||||
// NewPrivateAccountAPI create a new PrivateAccountAPI.
|
||||
func NewPrivateAccountAPI(e *Ethereum) *PrivateAccountAPI {
|
||||
return &PrivateAccountAPI{
|
||||
bc: e.blockchain,
|
||||
am: e.accountManager,
|
||||
txPool: e.txPool,
|
||||
txMu: &e.txMu,
|
||||
@@ -503,6 +498,10 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd strin
|
||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||
}
|
||||
|
||||
if s.bc.Config().IsEIP155(s.bc.CurrentBlock().Number()) {
|
||||
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
|
||||
}
|
||||
|
||||
signature, err := s.am.SignWithPassphrase(args.From, passwd, tx.SigHash().Bytes())
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
@@ -514,7 +513,7 @@ func (s *PrivateAccountAPI) SignAndSendTransaction(args SendTxArgs, passwd strin
|
||||
// PublicBlockChainAPI provides an API to access the Ethereum blockchain.
|
||||
// It offers only methods that operate on public data that is freely available to anyone.
|
||||
type PublicBlockChainAPI struct {
|
||||
config *core.ChainConfig
|
||||
config *params.ChainConfig
|
||||
bc *core.BlockChain
|
||||
chainDb ethdb.Database
|
||||
eventMux *event.TypeMux
|
||||
@@ -526,7 +525,7 @@ type PublicBlockChainAPI struct {
|
||||
}
|
||||
|
||||
// NewPublicBlockChainAPI creates a new Etheruem blockchain API.
|
||||
func NewPublicBlockChainAPI(config *core.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
|
||||
func NewPublicBlockChainAPI(config *params.ChainConfig, bc *core.BlockChain, m *miner.Miner, chainDb ethdb.Database, gpo *GasPriceOracle, eventMux *event.TypeMux, am *accounts.Manager) *PublicBlockChainAPI {
|
||||
api := &PublicBlockChainAPI{
|
||||
config: config,
|
||||
bc: bc,
|
||||
@@ -777,7 +776,7 @@ func (s *PublicBlockChainAPI) doCall(args CallArgs, blockNr rpc.BlockNumber) (st
|
||||
}
|
||||
|
||||
// Execute the call and return
|
||||
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), s.config.VmConfig)
|
||||
vmenv := core.NewEnv(stateDb, s.config, s.bc, msg, block.Header(), vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
|
||||
res, requiredGas, _, err := core.NewStateTransition(vmenv, msg, gp).TransitionDb()
|
||||
@@ -831,6 +830,9 @@ func (s *PublicBlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx
|
||||
|
||||
if fullTx {
|
||||
formatTx = func(tx *types.Transaction) (interface{}, error) {
|
||||
if tx.Protected() {
|
||||
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
|
||||
}
|
||||
return newRPCTransaction(b, tx.Hash())
|
||||
}
|
||||
}
|
||||
@@ -987,7 +989,7 @@ func getTransaction(chainDb ethdb.Database, txPool *core.TxPool, txHash common.H
|
||||
}
|
||||
} else {
|
||||
// pending transaction?
|
||||
tx = txPool.GetTransaction(txHash)
|
||||
tx = txPool.Get(txHash)
|
||||
isPending = true
|
||||
}
|
||||
|
||||
@@ -1215,6 +1217,10 @@ func (s *PublicTransactionPoolAPI) SendTransaction(args SendTxArgs) (common.Hash
|
||||
tx = types.NewTransaction(args.Nonce.Uint64(), *args.To, args.Value.BigInt(), args.Gas.BigInt(), args.GasPrice.BigInt(), common.FromHex(args.Data))
|
||||
}
|
||||
|
||||
if s.bc.Config().IsEIP155(s.bc.CurrentBlock().Number()) {
|
||||
tx.SetSigner(types.NewEIP155Signer(s.bc.Config().ChainId))
|
||||
}
|
||||
|
||||
signature, err := s.am.Sign(args.From, tx.SigHash().Bytes())
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
@@ -1399,12 +1405,13 @@ func (s *PublicTransactionPoolAPI) SignTransaction(args SignTransactionArgs) (*S
|
||||
// PendingTransactions returns the transactions that are in the transaction pool and have a from address that is one of
|
||||
// the accounts this node manages.
|
||||
func (s *PublicTransactionPoolAPI) PendingTransactions() []*RPCTransaction {
|
||||
pending := s.txPool.GetTransactions()
|
||||
pending := s.txPool.Pending()
|
||||
transactions := make([]*RPCTransaction, 0, len(pending))
|
||||
for _, tx := range pending {
|
||||
from, _ := tx.FromFrontier()
|
||||
if s.am.HasAddress(from) {
|
||||
transactions = append(transactions, newRPCPendingTransaction(tx))
|
||||
for addr, txs := range pending {
|
||||
if s.am.HasAddress(addr) {
|
||||
for _, tx := range txs {
|
||||
transactions = append(transactions, newRPCPendingTransaction(tx))
|
||||
}
|
||||
}
|
||||
}
|
||||
return transactions
|
||||
@@ -1438,35 +1445,36 @@ func (s *PublicTransactionPoolAPI) NewPendingTransactions(ctx context.Context) (
|
||||
// Resend accepts an existing transaction and a new gas price and limit. It will remove the given transaction from the
|
||||
// pool and reinsert it with the new gas price and limit.
|
||||
func (s *PublicTransactionPoolAPI) Resend(tx Tx, gasPrice, gasLimit *rpc.HexNumber) (common.Hash, error) {
|
||||
pending := s.txPool.Pending()
|
||||
for addr, txs := range pending {
|
||||
for _, p := range txs {
|
||||
if addr == tx.From && p.SigHash() == tx.tx.SigHash() {
|
||||
if gasPrice == nil {
|
||||
gasPrice = rpc.NewHexNumber(tx.tx.GasPrice())
|
||||
}
|
||||
if gasLimit == nil {
|
||||
gasLimit = rpc.NewHexNumber(tx.tx.Gas())
|
||||
}
|
||||
|
||||
pending := s.txPool.GetTransactions()
|
||||
for _, p := range pending {
|
||||
if pFrom, err := p.FromFrontier(); err == nil && pFrom == tx.From && p.SigHash() == tx.tx.SigHash() {
|
||||
if gasPrice == nil {
|
||||
gasPrice = rpc.NewHexNumber(tx.tx.GasPrice())
|
||||
}
|
||||
if gasLimit == nil {
|
||||
gasLimit = rpc.NewHexNumber(tx.tx.Gas())
|
||||
}
|
||||
var newTx *types.Transaction
|
||||
if tx.tx.To() == nil {
|
||||
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||
} else {
|
||||
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||
}
|
||||
|
||||
var newTx *types.Transaction
|
||||
if tx.tx.To() == nil {
|
||||
newTx = types.NewContractCreation(tx.tx.Nonce(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||
} else {
|
||||
newTx = types.NewTransaction(tx.tx.Nonce(), *tx.tx.To(), tx.tx.Value(), gasPrice.BigInt(), gasLimit.BigInt(), tx.tx.Data())
|
||||
}
|
||||
signedTx, err := s.sign(tx.From, newTx)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
signedTx, err := s.sign(tx.From, newTx)
|
||||
if err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
s.txPool.Remove(tx.Hash)
|
||||
if err = s.txPool.Add(signedTx); err != nil {
|
||||
return common.Hash{}, err
|
||||
}
|
||||
|
||||
s.txPool.RemoveTx(tx.Hash)
|
||||
if err = s.txPool.Add(signedTx); err != nil {
|
||||
return common.Hash{}, err
|
||||
return signedTx.Hash(), nil
|
||||
}
|
||||
|
||||
return signedTx.Hash(), nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1625,13 +1633,13 @@ func (api *PublicDebugAPI) SeedHash(number uint64) (string, error) {
|
||||
// PrivateDebugAPI is the collection of Etheruem APIs exposed over the private
|
||||
// debugging endpoint.
|
||||
type PrivateDebugAPI struct {
|
||||
config *core.ChainConfig
|
||||
config *params.ChainConfig
|
||||
eth *Ethereum
|
||||
}
|
||||
|
||||
// NewPrivateDebugAPI creates a new API definition for the private debug methods
|
||||
// of the Ethereum service.
|
||||
func NewPrivateDebugAPI(config *core.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
|
||||
func NewPrivateDebugAPI(config *params.ChainConfig, eth *Ethereum) *PrivateDebugAPI {
|
||||
return &PrivateDebugAPI{config: config, eth: eth}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/common/registrar/ethreg"
|
||||
"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/eth/downloader"
|
||||
"github.com/ethereum/go-ethereum/eth/filters"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
@@ -47,6 +46,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/miner"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
@@ -65,7 +65,7 @@ var (
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
ChainConfig *core.ChainConfig // chain configuration
|
||||
ChainConfig *params.ChainConfig // chain configuration
|
||||
|
||||
NetworkId int // Network ID to use for selecting peers to connect to
|
||||
Genesis string // Genesis JSON to seed the chain database with
|
||||
@@ -104,14 +104,13 @@ type Config struct {
|
||||
}
|
||||
|
||||
type Ethereum struct {
|
||||
chainConfig *core.ChainConfig
|
||||
chainConfig *params.ChainConfig
|
||||
// Channel for shutting down the ethereum
|
||||
shutdownChan chan bool
|
||||
|
||||
// DB interfaces
|
||||
chainDb ethdb.Database // Block chain database
|
||||
dappDb ethdb.Database // Dapp database
|
||||
|
||||
// Handlers
|
||||
txPool *core.TxPool
|
||||
txMu sync.Mutex
|
||||
@@ -253,10 +252,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
core.WriteChainConfig(chainDb, genesis.Hash(), config.ChainConfig)
|
||||
|
||||
eth.chainConfig = config.ChainConfig
|
||||
eth.chainConfig.VmConfig = vm.Config{
|
||||
EnableJit: config.EnableJit,
|
||||
ForceJit: config.ForceJit,
|
||||
}
|
||||
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
|
||||
if err != nil {
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
func TestMipmapUpgrade(t *testing.T) {
|
||||
@@ -32,7 +33,7 @@ func TestMipmapUpgrade(t *testing.T) {
|
||||
addr := common.BytesToAddress([]byte("jeff"))
|
||||
genesis := core.WriteGenesisBlockForTesting(db)
|
||||
|
||||
chain, receipts := core.GenerateChain(nil, genesis, db, 10, func(i int, gen *core.BlockGen) {
|
||||
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {
|
||||
var receipts types.Receipts
|
||||
switch i {
|
||||
case 1:
|
||||
|
||||
@@ -55,7 +55,7 @@ func init() {
|
||||
// reassembly.
|
||||
func makeChain(n int, seed byte, parent *types.Block, parentReceipts types.Receipts, heavy bool) ([]common.Hash, map[common.Hash]*types.Header, map[common.Hash]*types.Block, map[common.Hash]types.Receipts) {
|
||||
// Generate the block chain
|
||||
blocks, receipts := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||
blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||
block.SetCoinbase(common.Address{seed})
|
||||
|
||||
// If a heavy chain is requested, delay blocks to raise difficulty
|
||||
@@ -286,7 +286,7 @@ func (dl *downloadTester) headFastBlock() *types.Block {
|
||||
func (dl *downloadTester) commitHeadBlock(hash common.Hash) error {
|
||||
// For now only check that the state trie is correct
|
||||
if block := dl.getBlock(hash); block != nil {
|
||||
_, err := trie.NewSecure(block.Root(), dl.stateDb)
|
||||
_, err := trie.NewSecure(block.Root(), dl.stateDb, 0)
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("non existent block: %x", hash[:4])
|
||||
|
||||
@@ -45,7 +45,7 @@ var (
|
||||
// contains a transaction and every 5th an uncle to allow testing correct block
|
||||
// reassembly.
|
||||
func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
|
||||
blocks, _ := core.GenerateChain(nil, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||
blocks, _ := core.GenerateChain(params.TestChainConfig, parent, testdb, n, func(i int, block *core.BlockGen) {
|
||||
block.SetCoinbase(common.Address{seed})
|
||||
|
||||
// If the block number is multiple of 3, send a bonus transaction to the miner
|
||||
|
||||
@@ -28,6 +28,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
|
||||
func makeReceipt(addr common.Address) *types.Receipt {
|
||||
@@ -57,7 +58,7 @@ func BenchmarkMipmaps(b *testing.B) {
|
||||
defer db.Close()
|
||||
|
||||
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr1, Balance: big.NewInt(1000000)})
|
||||
chain, receipts := core.GenerateChain(nil, genesis, db, 100010, func(i int, gen *core.BlockGen) {
|
||||
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 100010, func(i int, gen *core.BlockGen) {
|
||||
var receipts types.Receipts
|
||||
switch i {
|
||||
case 2403:
|
||||
@@ -133,7 +134,7 @@ func TestFilters(t *testing.T) {
|
||||
defer db.Close()
|
||||
|
||||
genesis := core.WriteGenesisBlockForTesting(db, core.GenesisAccount{Address: addr, Balance: big.NewInt(1000000)})
|
||||
chain, receipts := core.GenerateChain(nil, genesis, db, 1000, func(i int, gen *core.BlockGen) {
|
||||
chain, receipts := core.GenerateChain(params.TestChainConfig, genesis, db, 1000, func(i int, gen *core.BlockGen) {
|
||||
var receipts types.Receipts
|
||||
switch i {
|
||||
case 1:
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -36,6 +37,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
@@ -66,7 +68,7 @@ type ProtocolManager struct {
|
||||
txpool txPool
|
||||
blockchain *core.BlockChain
|
||||
chaindb ethdb.Database
|
||||
chainconfig *core.ChainConfig
|
||||
chainconfig *params.ChainConfig
|
||||
|
||||
downloader *downloader.Downloader
|
||||
fetcher *fetcher.Fetcher
|
||||
@@ -93,7 +95,7 @@ type ProtocolManager struct {
|
||||
|
||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||
// with the ethereum network.
|
||||
func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||
func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int, mux *event.TypeMux, txpool txPool, pow pow.PoW, blockchain *core.BlockChain, chaindb ethdb.Database) (*ProtocolManager, error) {
|
||||
// Create the protocol manager with the base fields
|
||||
manager := &ProtocolManager{
|
||||
networkId: networkId,
|
||||
@@ -287,7 +289,7 @@ func (pm *ProtocolManager) handle(p *peer) error {
|
||||
}
|
||||
// Start a timer to disconnect if the peer doesn't reply in time
|
||||
p.forkDrop = time.AfterFunc(daoChallengeTimeout, func() {
|
||||
glog.V(logger.Warn).Infof("%v: timed out DAO fork-check, dropping", p)
|
||||
glog.V(logger.Debug).Infof("%v: timed out DAO fork-check, dropping", p)
|
||||
pm.removePeer(p.id)
|
||||
})
|
||||
// Make sure it's cleaned up if the peer dies off
|
||||
@@ -369,14 +371,24 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
}
|
||||
case query.Origin.Hash != (common.Hash{}) && !query.Reverse:
|
||||
// Hash based traversal towards the leaf block
|
||||
if header := pm.blockchain.GetHeaderByNumber(origin.Number.Uint64() + query.Skip + 1); header != nil {
|
||||
if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
|
||||
query.Origin.Hash = header.Hash()
|
||||
var (
|
||||
current = origin.Number.Uint64()
|
||||
next = current + query.Skip + 1
|
||||
)
|
||||
if next <= current {
|
||||
infos, _ := json.MarshalIndent(p.Peer.Info(), "", " ")
|
||||
glog.V(logger.Warn).Infof("%v: GetBlockHeaders skip overflow attack (current %v, skip %v, next %v)\nMalicious peer infos: %s", p, current, query.Skip, next, infos)
|
||||
unknown = true
|
||||
} else {
|
||||
if header := pm.blockchain.GetHeaderByNumber(next); header != nil {
|
||||
if pm.blockchain.GetBlockHashesFromHash(header.Hash(), query.Skip+1)[query.Skip] == query.Origin.Hash {
|
||||
query.Origin.Hash = header.Hash()
|
||||
} else {
|
||||
unknown = true
|
||||
}
|
||||
} else {
|
||||
unknown = true
|
||||
}
|
||||
} else {
|
||||
unknown = true
|
||||
}
|
||||
case query.Reverse:
|
||||
// Number based traversal towards the genesis block
|
||||
@@ -678,7 +690,7 @@ func (pm *ProtocolManager) handleMsg(p *peer) error {
|
||||
}
|
||||
p.MarkTransaction(tx.Hash())
|
||||
}
|
||||
pm.txpool.AddTransactions(txs)
|
||||
pm.txpool.AddBatch(txs)
|
||||
|
||||
default:
|
||||
return errResp(ErrInvalidMsgCode, "%v", msg.Code)
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package eth
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"testing"
|
||||
@@ -173,6 +174,20 @@ func testGetBlockHeaders(t *testing.T, protocol int) {
|
||||
pm.blockchain.GetBlockByNumber(0).Hash(),
|
||||
},
|
||||
},
|
||||
// Check a corner case where skipping overflow loops back into the chain start
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(3).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64 - 1},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(3).Hash(),
|
||||
},
|
||||
},
|
||||
// Check a corner case where skipping overflow loops back to the same header
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Hash: pm.blockchain.GetBlockByNumber(1).Hash()}, Amount: 2, Reverse: false, Skip: math.MaxUint64},
|
||||
[]common.Hash{
|
||||
pm.blockchain.GetBlockByNumber(1).Hash(),
|
||||
},
|
||||
},
|
||||
// Check that non existing headers aren't returned
|
||||
{
|
||||
&getBlockHeadersData{Origin: hashOrNumber{Hash: unknown}, Amount: 1},
|
||||
@@ -451,7 +466,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
||||
pow = new(core.FakePow)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
genesis = core.WriteGenesisBlockForTesting(db)
|
||||
config = &core.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
||||
config = ¶ms.ChainConfig{DAOForkBlock: big.NewInt(1), DAOForkSupport: localForked}
|
||||
blockchain, _ = core.NewBlockChain(db, config, pow, evmux)
|
||||
)
|
||||
pm, err := NewProtocolManager(config, false, NetworkId, evmux, new(testTxPool), pow, blockchain, db)
|
||||
@@ -476,7 +491,7 @@ func testDAOChallenge(t *testing.T, localForked, remoteForked bool, timeout bool
|
||||
}
|
||||
// Create a block to reply to the challenge if no timeout is simualted
|
||||
if !timeout {
|
||||
blocks, _ := core.GenerateChain(nil, genesis, db, 1, func(i int, block *core.BlockGen) {
|
||||
blocks, _ := core.GenerateChain(¶ms.ChainConfig{}, genesis, db, 1, func(i int, block *core.BlockGen) {
|
||||
if remoteForked {
|
||||
block.SetExtra(params.DAOForkBlockExtra)
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"crypto/ecdsa"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"sort"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
@@ -34,6 +35,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/p2p"
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -53,10 +55,10 @@ func newTestProtocolManager(fastSync bool, blocks int, generator func(int, *core
|
||||
pow = new(core.FakePow)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
genesis = core.WriteGenesisBlockForTesting(db, testBank)
|
||||
chainConfig = &core.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker
|
||||
chainConfig = ¶ms.ChainConfig{HomesteadBlock: big.NewInt(0)} // homestead set to 0 because of chain maker
|
||||
blockchain, _ = core.NewBlockChain(db, chainConfig, pow, evmux)
|
||||
)
|
||||
chain, _ := core.GenerateChain(nil, genesis, db, blocks, generator)
|
||||
chain, _ := core.GenerateChain(chainConfig, genesis, db, blocks, generator)
|
||||
if _, err := blockchain.InsertChain(chain); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -89,9 +91,9 @@ type testTxPool struct {
|
||||
lock sync.RWMutex // Protects the transaction pool
|
||||
}
|
||||
|
||||
// AddTransactions appends a batch of transactions to the pool, and notifies any
|
||||
// AddBatch appends a batch of transactions to the pool, and notifies any
|
||||
// listeners if the addition channel is non nil
|
||||
func (p *testTxPool) AddTransactions(txs []*types.Transaction) {
|
||||
func (p *testTxPool) AddBatch(txs []*types.Transaction) {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
@@ -101,15 +103,20 @@ func (p *testTxPool) AddTransactions(txs []*types.Transaction) {
|
||||
}
|
||||
}
|
||||
|
||||
// GetTransactions returns all the transactions known to the pool
|
||||
func (p *testTxPool) GetTransactions() types.Transactions {
|
||||
// Pending returns all the transactions known to the pool
|
||||
func (p *testTxPool) Pending() map[common.Address]types.Transactions {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
txs := make([]*types.Transaction, len(p.pool))
|
||||
copy(txs, p.pool)
|
||||
|
||||
return txs
|
||||
batches := make(map[common.Address]types.Transactions)
|
||||
for _, tx := range p.pool {
|
||||
from, _ := tx.From()
|
||||
batches[from] = append(batches[from], tx)
|
||||
}
|
||||
for _, batch := range batches {
|
||||
sort.Sort(types.TxByNonce(batch))
|
||||
}
|
||||
return batches
|
||||
}
|
||||
|
||||
// newTestTransaction create a new dummy transaction.
|
||||
|
||||
@@ -97,12 +97,12 @@ var errorToString = map[int]string{
|
||||
}
|
||||
|
||||
type txPool interface {
|
||||
// AddTransactions should add the given transactions to the pool.
|
||||
AddTransactions([]*types.Transaction)
|
||||
// AddBatch should add the given transactions to the pool.
|
||||
AddBatch([]*types.Transaction)
|
||||
|
||||
// GetTransactions should return pending transactions.
|
||||
// Pending should return pending transactions.
|
||||
// The slice should be modifiable by the caller.
|
||||
GetTransactions() types.Transactions
|
||||
Pending() map[common.Address]types.Transactions
|
||||
}
|
||||
|
||||
// statusData is the network packet for the status message.
|
||||
|
||||
@@ -130,7 +130,7 @@ func testSendTransactions(t *testing.T, protocol int) {
|
||||
for nonce := range alltxs {
|
||||
alltxs[nonce] = newTestTransaction(testAccount, uint64(nonce), txsize)
|
||||
}
|
||||
pm.txpool.AddTransactions(alltxs)
|
||||
pm.txpool.AddBatch(alltxs)
|
||||
|
||||
// Connect several peers. They should all receive the pending transactions.
|
||||
var wg sync.WaitGroup
|
||||
|
||||
@@ -45,7 +45,10 @@ type txsync struct {
|
||||
|
||||
// syncTransactions starts sending all currently pending transactions to the given peer.
|
||||
func (pm *ProtocolManager) syncTransactions(p *peer) {
|
||||
txs := pm.txpool.GetTransactions()
|
||||
var txs types.Transactions
|
||||
for _, batch := range pm.txpool.Pending() {
|
||||
txs = append(txs, batch...)
|
||||
}
|
||||
if len(txs) == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -102,6 +102,11 @@ func NewLDBDatabase(file string, cache int, handles int) (*LDBDatabase, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Path returns the path to the database directory.
|
||||
func (db *LDBDatabase) Path() string {
|
||||
return db.fn
|
||||
}
|
||||
|
||||
// Put puts the given key / value to the queue
|
||||
func (self *LDBDatabase) Put(key []byte, value []byte) error {
|
||||
// Measure the database put latency, if requested
|
||||
|
||||
116
internal/build/env.go
Normal file
116
internal/build/env.go
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2016 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 build
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
// These flags override values in build env.
|
||||
GitCommitFlag = flag.String("git-commit", "", `Overrides git commit hash embedded into executables`)
|
||||
GitBranchFlag = flag.String("git-branch", "", `Overrides git branch being built`)
|
||||
GitTagFlag = flag.String("git-tag", "", `Overrides git tag being built`)
|
||||
BuildnumFlag = flag.String("buildnum", "", `Overrides CI build number`)
|
||||
PullRequestFlag = flag.Bool("pull-request", false, `Overrides pull request status of the build`)
|
||||
)
|
||||
|
||||
// Environment contains metadata provided by the build environment.
|
||||
type Environment struct {
|
||||
Name string // name of the environment
|
||||
Repo string // name of GitHub repo
|
||||
Commit, Branch, Tag string // Git info
|
||||
Buildnum string
|
||||
IsPullRequest bool
|
||||
}
|
||||
|
||||
func (env Environment) String() string {
|
||||
return fmt.Sprintf("%s env (commit:%s branch:%s tag:%s buildnum:%s pr:%t)",
|
||||
env.Name, env.Commit, env.Branch, env.Tag, env.Buildnum, env.IsPullRequest)
|
||||
}
|
||||
|
||||
// Env returns metadata about the current CI environment, falling back to LocalEnv
|
||||
// if not running on CI.
|
||||
func Env() Environment {
|
||||
switch {
|
||||
case os.Getenv("CI") == "true" && os.Getenv("TRAVIS") == "true":
|
||||
return Environment{
|
||||
Name: "travis",
|
||||
Repo: os.Getenv("TRAVIS_REPO_SLUG"),
|
||||
Commit: os.Getenv("TRAVIS_COMMIT"),
|
||||
Branch: os.Getenv("TRAVIS_BRANCH"),
|
||||
Tag: os.Getenv("TRAVIS_TAG"),
|
||||
Buildnum: os.Getenv("TRAVIS_BUILD_NUMBER"),
|
||||
IsPullRequest: os.Getenv("TRAVIS_PULL_REQUEST") != "false",
|
||||
}
|
||||
case os.Getenv("CI") == "True" && os.Getenv("APPVEYOR") == "True":
|
||||
return Environment{
|
||||
Name: "appveyor",
|
||||
Repo: os.Getenv("APPVEYOR_REPO_NAME"),
|
||||
Commit: os.Getenv("APPVEYOR_REPO_COMMIT"),
|
||||
Branch: os.Getenv("APPVEYOR_REPO_BRANCH"),
|
||||
Tag: os.Getenv("APPVEYOR_REPO_TAG_NAME"),
|
||||
Buildnum: os.Getenv("APPVEYOR_BUILD_NUMBER"),
|
||||
IsPullRequest: os.Getenv("APPVEYOR_PULL_REQUEST_NUMBER") != "",
|
||||
}
|
||||
default:
|
||||
return LocalEnv()
|
||||
}
|
||||
}
|
||||
|
||||
// LocalEnv returns build environment metadata gathered from git.
|
||||
func LocalEnv() Environment {
|
||||
env := applyEnvFlags(Environment{Name: "local", Repo: "ethereum/go-ethereum"})
|
||||
if _, err := os.Stat(".git"); err != nil {
|
||||
return env
|
||||
}
|
||||
if env.Commit == "" {
|
||||
env.Commit = RunGit("rev-parse", "HEAD")
|
||||
}
|
||||
if env.Branch == "" {
|
||||
if b := RunGit("rev-parse", "--abbrev-ref", "HEAD"); b != "HEAD" {
|
||||
env.Branch = b
|
||||
}
|
||||
}
|
||||
// Note that we don't get the current git tag. It would slow down
|
||||
// builds and isn't used by anything.
|
||||
return env
|
||||
}
|
||||
|
||||
func applyEnvFlags(env Environment) Environment {
|
||||
if !flag.Parsed() {
|
||||
panic("you need to call flag.Parse before Env or LocalEnv")
|
||||
}
|
||||
if *GitCommitFlag != "" {
|
||||
env.Commit = *GitCommitFlag
|
||||
}
|
||||
if *GitBranchFlag != "" {
|
||||
env.Branch = *GitBranchFlag
|
||||
}
|
||||
if *GitTagFlag != "" {
|
||||
env.Tag = *GitTagFlag
|
||||
}
|
||||
if *BuildnumFlag != "" {
|
||||
env.Buildnum = *BuildnumFlag
|
||||
}
|
||||
if *PullRequestFlag {
|
||||
env.IsPullRequest = true
|
||||
}
|
||||
return env
|
||||
}
|
||||
@@ -29,9 +29,7 @@ import (
|
||||
"text/template"
|
||||
)
|
||||
|
||||
var (
|
||||
DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
|
||||
)
|
||||
var DryRunFlag = flag.Bool("n", false, "dry run, don't execute commands")
|
||||
|
||||
// MustRun executes the given command and exits the host process for
|
||||
// any error.
|
||||
@@ -69,6 +67,7 @@ func GOPATH() string {
|
||||
return strings.Join(newpath, string(filepath.ListSeparator))
|
||||
}
|
||||
|
||||
// VERSION returns the content of the VERSION file.
|
||||
func VERSION() string {
|
||||
version, err := ioutil.ReadFile("VERSION")
|
||||
if err != nil {
|
||||
@@ -77,10 +76,8 @@ func VERSION() string {
|
||||
return string(bytes.TrimSpace(version))
|
||||
}
|
||||
|
||||
func GitCommit() string {
|
||||
return RunGit("rev-parse", "HEAD")
|
||||
}
|
||||
|
||||
// RunGit runs a git subcommand and returns its output.
|
||||
// The command must complete successfully.
|
||||
func RunGit(args ...string) string {
|
||||
cmd := exec.Command("git", args...)
|
||||
var stdout, stderr bytes.Buffer
|
||||
@@ -94,12 +91,13 @@ func RunGit(args ...string) string {
|
||||
return strings.TrimSpace(stdout.String())
|
||||
}
|
||||
|
||||
// Render renders the given template file.
|
||||
// Render renders the given template file into outputFile.
|
||||
func Render(templateFile, outputFile string, outputPerm os.FileMode, x interface{}) {
|
||||
tpl := template.Must(template.ParseFiles(templateFile))
|
||||
render(tpl, outputFile, outputPerm, x)
|
||||
}
|
||||
|
||||
// RenderString renders the given template string into outputFile.
|
||||
func RenderString(templateContent, outputFile string, outputPerm os.FileMode, x interface{}) {
|
||||
tpl := template.Must(template.New("").Parse(templateContent))
|
||||
render(tpl, outputFile, outputPerm, x)
|
||||
|
||||
@@ -52,6 +52,11 @@ var (
|
||||
Usage: "pprof HTTP server listening port",
|
||||
Value: 6060,
|
||||
}
|
||||
pprofAddrFlag = cli.StringFlag{
|
||||
Name: "pprofaddr",
|
||||
Usage: "pprof HTTP server listening interface",
|
||||
Value: "127.0.0.1",
|
||||
}
|
||||
memprofilerateFlag = cli.IntFlag{
|
||||
Name: "memprofilerate",
|
||||
Usage: "Turn on memory profiling with the given rate",
|
||||
@@ -74,7 +79,7 @@ var (
|
||||
// Flags holds all command-line flags required for debugging.
|
||||
var Flags = []cli.Flag{
|
||||
verbosityFlag, vmoduleFlag, backtraceAtFlag,
|
||||
pprofFlag, pprofPortFlag,
|
||||
pprofFlag, pprofAddrFlag, pprofPortFlag,
|
||||
memprofilerateFlag, blockprofilerateFlag, cpuprofileFlag, traceFlag,
|
||||
}
|
||||
|
||||
@@ -101,7 +106,7 @@ func Setup(ctx *cli.Context) error {
|
||||
|
||||
// pprof server
|
||||
if ctx.GlobalBool(pprofFlag.Name) {
|
||||
address := fmt.Sprintf("127.0.0.1:%d", ctx.GlobalInt(pprofPortFlag.Name))
|
||||
address := fmt.Sprintf("%s:%d", ctx.GlobalString(pprofAddrFlag.Name), ctx.GlobalInt(pprofPortFlag.Name))
|
||||
go func() {
|
||||
glog.V(logger.Info).Infof("starting pprof server at http://%s/debug/pprof", address)
|
||||
glog.Errorln(http.ListenAndServe(address, nil))
|
||||
|
||||
@@ -53,18 +53,15 @@ func makeTestState() (common.Hash, ethdb.Database) {
|
||||
sdb, _ := ethdb.NewMemDatabase()
|
||||
st, _ := state.New(common.Hash{}, sdb)
|
||||
for i := byte(0); i < 100; i++ {
|
||||
so := st.GetOrNewStateObject(common.Address{i})
|
||||
addr := common.Address{i}
|
||||
for j := byte(0); j < 100; j++ {
|
||||
val := common.Hash{i, j}
|
||||
so.SetState(common.Hash{j}, val)
|
||||
so.SetNonce(100)
|
||||
st.SetState(addr, common.Hash{j}, common.Hash{i, j})
|
||||
}
|
||||
so.AddBalance(big.NewInt(int64(i)))
|
||||
so.SetCode([]byte{i, i, i})
|
||||
so.UpdateRoot(sdb)
|
||||
st.UpdateStateObject(so)
|
||||
st.SetNonce(addr, 100)
|
||||
st.AddBalance(addr, big.NewInt(int64(i)))
|
||||
st.SetCode(addr, []byte{i, i, i})
|
||||
}
|
||||
root, _ := st.Commit()
|
||||
root, _ := st.Commit(false)
|
||||
return root, sdb
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ func (t *LightTrie) do(ctx context.Context, fallbackKey []byte, fn func() error)
|
||||
func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error) {
|
||||
err = t.do(ctx, key, func() (err error) {
|
||||
if t.trie == nil {
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db)
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
|
||||
}
|
||||
if err == nil {
|
||||
res, err = t.trie.TryGet(key)
|
||||
@@ -98,7 +98,7 @@ func (t *LightTrie) Get(ctx context.Context, key []byte) (res []byte, err error)
|
||||
func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
|
||||
err = t.do(ctx, key, func() (err error) {
|
||||
if t.trie == nil {
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db)
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
|
||||
}
|
||||
if err == nil {
|
||||
err = t.trie.TryUpdate(key, value)
|
||||
@@ -112,7 +112,7 @@ func (t *LightTrie) Update(ctx context.Context, key, value []byte) (err error) {
|
||||
func (t *LightTrie) Delete(ctx context.Context, key []byte) (err error) {
|
||||
err = t.do(ctx, key, func() (err error) {
|
||||
if t.trie == nil {
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db)
|
||||
t.trie, err = trie.NewSecure(t.originalRoot, t.db, 0)
|
||||
}
|
||||
if err == nil {
|
||||
err = t.trie.TryDelete(key)
|
||||
|
||||
@@ -26,6 +26,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/rcrowley/go-metrics"
|
||||
"github.com/rcrowley/go-metrics/exp"
|
||||
)
|
||||
|
||||
// MetricsEnabledFlag is the CLI flag name to use to enable metrics collections.
|
||||
@@ -44,6 +45,7 @@ func init() {
|
||||
Enabled = true
|
||||
}
|
||||
}
|
||||
exp.Exp(metrics.DefaultRegistry)
|
||||
}
|
||||
|
||||
// NewMeter create a new metrics Meter, either a real one of a NOP stub depending
|
||||
|
||||
@@ -51,7 +51,7 @@ type Miner struct {
|
||||
shouldStart int32 // should start indicates whether we should start after sync
|
||||
}
|
||||
|
||||
func New(eth core.Backend, config *core.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner {
|
||||
func New(eth core.Backend, config *params.ChainConfig, mux *event.TypeMux, pow pow.PoW) *Miner {
|
||||
miner := &Miner{eth: eth, mux: mux, pow: pow, worker: newWorker(config, common.Address{}, eth), canStart: 1}
|
||||
go miner.update()
|
||||
|
||||
|
||||
172
miner/worker.go
172
miner/worker.go
@@ -63,18 +63,17 @@ type uint64RingBuffer struct {
|
||||
// environment is the workers current environment and holds
|
||||
// all of the current state information
|
||||
type Work struct {
|
||||
config *core.ChainConfig
|
||||
state *state.StateDB // apply state changes here
|
||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||
family *set.Set // family set (used for checking uncle invalidity)
|
||||
uncles *set.Set // uncle set
|
||||
remove *set.Set // tx which will be removed
|
||||
tcount int // tx count in cycle
|
||||
ignoredTransactors *set.Set
|
||||
lowGasTransactors *set.Set
|
||||
ownedAccounts *set.Set
|
||||
lowGasTxs types.Transactions
|
||||
localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
|
||||
config *params.ChainConfig
|
||||
signer types.Signer
|
||||
state *state.StateDB // apply state changes here
|
||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||
family *set.Set // family set (used for checking uncle invalidity)
|
||||
uncles *set.Set // uncle set
|
||||
tcount int // tx count in cycle
|
||||
ownedAccounts *set.Set
|
||||
lowGasTxs types.Transactions
|
||||
failedTxs types.Transactions
|
||||
localMinedBlocks *uint64RingBuffer // the most recent block numbers that were mined locally (used to check block inclusion)
|
||||
|
||||
Block *types.Block // the new block
|
||||
|
||||
@@ -92,7 +91,7 @@ type Result struct {
|
||||
|
||||
// worker is the main object which takes care of applying messages to the new state
|
||||
type worker struct {
|
||||
config *core.ChainConfig
|
||||
config *params.ChainConfig
|
||||
|
||||
mu sync.Mutex
|
||||
|
||||
@@ -130,7 +129,7 @@ type worker struct {
|
||||
fullValidation bool
|
||||
}
|
||||
|
||||
func newWorker(config *core.ChainConfig, coinbase common.Address, eth core.Backend) *worker {
|
||||
func newWorker(config *params.ChainConfig, coinbase common.Address, eth core.Backend) *worker {
|
||||
worker := &worker{
|
||||
config: config,
|
||||
eth: eth,
|
||||
@@ -173,7 +172,7 @@ func (self *worker) pending() (*types.Block, *state.StateDB) {
|
||||
self.current.receipts,
|
||||
), self.current.state
|
||||
}
|
||||
return self.current.Block, self.current.state
|
||||
return self.current.Block, self.current.state.Copy()
|
||||
}
|
||||
|
||||
func (self *worker) start() {
|
||||
@@ -236,7 +235,12 @@ func (self *worker) update() {
|
||||
// Apply transaction to the pending state if we're not mining
|
||||
if atomic.LoadInt32(&self.mining) == 0 {
|
||||
self.currentMu.Lock()
|
||||
self.current.commitTransactions(self.mux, types.Transactions{ev.Tx}, self.gasPrice, self.chain)
|
||||
|
||||
acc, _ := ev.Tx.From()
|
||||
txs := map[common.Address]types.Transactions{acc: types.Transactions{ev.Tx}}
|
||||
txset := types.NewTransactionsByPriceAndNonce(txs)
|
||||
|
||||
self.current.commitTransactions(self.mux, txset, self.gasPrice, self.chain)
|
||||
self.currentMu.Unlock()
|
||||
}
|
||||
}
|
||||
@@ -273,7 +277,7 @@ func (self *worker) wait() {
|
||||
}
|
||||
go self.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||
} else {
|
||||
work.state.Commit()
|
||||
work.state.Commit(self.config.IsEIP158(block.Number()))
|
||||
parent := self.chain.GetBlock(block.ParentHash())
|
||||
if parent == nil {
|
||||
glog.V(logger.Error).Infoln("Invalid block found during mining")
|
||||
@@ -364,6 +368,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
|
||||
}
|
||||
work := &Work{
|
||||
config: self.config,
|
||||
signer: types.NewEIP155Signer(self.config.ChainId),
|
||||
state: state,
|
||||
ancestors: set.New(),
|
||||
family: set.New(),
|
||||
@@ -383,10 +388,7 @@ func (self *worker) makeCurrent(parent *types.Block, header *types.Header) error
|
||||
accounts := self.eth.AccountManager().Accounts()
|
||||
|
||||
// Keep track of transactions which return errors so they can be removed
|
||||
work.remove = set.New()
|
||||
work.tcount = 0
|
||||
work.ignoredTransactors = set.New()
|
||||
work.lowGasTransactors = set.New()
|
||||
work.ownedAccounts = accountAddressesSet(accounts)
|
||||
if self.current != nil {
|
||||
work.localMinedBlocks = self.current.localMinedBlocks
|
||||
@@ -495,45 +497,11 @@ func (self *worker) commitNewWork() {
|
||||
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
||||
core.ApplyDAOHardFork(work.state)
|
||||
}
|
||||
txs := types.NewTransactionsByPriceAndNonce(self.eth.TxPool().Pending())
|
||||
work.commitTransactions(self.mux, txs, self.gasPrice, self.chain)
|
||||
|
||||
/* //approach 1
|
||||
transactions := self.eth.TxPool().GetTransactions()
|
||||
sort.Sort(types.TxByNonce(transactions))
|
||||
*/
|
||||
|
||||
//approach 2
|
||||
transactions := self.eth.TxPool().GetTransactions()
|
||||
types.SortByPriceAndNonce(transactions)
|
||||
|
||||
/* // approach 3
|
||||
// commit transactions for this run.
|
||||
txPerOwner := make(map[common.Address]types.Transactions)
|
||||
// Sort transactions by owner
|
||||
for _, tx := range self.eth.TxPool().GetTransactions() {
|
||||
from, _ := tx.From() // we can ignore the sender error
|
||||
txPerOwner[from] = append(txPerOwner[from], tx)
|
||||
}
|
||||
var (
|
||||
singleTxOwner types.Transactions
|
||||
multiTxOwner types.Transactions
|
||||
)
|
||||
// Categorise transactions by
|
||||
// 1. 1 owner tx per block
|
||||
// 2. multi txs owner per block
|
||||
for _, txs := range txPerOwner {
|
||||
if len(txs) == 1 {
|
||||
singleTxOwner = append(singleTxOwner, txs[0])
|
||||
} else {
|
||||
multiTxOwner = append(multiTxOwner, txs...)
|
||||
}
|
||||
}
|
||||
sort.Sort(types.TxByPrice(singleTxOwner))
|
||||
sort.Sort(types.TxByNonce(multiTxOwner))
|
||||
transactions := append(singleTxOwner, multiTxOwner...)
|
||||
*/
|
||||
|
||||
work.commitTransactions(self.mux, transactions, self.gasPrice, self.chain)
|
||||
self.eth.TxPool().RemoveTransactions(work.lowGasTxs)
|
||||
self.eth.TxPool().RemoveBatch(work.lowGasTxs)
|
||||
self.eth.TxPool().RemoveBatch(work.failedTxs)
|
||||
|
||||
// compute uncles for the new block.
|
||||
var (
|
||||
@@ -562,7 +530,7 @@ func (self *worker) commitNewWork() {
|
||||
if atomic.LoadInt32(&self.mining) == 1 {
|
||||
// commit state root after all state transitions.
|
||||
core.AccumulateRewards(work.state, header, uncles)
|
||||
header.Root = work.state.IntermediateRoot()
|
||||
header.Root = work.state.IntermediateRoot(self.config.IsEIP158(header.Number))
|
||||
}
|
||||
|
||||
// create the new block whose nonce will be mined.
|
||||
@@ -591,65 +559,62 @@ func (self *worker) commitUncle(work *Work, uncle *types.Header) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Transactions, gasPrice *big.Int, bc *core.BlockChain) {
|
||||
func (env *Work) commitTransactions(mux *event.TypeMux, txs *types.TransactionsByPriceAndNonce, gasPrice *big.Int, bc *core.BlockChain) {
|
||||
gp := new(core.GasPool).AddGas(env.header.GasLimit)
|
||||
|
||||
var coalescedLogs vm.Logs
|
||||
for _, tx := range transactions {
|
||||
for {
|
||||
// Retrieve the next transaction and abort if all done
|
||||
tx := txs.Peek()
|
||||
if tx == nil {
|
||||
break
|
||||
}
|
||||
// Error may be ignored here. The error has already been checked
|
||||
// during transaction acceptance is the transaction pool.
|
||||
//
|
||||
// We use the eip155 signer regardless of the current hf.
|
||||
tx.SetSigner(env.signer)
|
||||
from, _ := tx.From()
|
||||
// Check whether the tx is replay protected. If we're not in the EIP155 hf
|
||||
// phase, start ignoring the sender until we do.
|
||||
if tx.Protected() && !env.config.IsEIP155(env.header.Number) {
|
||||
glog.V(logger.Detail).Infof("Transaction (%x) is replay protected, but we haven't yet hardforked. Transaction will be ignored until we hardfork.\n", tx.Hash())
|
||||
|
||||
// Check if it falls within margin. Txs from owned accounts are always processed.
|
||||
txs.Pop()
|
||||
continue
|
||||
}
|
||||
|
||||
// Ignore any transactions (and accounts subsequently) with low gas limits
|
||||
if tx.GasPrice().Cmp(gasPrice) < 0 && !env.ownedAccounts.Has(from) {
|
||||
// ignore the transaction and transactor. We ignore the transactor
|
||||
// because nonce will fail after ignoring this transaction so there's
|
||||
// no point
|
||||
env.lowGasTransactors.Add(from)
|
||||
// Pop the current low-priced transaction without shifting in the next from the account
|
||||
glog.V(logger.Info).Infof("Transaction (%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4])
|
||||
|
||||
glog.V(logger.Info).Infof("transaction(%x) below gas price (tx=%v ask=%v). All sequential txs from this address(%x) will be ignored\n", tx.Hash().Bytes()[:4], common.CurrencyToString(tx.GasPrice()), common.CurrencyToString(gasPrice), from[:4])
|
||||
}
|
||||
env.lowGasTxs = append(env.lowGasTxs, tx)
|
||||
txs.Pop()
|
||||
|
||||
// Continue with the next transaction if the transaction sender is included in
|
||||
// the low gas tx set. This will also remove the tx and all sequential transaction
|
||||
// from this transactor
|
||||
if env.lowGasTransactors.Has(from) {
|
||||
// add tx to the low gas set. This will be removed at the end of the run
|
||||
// owned accounts are ignored
|
||||
if !env.ownedAccounts.Has(from) {
|
||||
env.lowGasTxs = append(env.lowGasTxs, tx)
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
// Move on to the next transaction when the transactor is in ignored transactions set
|
||||
// This may occur when a transaction hits the gas limit. When a gas limit is hit and
|
||||
// the transaction is processed (that could potentially be included in the block) it
|
||||
// will throw a nonce error because the previous transaction hasn't been processed.
|
||||
// Therefor we need to ignore any transaction after the ignored one.
|
||||
if env.ignoredTransactors.Has(from) {
|
||||
continue
|
||||
}
|
||||
|
||||
// Start executing the transaction
|
||||
env.state.StartRecord(tx.Hash(), common.Hash{}, 0)
|
||||
|
||||
err, logs := env.commitTransaction(tx, bc, gp)
|
||||
switch {
|
||||
case core.IsGasLimitErr(err):
|
||||
// ignore the transactor so no nonce errors will be thrown for this account
|
||||
// next time the worker is run, they'll be picked up again.
|
||||
env.ignoredTransactors.Add(from)
|
||||
|
||||
// Pop the current out-of-gas transaction without shifting in the next from the account
|
||||
glog.V(logger.Detail).Infof("Gas limit reached for (%x) in this block. Continue to try smaller txs\n", from[:4])
|
||||
case err != nil:
|
||||
env.remove.Add(tx.Hash())
|
||||
txs.Pop()
|
||||
|
||||
case err != nil:
|
||||
// Pop the current failed transaction without shifting in the next from the account
|
||||
glog.V(logger.Detail).Infof("Transaction (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
|
||||
env.failedTxs = append(env.failedTxs, tx)
|
||||
txs.Pop()
|
||||
|
||||
if glog.V(logger.Detail) {
|
||||
glog.Infof("TX (%x) failed, will be removed: %v\n", tx.Hash().Bytes()[:4], err)
|
||||
}
|
||||
default:
|
||||
env.tcount++
|
||||
// Everything ok, collect the logs and shift in the next transaction from the same account
|
||||
coalescedLogs = append(coalescedLogs, logs...)
|
||||
env.tcount++
|
||||
txs.Shift()
|
||||
}
|
||||
}
|
||||
if len(coalescedLogs) > 0 || env.tcount > 0 {
|
||||
@@ -665,18 +630,11 @@ func (env *Work) commitTransactions(mux *event.TypeMux, transactions types.Trans
|
||||
}
|
||||
|
||||
func (env *Work) commitTransaction(tx *types.Transaction, bc *core.BlockChain, gp *core.GasPool) (error, vm.Logs) {
|
||||
snap := env.state.Copy()
|
||||
snap := env.state.Snapshot()
|
||||
|
||||
// this is a bit of a hack to force jit for the miners
|
||||
config := env.config.VmConfig
|
||||
if !(config.EnableJit && config.ForceJit) {
|
||||
config.EnableJit = false
|
||||
}
|
||||
config.ForceJit = false // disable forcing jit
|
||||
|
||||
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, config)
|
||||
receipt, logs, _, err := core.ApplyTransaction(env.config, bc, gp, env.state, env.header, tx, env.header.GasUsed, vm.Config{})
|
||||
if err != nil {
|
||||
env.state.Set(snap)
|
||||
env.state.RevertToSnapshot(snap)
|
||||
return err, nil
|
||||
}
|
||||
env.txs = append(env.txs, tx)
|
||||
|
||||
112
params/config.go
Normal file
112
params/config.go
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2016 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 params
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// ChainConfig is the core config which determines the blockchain settings.
|
||||
//
|
||||
// ChainConfig is stored in the database on a per block basis. This means
|
||||
// that any network, identified by its genesis block, can have its own
|
||||
// set of configuration options.
|
||||
type ChainConfig struct {
|
||||
ChainId *big.Int `json:"chainId"` // Chain id identifies the current chain and is used for replay protection
|
||||
|
||||
HomesteadBlock *big.Int `json:"homesteadBlock"` // Homestead switch block (nil = no fork, 0 = already homestead)
|
||||
DAOForkBlock *big.Int `json:"daoForkBlock"` // TheDAO hard-fork switch block (nil = no fork)
|
||||
DAOForkSupport bool `json:"daoForkSupport"` // Whether the nodes supports or opposes the DAO hard-fork
|
||||
|
||||
// EIP150 implements the Gas price changes (https://github.com/ethereum/EIPs/issues/150)
|
||||
EIP150Block *big.Int `json:"EIP150Block"` // EIP150 HF block (nil = no fork)
|
||||
EIP150Hash common.Hash `json:"EIP150Hash"` // EIP150 HF hash (fast sync aid)
|
||||
|
||||
EIP155Block *big.Int `json:"EIP155Block"` // EIP155 HF block
|
||||
EIP158Block *big.Int `json:"EIP158Block"` // EIP158 HF block
|
||||
}
|
||||
|
||||
var (
|
||||
TestChainConfig = &ChainConfig{big.NewInt(1), new(big.Int), new(big.Int), true, new(big.Int), common.Hash{}, new(big.Int), new(big.Int)}
|
||||
TestRules = TestChainConfig.Rules(new(big.Int))
|
||||
)
|
||||
|
||||
// IsHomestead returns whether num is either equal to the homestead block or greater.
|
||||
func (c *ChainConfig) IsHomestead(num *big.Int) bool {
|
||||
if c.HomesteadBlock == nil || num == nil {
|
||||
return false
|
||||
}
|
||||
return num.Cmp(c.HomesteadBlock) >= 0
|
||||
}
|
||||
|
||||
// GasTable returns the gas table corresponding to the current phase (homestead or homestead reprice).
|
||||
//
|
||||
// The returned GasTable's fields shouldn't, under any circumstances, be changed.
|
||||
func (c *ChainConfig) GasTable(num *big.Int) GasTable {
|
||||
if num == nil {
|
||||
return GasTableHomestead
|
||||
}
|
||||
|
||||
switch {
|
||||
case c.EIP158Block != nil && num.Cmp(c.EIP158Block) >= 0:
|
||||
return GasTableEIP158
|
||||
case c.EIP150Block != nil && num.Cmp(c.EIP150Block) >= 0:
|
||||
return GasTableHomesteadGasRepriceFork
|
||||
default:
|
||||
return GasTableHomestead
|
||||
}
|
||||
}
|
||||
|
||||
func (c *ChainConfig) IsEIP150(num *big.Int) bool {
|
||||
if c.EIP150Block == nil || num == nil {
|
||||
return false
|
||||
}
|
||||
return num.Cmp(c.EIP150Block) >= 0
|
||||
|
||||
}
|
||||
|
||||
func (c *ChainConfig) IsEIP155(num *big.Int) bool {
|
||||
if c.EIP155Block == nil || num == nil {
|
||||
return false
|
||||
}
|
||||
return num.Cmp(c.EIP155Block) >= 0
|
||||
|
||||
}
|
||||
|
||||
func (c *ChainConfig) IsEIP158(num *big.Int) bool {
|
||||
if c.EIP158Block == nil || num == nil {
|
||||
return false
|
||||
}
|
||||
return num.Cmp(c.EIP158Block) >= 0
|
||||
|
||||
}
|
||||
|
||||
// Rules wraps ChainConfig and is merely syntatic sugar or can be used for functions
|
||||
// that do not have or require information about the block.
|
||||
//
|
||||
// Rules is a one time interface meaning that it shouldn't be used in between transition
|
||||
// phases.
|
||||
type Rules struct {
|
||||
ChainId *big.Int
|
||||
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
|
||||
}
|
||||
|
||||
func (c *ChainConfig) Rules(num *big.Int) Rules {
|
||||
return Rules{ChainId: new(big.Int).Set(c.ChainId), IsHomestead: c.IsHomestead(num), IsEIP150: c.IsEIP150(num), IsEIP155: c.IsEIP155(num), IsEIP158: c.IsEIP158(num)}
|
||||
}
|
||||
83
params/gas_table.go
Normal file
83
params/gas_table.go
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2016 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 params
|
||||
|
||||
import "math/big"
|
||||
|
||||
type GasTable struct {
|
||||
ExtcodeSize *big.Int
|
||||
ExtcodeCopy *big.Int
|
||||
Balance *big.Int
|
||||
SLoad *big.Int
|
||||
Calls *big.Int
|
||||
Suicide *big.Int
|
||||
|
||||
ExpByte *big.Int
|
||||
|
||||
// CreateBySuicide occurs when the
|
||||
// refunded account is one that does
|
||||
// not exist. This logic is similar
|
||||
// to call. May be left nil. Nil means
|
||||
// not charged.
|
||||
CreateBySuicide *big.Int
|
||||
}
|
||||
|
||||
var (
|
||||
// GasTableHomestead contain the gas prices for
|
||||
// the homestead phase.
|
||||
GasTableHomestead = GasTable{
|
||||
ExtcodeSize: big.NewInt(20),
|
||||
ExtcodeCopy: big.NewInt(20),
|
||||
Balance: big.NewInt(20),
|
||||
SLoad: big.NewInt(50),
|
||||
Calls: big.NewInt(40),
|
||||
Suicide: big.NewInt(0),
|
||||
ExpByte: big.NewInt(10),
|
||||
|
||||
// explicitly set to nil to indicate
|
||||
// this rule does not apply to homestead.
|
||||
CreateBySuicide: nil,
|
||||
}
|
||||
|
||||
// GasTableHomestead contain the gas re-prices for
|
||||
// the homestead phase.
|
||||
//
|
||||
// TODO rename to GasTableEIP150
|
||||
GasTableHomesteadGasRepriceFork = GasTable{
|
||||
ExtcodeSize: big.NewInt(700),
|
||||
ExtcodeCopy: big.NewInt(700),
|
||||
Balance: big.NewInt(400),
|
||||
SLoad: big.NewInt(200),
|
||||
Calls: big.NewInt(700),
|
||||
Suicide: big.NewInt(5000),
|
||||
ExpByte: big.NewInt(10),
|
||||
|
||||
CreateBySuicide: big.NewInt(25000),
|
||||
}
|
||||
|
||||
GasTableEIP158 = GasTable{
|
||||
ExtcodeSize: big.NewInt(700),
|
||||
ExtcodeCopy: big.NewInt(700),
|
||||
Balance: big.NewInt(400),
|
||||
SLoad: big.NewInt(200),
|
||||
Calls: big.NewInt(700),
|
||||
Suicide: big.NewInt(5000),
|
||||
ExpByte: big.NewInt(50),
|
||||
|
||||
CreateBySuicide: big.NewInt(25000),
|
||||
}
|
||||
)
|
||||
@@ -71,4 +71,5 @@ var (
|
||||
SuicideRefundGas = big.NewInt(24000) // Refunded following a suicide operation.
|
||||
MemoryGas = big.NewInt(3) // Times the address of the (highest referenced byte in memory + 1). NOTE: referencing happens on read, write and in instructions such as RETURN and CALL.
|
||||
TxDataNonZeroGas = big.NewInt(68) // Per byte of data attached to a transaction that is not equal to zero. NOTE: Not payable on data of calls between transactions.
|
||||
|
||||
)
|
||||
|
||||
@@ -16,9 +16,22 @@
|
||||
|
||||
package params
|
||||
|
||||
import "math/big"
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var (
|
||||
TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block
|
||||
MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block
|
||||
TestNetHomesteadBlock = big.NewInt(494000) // Testnet homestead block
|
||||
MainNetHomesteadBlock = big.NewInt(1150000) // Mainnet homestead block
|
||||
TestNetHomesteadGasRepriceBlock = big.NewInt(1783000) // Test net gas reprice block
|
||||
MainNetHomesteadGasRepriceBlock = big.NewInt(2463000) // Main net gas reprice block
|
||||
TestNetHomesteadGasRepriceHash = common.HexToHash("0xf376243aeff1f256d970714c3de9fd78fa4e63cf63e32a51fe1169e375d98145") // Testnet gas reprice block hash (used by fast sync)
|
||||
MainNetHomesteadGasRepriceHash = common.HexToHash("0x2086799aeebeae135c246c65021c82b4e15a2c451340993aacfd2751886514f0") // Mainnet gas reprice block hash (used by fast sync)
|
||||
TestNetChainID = big.NewInt(2) // Testnet default chain ID
|
||||
MainNetChainID = big.NewInt(1) // Main net default chain ID
|
||||
|
||||
TestNetSpuriousDragon = big.NewInt(1885000)
|
||||
MainNetSpuriousDragon = big.NewInt(2675000)
|
||||
)
|
||||
|
||||
@@ -23,63 +23,63 @@ import (
|
||||
)
|
||||
|
||||
func TestBcValidBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcValidBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcUncleHeaderValidityTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcUncleTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcForkUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkUncle.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcInvalidHeaderTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcInvalidRLPTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcInvalidRLPTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcRPCAPITests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcForkBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcForkStress(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcForkStressTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -89,21 +89,21 @@ func TestBcTotalDifficulty(t *testing.T) {
|
||||
// skip because these will fail due to selfish mining fix
|
||||
t.Skip()
|
||||
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcWallet(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcWalletTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcGasPricer(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, nil, filepath.Join(blockTestDir, "bcGasPricerTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -111,7 +111,7 @@ func TestBcGasPricer(t *testing.T) {
|
||||
|
||||
// TODO: iterate over files once we got more than a few
|
||||
func TestBcRandom(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "RandomTests/bl201507071825GO.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -121,14 +121,14 @@ func TestBcMultiChain(t *testing.T) {
|
||||
// skip due to selfish mining
|
||||
t.Skip()
|
||||
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcMultiChainTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBcState(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(1000000), nil, big.NewInt(10), filepath.Join(blockTestDir, "bcStateTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -136,77 +136,77 @@ func TestBcState(t *testing.T) {
|
||||
|
||||
// Homestead tests
|
||||
func TestHomesteadBcValidBlockTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcValidBlockTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcUncleHeaderValidityTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleHeaderValiditiy.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcUncleTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcUncleTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcInvalidHeaderTests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcInvalidHeaderTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcRPCAPITests(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcRPC_API_Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcForkStress(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcForkStressTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcTotalDifficulty(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcTotalDifficultyTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcWallet(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcWalletTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcGasPricer(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcGasPricerTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcMultiChain(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcMultiChainTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcState(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcStateTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -214,7 +214,33 @@ func TestHomesteadBcState(t *testing.T) {
|
||||
|
||||
// DAO hard-fork tests
|
||||
func TestDAOBcTheDao(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(5), big.NewInt(8), filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests)
|
||||
err := RunBlockTest(big.NewInt(5), big.NewInt(8), nil, filepath.Join(blockTestDir, "TestNetwork", "bcTheDaoTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP150Bc(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), big.NewInt(8), big.NewInt(10), filepath.Join(blockTestDir, "TestNetwork", "bcEIP150Test.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHomesteadBcExploit(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcExploitTest.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestHomesteadBcShanghaiLove(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcShanghaiLove.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
func TestHomesteadBcSuicideIssue(t *testing.T) {
|
||||
err := RunBlockTest(big.NewInt(0), nil, nil, filepath.Join(blockTestDir, "Homestead", "bcSuicideIssue.json"), BlockSkipTests)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
@@ -31,9 +31,11 @@ import (
|
||||
"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/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
@@ -103,7 +105,7 @@ type btTransaction struct {
|
||||
Value string
|
||||
}
|
||||
|
||||
func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader, skipTests []string) error {
|
||||
func RunBlockTestWithReader(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, r io.Reader, skipTests []string) error {
|
||||
btjs := make(map[string]*btJSON)
|
||||
if err := readJson(r, &btjs); err != nil {
|
||||
return err
|
||||
@@ -114,13 +116,13 @@ func RunBlockTestWithReader(homesteadBlock, daoForkBlock *big.Int, r io.Reader,
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil {
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests []string) error {
|
||||
func RunBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, file string, skipTests []string) error {
|
||||
btjs := make(map[string]*btJSON)
|
||||
if err := readJsonFile(file, &btjs); err != nil {
|
||||
return err
|
||||
@@ -130,13 +132,13 @@ func RunBlockTest(homesteadBlock, daoForkBlock *big.Int, file string, skipTests
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, bt, skipTests); err != nil {
|
||||
if err := runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork, bt, skipTests); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTest, skipTests []string) error {
|
||||
func runBlockTests(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, bt map[string]*BlockTest, skipTests []string) error {
|
||||
skipTest := make(map[string]bool, len(skipTests))
|
||||
for _, name := range skipTests {
|
||||
skipTest[name] = true
|
||||
@@ -148,7 +150,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe
|
||||
continue
|
||||
}
|
||||
// test the block
|
||||
if err := runBlockTest(homesteadBlock, daoForkBlock, test); err != nil {
|
||||
if err := runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork, test); err != nil {
|
||||
return fmt.Errorf("%s: %v", name, err)
|
||||
}
|
||||
glog.Infoln("Block test passed: ", name)
|
||||
@@ -157,7 +159,7 @@ func runBlockTests(homesteadBlock, daoForkBlock *big.Int, bt map[string]*BlockTe
|
||||
return nil
|
||||
}
|
||||
|
||||
func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error {
|
||||
func runBlockTest(homesteadBlock, daoForkBlock, gasPriceFork *big.Int, test *BlockTest) error {
|
||||
// import pre accounts & construct test genesis block & state root
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
if _, err := test.InsertPreState(db); err != nil {
|
||||
@@ -169,7 +171,7 @@ func runBlockTest(homesteadBlock, daoForkBlock *big.Int, test *BlockTest) error
|
||||
core.WriteCanonicalHash(db, test.Genesis.Hash(), test.Genesis.NumberU64())
|
||||
core.WriteHeadBlockHash(db, test.Genesis.Hash())
|
||||
evmux := new(event.TypeMux)
|
||||
config := &core.ChainConfig{HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true}
|
||||
config := ¶ms.ChainConfig{ChainId: new(big.Int), HomesteadBlock: homesteadBlock, DAOForkBlock: daoForkBlock, DAOForkSupport: true, EIP150Block: gasPriceFork}
|
||||
chain, err := core.NewBlockChain(db, config, ethash.NewShared(), evmux)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -219,7 +221,7 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
|
||||
return nil, err
|
||||
}
|
||||
obj := statedb.CreateAccount(common.HexToAddress(addrString))
|
||||
obj.SetCode(code)
|
||||
obj.SetCode(crypto.Keccak256Hash(code), code)
|
||||
obj.SetBalance(balance)
|
||||
obj.SetNonce(nonce)
|
||||
for k, v := range acct.Storage {
|
||||
@@ -227,7 +229,7 @@ func (t *BlockTest) InsertPreState(db ethdb.Database) (*state.StateDB, error) {
|
||||
}
|
||||
}
|
||||
|
||||
root, err := statedb.Commit()
|
||||
root, err := statedb.Commit(false)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error writing state: %v", err)
|
||||
}
|
||||
|
||||
0
tests/files/ABITests/basic_abi_tests.json
Executable file → Normal file
0
tests/files/ABITests/basic_abi_tests.json
Executable file → Normal file
0
tests/files/BasicTests/blockgenesistest.json
Executable file → Normal file
0
tests/files/BasicTests/blockgenesistest.json
Executable file → Normal file
0
tests/files/BasicTests/crypto.json
Executable file → Normal file
0
tests/files/BasicTests/crypto.json
Executable file → Normal file
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user