diff --git a/.travis.yml b/.travis.yml index d0fd4b7758..383e7fb36b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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) " -upload ppa:lp-fjl/geth-ci-testing install: - go get golang.org/x/tools/cmd/cover diff --git a/Makefile b/Makefile index 6cb64e8a44..f23b32f42c 100644 --- a/Makefile +++ b/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-* diff --git a/VERSION b/VERSION index 323afbcd2a..8a3b8ac20b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.4.14 +1.4.15 diff --git a/build/ci-notes.md b/build/ci-notes.md index 989cba6dde..39375ff4c3 100644 --- a/build/ci-notes.md +++ b/build/ci-notes.md @@ -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 diff --git a/build/ci.go b/build/ci.go index 87e8b6275f..cf43969f6c 100644 --- a/build/ci.go +++ b/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) ", - "-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 ", also selects signing key - Buildnum string // build number - Distro, Commit, Time string - Executables []debExecutable - Unstable bool + Author string // "name ", 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 " } 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(), diff --git a/build/deb.changelog b/build/deb.changelog index a221f54702..83f804a833 100644 --- a/build/deb.changelog +++ b/build/deb.changelog @@ -1,5 +1,5 @@ {{.Name}} ({{.VersionString}}) {{.Distro}}; urgency=low - * git build of {{.Commit}} + * git build of {{.Env.Commit}} -- {{.Author}} {{.Time}} diff --git a/build/deb.control b/build/deb.control index 4a65c7fac1..d39393d290 100644 --- a/build/deb.control +++ b/build/deb.control @@ -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 diff --git a/build/deb.rules b/build/deb.rules index 3dfadb08d3..11efe15fc2 100644 --- a/build/deb.rules +++ b/build/deb.rules @@ -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: diff --git a/cmd/evm/main.go b/cmd/evm/main.go index aa48f6edec..04f51cecca 100644 --- a/cmd/evm/main.go +++ b/cmd/evm/main.go @@ -30,6 +30,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/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/logger/glog" "gopkg.in/urfave/cli.v1" @@ -141,7 +142,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(), diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 71a185eb5d..b7fc89d7d0 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -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 = 15 // 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 ) diff --git a/core/execution.go b/core/execution.go index 82143443c7..1bc02f7fb2 100644 --- a/core/execution.go +++ b/core/execution.go @@ -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(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(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(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(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. @@ -105,7 +105,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A // 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) @@ -135,7 +135,7 @@ func exec(env vm.Environment, caller vm.ContractRef, address, codeAddr *common.A 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. @@ -155,7 +155,7 @@ 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) diff --git a/core/state/state_object.go b/core/state/state_object.go index a54620d555..cbd50e2a36 100644 --- a/core/state/state_object.go +++ b/core/state/state_object.go @@ -75,9 +75,11 @@ 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 @@ -105,7 +107,7 @@ func NewObject(address common.Address, data Account, onDirty func(addr common.Ad if data.CodeHash == nil { data.CodeHash = emptyCodeHash } - return &StateObject{address: address, data: data, storage: make(Storage), onDirty: onDirty} + return &StateObject{address: address, data: data, cachedStorage: make(Storage), dirtyStorage: make(Storage), onDirty: onDirty} } // EncodeRLP implements rlp.Encoder. @@ -145,7 +147,7 @@ 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 } @@ -155,14 +157,16 @@ func (self *StateObject) GetState(db trie.Database, key common.Hash) common.Hash rlp.DecodeBytes(tr.Get(key[:]), &ret) value = common.BytesToHash(ret) 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 + self.cachedStorage[key] = value + self.dirtyStorage[key] = value + if self.onDirty != nil { self.onDirty(self.Address()) self.onDirty = nil @@ -172,7 +176,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 @@ -241,7 +246,8 @@ func (self *StateObject) Copy(db trie.Database, onDirty func(addr common.Address stateObject := NewObject(self.address, self.data, onDirty) stateObject.trie = self.trie stateObject.code = self.code - stateObject.storage = self.storage.Copy() + stateObject.dirtyStorage = self.dirtyStorage.Copy() + stateObject.cachedStorage = self.dirtyStorage.Copy() stateObject.remove = self.remove stateObject.dirtyCode = self.dirtyCode stateObject.deleted = self.deleted @@ -273,9 +279,9 @@ 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) { self.code = code - self.data.CodeHash = crypto.Keccak256(code) + self.data.CodeHash = codeHash[:] self.dirtyCode = true if self.onDirty != nil { self.onDirty(self.Address()) @@ -312,7 +318,7 @@ 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) } @@ -320,7 +326,7 @@ func (self *StateObject) ForEachStorage(cb func(key, value common.Hash) bool) { 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)) } } diff --git a/core/state/state_test.go b/core/state/state_test.go index fcdc385884..7b9b39e065 100644 --- a/core/state/state_test.go +++ b/core/state/state_test.go @@ -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,7 +41,7 @@ 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)) @@ -148,7 +149,7 @@ 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.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e'}), []byte{'c', 'a', 'f', 'e'}) so0.remove = false so0.deleted = false state.SetStateObject(so0) @@ -160,7 +161,7 @@ func TestSnapshot2(t *testing.T) { so1 := state.GetStateObject(stateobjaddr1) so1.SetBalance(big.NewInt(52)) so1.SetNonce(53) - so1.SetCode([]byte{'c', 'a', 'f', 'e', '2'}) + so1.SetCode(crypto.Keccak256Hash([]byte{'c', 'a', 'f', 'e', '2'}), []byte{'c', 'a', 'f', 'e', '2'}) so1.remove = true so1.deleted = true state.SetStateObject(so1) @@ -207,16 +208,16 @@ 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) } } diff --git a/core/state/statedb.go b/core/state/statedb.go index 5c51e3b59b..4204c456e0 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -24,6 +24,7 @@ import ( "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" @@ -246,6 +247,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 { @@ -283,7 +292,7 @@ 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) } } diff --git a/core/state/statedb_test.go b/core/state/statedb_test.go index 9283334597..7930b620d4 100644 --- a/core/state/statedb_test.go +++ b/core/state/statedb_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/ethereum/go-ethereum/common" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" ) @@ -40,7 +41,7 @@ func TestUpdateLeaks(t *testing.T) { obj.SetState(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}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i}), []byte{i, i, i, i, i}) } state.UpdateStateObject(obj) } @@ -70,7 +71,7 @@ func TestIntermediateLeaks(t *testing.T) { obj.SetState(common.BytesToHash([]byte{i, i, i, 0}), common.BytesToHash([]byte{i, i, i, i, 0})) } if i%3 == 0 { - obj.SetCode([]byte{i, i, i, i, i, 0}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 0}), []byte{i, i, i, i, i, 0}) } transState.UpdateStateObject(obj) @@ -82,7 +83,7 @@ func TestIntermediateLeaks(t *testing.T) { 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}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 1}), []byte{i, i, i, i, i, 1}) } transState.UpdateStateObject(obj) @@ -94,7 +95,7 @@ func TestIntermediateLeaks(t *testing.T) { 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}) + obj.SetCode(crypto.Keccak256Hash([]byte{i, i, i, i, i, 1}), []byte{i, i, i, i, i, 1}) } finalState.UpdateStateObject(obj) } diff --git a/core/state/sync_test.go b/core/state/sync_test.go index c768781a47..670e1fb1bf 100644 --- a/core/state/sync_test.go +++ b/core/state/sync_test.go @@ -54,7 +54,7 @@ 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) diff --git a/core/vm/contract.go b/core/vm/contract.go index 844d3f328e..70455a4c23 100644 --- a/core/vm/contract.go +++ b/core/vm/contract.go @@ -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 } diff --git a/core/vm/environment.go b/core/vm/environment.go index 4bd03de7eb..daf6fb90d5 100644 --- a/core/vm/environment.go +++ b/core/vm/environment.go @@ -94,6 +94,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) @@ -118,7 +119,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 } diff --git a/core/vm/jit_test.go b/core/vm/jit_test.go index 403c15a8db..045b00001a 100644 --- a/core/vm/jit_test.go +++ b/core/vm/jit_test.go @@ -135,7 +135,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) { diff --git a/core/vm/logger_test.go b/core/vm/logger_test.go index 1445698659..059368155e 100644 --- a/core/vm/logger_test.go +++ b/core/vm/logger_test.go @@ -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 } diff --git a/core/vm/runtime/runtime.go b/core/vm/runtime/runtime.go index 309d508c3d..6d980cb327 100644 --- a/core/vm/runtime/runtime.go +++ b/core/vm/runtime/runtime.go @@ -104,7 +104,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( diff --git a/core/vm/vm.go b/core/vm/vm.go index 0f93715d6f..05bd2710aa 100644 --- a/core/vm/vm.go +++ b/core/vm/vm.go @@ -79,10 +79,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 diff --git a/internal/build/env.go b/internal/build/env.go new file mode 100644 index 0000000000..3f63239ca1 --- /dev/null +++ b/internal/build/env.go @@ -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 . + +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"), + 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 +} diff --git a/internal/build/util.go b/internal/build/util.go index eead824b25..be41efed75 100644 --- a/internal/build/util.go +++ b/internal/build/util.go @@ -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) diff --git a/light/state_test.go b/light/state_test.go index d7014a2dcc..d4fe950229 100644 --- a/light/state_test.go +++ b/light/state_test.go @@ -23,6 +23,7 @@ import ( "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/core/state" + "github.com/ethereum/go-ethereum/crypto" "github.com/ethereum/go-ethereum/ethdb" "github.com/ethereum/go-ethereum/trie" "golang.org/x/net/context" @@ -60,7 +61,7 @@ func makeTestState() (common.Hash, ethdb.Database) { so.SetNonce(100) } so.AddBalance(big.NewInt(int64(i))) - so.SetCode([]byte{i, i, i}) + so.SetCode(crypto.Keccak256Hash([]byte{i, i, i}), []byte{i, i, i}) so.UpdateRoot(sdb) st.UpdateStateObject(so) } diff --git a/tests/block_test_util.go b/tests/block_test_util.go index 30bed34022..947bec647d 100644 --- a/tests/block_test_util.go +++ b/tests/block_test_util.go @@ -31,6 +31,7 @@ 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" @@ -219,7 +220,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 { diff --git a/tests/util.go b/tests/util.go index df3432bdfa..48aee40697 100644 --- a/tests/util.go +++ b/tests/util.go @@ -108,12 +108,13 @@ func StateObjectFromAccount(db ethdb.Database, addr string, account Account, onD account.Code = account.Code[2:] } code := common.Hex2Bytes(account.Code) + codeHash := crypto.Keccak256Hash(code) obj := state.NewObject(common.HexToAddress(addr), state.Account{ Balance: common.Big(account.Balance), - CodeHash: crypto.Keccak256(code), + CodeHash: codeHash[:], Nonce: common.Big(account.Nonce).Uint64(), }, onDirty) - obj.SetCode(code) + obj.SetCode(codeHash, code) return obj } diff --git a/trie/trie.go b/trie/trie.go index 93e189e2e9..baedf7deac 100644 --- a/trie/trie.go +++ b/trie/trie.go @@ -374,6 +374,9 @@ func (t *Trie) delete(n node, prefix, key []byte) (bool, node, error) { // n still contains at least two values and cannot be reduced. return true, n, nil + case valueNode: + return true, nil, nil + case nil: return false, nil, nil diff --git a/trie/trie_test.go b/trie/trie_test.go index 5a3ea1be98..87a7ec258d 100644 --- a/trie/trie_test.go +++ b/trie/trie_test.go @@ -21,8 +21,11 @@ import ( "encoding/binary" "fmt" "io/ioutil" + "math/rand" "os" + "reflect" "testing" + "testing/quick" "github.com/davecgh/go-spew/spew" "github.com/ethereum/go-ethereum/common" @@ -297,41 +300,6 @@ func TestReplication(t *testing.T) { } } -func paranoiaCheck(t1 *Trie) (bool, *Trie) { - t2 := new(Trie) - it := NewIterator(t1) - for it.Next() { - t2.Update(it.Key, it.Value) - } - return t2.Hash() == t1.Hash(), t2 -} - -func TestParanoia(t *testing.T) { - t.Skip() - trie := newEmpty() - - vals := []struct{ k, v string }{ - {"do", "verb"}, - {"ether", "wookiedoo"}, - {"horse", "stallion"}, - {"shaman", "horse"}, - {"doge", "coin"}, - {"ether", ""}, - {"dog", "puppy"}, - {"shaman", ""}, - {"somethingveryoddindeedthis is", "myothernodedata"}, - } - for _, val := range vals { - updateString(trie, val.k, val.v) - } - trie.Commit() - - ok, t2 := paranoiaCheck(trie) - if !ok { - t.Errorf("trie paranoia check failed %x %x", trie.Hash(), t2.Hash()) - } -} - // Not an actual test func TestOutput(t *testing.T) { t.Skip() @@ -356,7 +324,128 @@ func TestLargeValue(t *testing.T) { trie.Update([]byte("key1"), []byte{99, 99, 99, 99}) trie.Update([]byte("key2"), bytes.Repeat([]byte{1}, 32)) trie.Hash() +} + +type randTestStep struct { + op int + key []byte // for opUpdate, opDelete, opGet + value []byte // for opUpdate +} + +type randTest []randTestStep + +const ( + opUpdate = iota + opDelete + opGet + opCommit + opHash + opReset + opItercheckhash + opMax // boundary value, not an actual op +) + +func (randTest) Generate(r *rand.Rand, size int) reflect.Value { + var allKeys [][]byte + genKey := func() []byte { + if len(allKeys) < 2 || r.Intn(100) < 10 { + // new key + key := make([]byte, r.Intn(50)) + randRead(r, key) + allKeys = append(allKeys, key) + return key + } + // use existing key + return allKeys[r.Intn(len(allKeys))] + } + + var steps randTest + for i := 0; i < size; i++ { + step := randTestStep{op: r.Intn(opMax)} + switch step.op { + case opUpdate: + step.key = genKey() + step.value = make([]byte, 8) + binary.BigEndian.PutUint64(step.value, uint64(i)) + case opGet, opDelete: + step.key = genKey() + } + steps = append(steps, step) + } + return reflect.ValueOf(steps) +} + +// rand.Rand provides a Read method in Go 1.7 and later, but +// we can't use it yet. +func randRead(r *rand.Rand, b []byte) { + pos := 0 + val := 0 + for n := 0; n < len(b); n++ { + if pos == 0 { + val = r.Int() + pos = 7 + } + b[n] = byte(val) + val >>= 8 + pos-- + } +} +func runRandTest(rt randTest) bool { + db, _ := ethdb.NewMemDatabase() + tr, _ := New(common.Hash{}, db) + values := make(map[string]string) // tracks content of the trie + + for _, step := range rt { + switch step.op { + case opUpdate: + tr.Update(step.key, step.value) + values[string(step.key)] = string(step.value) + case opDelete: + tr.Delete(step.key) + delete(values, string(step.key)) + case opGet: + v := tr.Get(step.key) + want := values[string(step.key)] + if string(v) != want { + fmt.Printf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want) + return false + } + case opCommit: + if _, err := tr.Commit(); err != nil { + panic(err) + } + case opHash: + tr.Hash() + case opReset: + hash, err := tr.Commit() + if err != nil { + panic(err) + } + newtr, err := New(hash, db) + if err != nil { + panic(err) + } + tr = newtr + case opItercheckhash: + checktr, _ := New(common.Hash{}, nil) + it := tr.Iterator() + for it.Next() { + checktr.Update(it.Key, it.Value) + } + if tr.Hash() != checktr.Hash() { + fmt.Println("hashes not equal") + return false + } + } + } + return true +} + +func TestRandom(t *testing.T) { + if err := quick.Check(runRandTest, nil); err != nil { + t.Fatal(err) + } } func BenchmarkGet(b *testing.B) { benchGet(b, false) }