mirror of
https://github.com/arnaucube/go-ethereum.git
synced 2026-03-01 06:26:46 +01:00
Compare commits
128 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff07d54843 | ||
|
|
e53879328c | ||
|
|
b792412d31 | ||
|
|
49c6f1053c | ||
|
|
4d960f6dc6 | ||
|
|
8941665896 | ||
|
|
9cc0f60666 | ||
|
|
fdb8edf5ea | ||
|
|
9ba9fe818d | ||
|
|
92224d27b1 | ||
|
|
157a4bd926 | ||
|
|
29d6881112 | ||
|
|
e2692921e1 | ||
|
|
b63138c3ec | ||
|
|
07311f3157 | ||
|
|
17637ed1bb | ||
|
|
f15828e901 | ||
|
|
dadd689359 | ||
|
|
b750cab56a | ||
|
|
485748c416 | ||
|
|
080699f7df | ||
|
|
8e35f54931 | ||
|
|
d44f1a77ee | ||
|
|
4181046488 | ||
|
|
d7c398b638 | ||
|
|
5f5d0aa4ff | ||
|
|
9f1520b4c0 | ||
|
|
a98e8c0889 | ||
|
|
ee445a2ba4 | ||
|
|
b2c226cb7d | ||
|
|
13614f4e1c | ||
|
|
4f9ccdd70f | ||
|
|
4e36b1e3da | ||
|
|
f12f8a6c14 | ||
|
|
c57c54ce96 | ||
|
|
c8130df1d9 | ||
|
|
af8a742d00 | ||
|
|
e67500aa15 | ||
|
|
a6d3bf6fc3 | ||
|
|
3e617f3cd6 | ||
|
|
0fe35b907a | ||
|
|
3fc7c97827 | ||
|
|
7f79d249a6 | ||
|
|
f138374027 | ||
|
|
f52a1ae849 | ||
|
|
3bc0fe1ee3 | ||
|
|
fa0cc27400 | ||
|
|
4cb29bde2e | ||
|
|
2dcf75a722 | ||
|
|
671fd94e25 | ||
|
|
717d2f6f9e | ||
|
|
8cb95cb916 | ||
|
|
56b446190a | ||
|
|
86f9e836be | ||
|
|
a90a170361 | ||
|
|
889a5e0cf1 | ||
|
|
9f8bc00cf5 | ||
|
|
3363a1c227 | ||
|
|
fc9939c4e1 | ||
|
|
7267f796e6 | ||
|
|
7dfeceb8cc | ||
|
|
3807e520ec | ||
|
|
7625b1a4f4 | ||
|
|
1fc5cc1b59 | ||
|
|
61ccb43487 | ||
|
|
bf24b120d7 | ||
|
|
b70acf3c5b | ||
|
|
b5be6b72cb | ||
|
|
318ad3c1e4 | ||
|
|
e949a2ed2f | ||
|
|
8d0108fc5d | ||
|
|
ba41efa8a0 | ||
|
|
f81660b6db | ||
|
|
91bceb4ace | ||
|
|
be746628c7 | ||
|
|
ec5f531f4b | ||
|
|
37e5816bcd | ||
|
|
665bb43a4c | ||
|
|
e4bf004560 | ||
|
|
1609df3275 | ||
|
|
24f288770e | ||
|
|
65e6319b12 | ||
|
|
ec75953f50 | ||
|
|
801a13f791 | ||
|
|
eea8d6aa96 | ||
|
|
2b9cd71d67 | ||
|
|
5df83e3bd9 | ||
|
|
6061707371 | ||
|
|
20899c05a4 | ||
|
|
4c8c5e2f74 | ||
|
|
d1a95c643e | ||
|
|
9c3ea0d32d | ||
|
|
67e0894d9e | ||
|
|
6cc87a31c6 | ||
|
|
b8c766a9c5 | ||
|
|
66441c9b4b | ||
|
|
18d51d1de8 | ||
|
|
01d5fc670b | ||
|
|
0f1cbfd3da | ||
|
|
586f10ecb1 | ||
|
|
978737f5d5 | ||
|
|
fa0e057f8a | ||
|
|
bca7bfa927 | ||
|
|
12d654a6fc | ||
|
|
8e64e4383c | ||
|
|
f59d8cde26 | ||
|
|
94c0519be2 | ||
|
|
529c502876 | ||
|
|
c04c8f10f0 | ||
|
|
e05d35e6e0 | ||
|
|
e1e2df656a | ||
|
|
f7da5b29f0 | ||
|
|
2b4c236773 | ||
|
|
a8ca75738a | ||
|
|
aad4890082 | ||
|
|
e5edd3b983 | ||
|
|
a47341cf96 | ||
|
|
e46bda5093 | ||
|
|
a98d1d67d6 | ||
|
|
ba2884f343 | ||
|
|
1d80155d5e | ||
|
|
a0e42aa4e2 | ||
|
|
92959cd4ef | ||
|
|
2c802399c3 | ||
|
|
8ed72a8470 | ||
|
|
0d9a8207d6 | ||
|
|
04edbb0703 | ||
|
|
e1c1fce92c |
4
.github/CONTRIBUTING.md
vendored
4
.github/CONTRIBUTING.md
vendored
@@ -9,9 +9,7 @@ and help.
|
||||
|
||||
If you'd like to contribute to go-ethereum please fork, fix, commit and
|
||||
send a pull request. Commits which do not comply with the coding standards
|
||||
are ignored (use gofmt!). If you send pull requests make absolute sure that you
|
||||
commit on the `develop` branch and that you do not merge to master.
|
||||
Commits that are directly based on master are simply ignored.
|
||||
are ignored (use gofmt!).
|
||||
|
||||
See [Developers' Guide](https://github.com/ethereum/go-ethereum/wiki/Developers'-Guide)
|
||||
for more details on configuring your environment, testing, and
|
||||
|
||||
@@ -13,15 +13,15 @@ matrix:
|
||||
go: 1.6.2
|
||||
- os: linux
|
||||
dist: trusty
|
||||
go: 1.7
|
||||
go: 1.7.4
|
||||
- os: osx
|
||||
go: 1.7
|
||||
go: 1.7.4
|
||||
|
||||
# This builder does the Ubuntu PPA and Linux Azure uploads
|
||||
- os: linux
|
||||
dist: trusty
|
||||
sudo: required
|
||||
go: 1.7
|
||||
go: 1.7.4
|
||||
env:
|
||||
- ubuntu-ppa
|
||||
- azure-linux
|
||||
@@ -55,7 +55,7 @@ matrix:
|
||||
|
||||
# This builder does the OSX Azure, Android Maven and Azure and iOS CocoaPods and Azure uploads
|
||||
- os: osx
|
||||
go: 1.7
|
||||
go: 1.7.4
|
||||
env:
|
||||
- azure-osx
|
||||
- mobile
|
||||
|
||||
25
Makefile
25
Makefile
@@ -2,12 +2,11 @@
|
||||
# with Go source code. If you know what GOPATH is then you probably
|
||||
# don't need to bother with make.
|
||||
|
||||
.PHONY: geth geth-cross evm all test clean
|
||||
.PHONY: geth android ios geth-cross evm all test clean
|
||||
.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
|
||||
.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
|
||||
.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
|
||||
.PHONY: geth-windows geth-windows-386 geth-windows-amd64
|
||||
.PHONY: geth-android geth-ios
|
||||
|
||||
GOBIN = build/bin
|
||||
GO ?= latest
|
||||
@@ -20,11 +19,21 @@ geth:
|
||||
evm:
|
||||
build/env.sh go run build/ci.go install ./cmd/evm
|
||||
@echo "Done building."
|
||||
@echo "Run \"$(GOBIN)/evm to start the evm."
|
||||
@echo "Run \"$(GOBIN)/evm\" to start the evm."
|
||||
|
||||
all:
|
||||
build/env.sh go run build/ci.go install
|
||||
|
||||
android:
|
||||
build/env.sh go run build/ci.go aar --local
|
||||
@echo "Done building."
|
||||
@echo "Import \"$(GOBIN)/geth.aar\" to use the library."
|
||||
|
||||
ios:
|
||||
build/env.sh go run build/ci.go xcode --local
|
||||
@echo "Done building."
|
||||
@echo "Import \"$(GOBIN)/Geth.framework\" to use the library."
|
||||
|
||||
test: all
|
||||
build/env.sh go run build/ci.go test
|
||||
|
||||
@@ -112,13 +121,3 @@ geth-windows-amd64:
|
||||
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
|
||||
@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
|
||||
@echo "iOS framework cross compilation done:"
|
||||
@ls -ld $(GOBIN)/geth-ios-*
|
||||
|
||||
@@ -39,9 +39,7 @@ The go-ethereum project comes with several wrappers/executables found in the `cm
|
||||
| `evm` | Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode. Its purpose is to allow insolated, fine-grained debugging of EVM opcodes (e.g. `evm --code 60ff60ff --debug`). |
|
||||
| `gethrpctest` | Developer utility tool to support our [ethereum/rpc-test](https://github.com/ethereum/rpc-tests) test suite which validates baseline conformity to the [Ethereum JSON RPC](https://github.com/ethereum/wiki/wiki/JSON-RPC) specs. Please see the [test suite's readme](https://github.com/ethereum/rpc-tests/blob/master/README.md) for details. |
|
||||
| `rlpdump` | Developer utility tool to convert binary RLP ([Recursive Length Prefix](https://github.com/ethereum/wiki/wiki/RLP)) dumps (data encoding used by the Ethereum protocol both network as well as consensus wise) to user friendlier hierarchical representation (e.g. `rlpdump --hex CE0183FFFFFFC4C304050583616263`). |
|
||||
| `bzzd` | swarm daemon. This is the entrypoint for the swarm network. `bzzd --help` for command line options. See http://swarm-guide.readthedocs.io for swarm documentation. |
|
||||
| `bzzup` | swarm command line file uploader. `bzzup --help` for command line options |
|
||||
| `bzzhash` | command to calculate the swarm hash of a file or directory. `bzzhash --help` for command line options |
|
||||
| `swarm` | swarm daemon and tools. This is the entrypoint for the swarm network. `swarm --help` for command line options and subcommands. See https://swarm-guide.readthedocs.io for swarm documentation. |
|
||||
|
||||
## Running geth
|
||||
|
||||
|
||||
@@ -225,7 +225,11 @@ func (b *SimulatedBackend) callContract(ctx context.Context, call ethereum.CallM
|
||||
from.SetBalance(common.MaxBig)
|
||||
// Execute the call.
|
||||
msg := callmsg{call}
|
||||
vmenv := core.NewEnv(statedb, chainConfig, b.blockchain, msg, block.Header(), vm.Config{})
|
||||
|
||||
evmContext := core.NewEVMContext(msg, block.Header(), b.blockchain)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEnvironment(evmContext, statedb, chainConfig, vm.Config{})
|
||||
gaspool := new(core.GasPool).AddGas(common.MaxBig)
|
||||
ret, gasUsed, _, err := core.NewStateTransition(vmenv, msg, gaspool).TransitionDb()
|
||||
return ret, gasUsed, err
|
||||
|
||||
@@ -225,7 +225,7 @@ func (ac *addrCache) scan() ([]Account, error) {
|
||||
buf = new(bufio.Reader)
|
||||
addrs []Account
|
||||
keyJSON struct {
|
||||
Address common.Address `json:"address"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
)
|
||||
for _, fi := range files {
|
||||
@@ -241,15 +241,16 @@ func (ac *addrCache) scan() ([]Account, error) {
|
||||
}
|
||||
buf.Reset(fd)
|
||||
// Parse the address.
|
||||
keyJSON.Address = common.Address{}
|
||||
keyJSON.Address = ""
|
||||
err = json.NewDecoder(buf).Decode(&keyJSON)
|
||||
addr := common.HexToAddress(keyJSON.Address)
|
||||
switch {
|
||||
case err != nil:
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: %v", path, err)
|
||||
case (keyJSON.Address == common.Address{}):
|
||||
case (addr == common.Address{}):
|
||||
glog.V(logger.Debug).Infof("can't decode key %s: missing or zero address", path)
|
||||
default:
|
||||
addrs = append(addrs, Account{Address: keyJSON.Address, File: path})
|
||||
addrs = append(addrs, Account{Address: addr, File: path})
|
||||
}
|
||||
fd.Close()
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris windows
|
||||
// +build darwin,!ios freebsd linux,!arm64 netbsd solaris
|
||||
|
||||
package accounts
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
// 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/>.
|
||||
|
||||
// +build ios linux,arm64 !darwin,!freebsd,!linux,!netbsd,!solaris,!windows
|
||||
// +build ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
|
||||
|
||||
// This is the fallback implementation of directory watching.
|
||||
// It is used on unsupported platforms.
|
||||
|
||||
11
appveyor.yml
11
appveyor.yml
@@ -22,19 +22,18 @@ environment:
|
||||
|
||||
install:
|
||||
- rmdir C:\go /s /q
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.3.windows-amd64.zip
|
||||
- 7z x go1.7.3.windows-amd64.zip -y -oC:\ > NUL
|
||||
- appveyor DownloadFile https://storage.googleapis.com/golang/go1.7.4.windows-%GETH_ARCH%.zip
|
||||
- 7z x go1.7.4.windows-%GETH_ARCH%.zip -y -oC:\ > NUL
|
||||
- go version
|
||||
- gcc --version
|
||||
|
||||
build_script:
|
||||
- go run build\ci.go install -arch %GETH_ARCH%
|
||||
- go run build\ci.go install
|
||||
|
||||
after_build:
|
||||
- go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go archive -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
- go run build\ci.go nsis -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
|
||||
|
||||
test_script:
|
||||
- set GOARCH=%GETH_ARCH%
|
||||
- set CGO_ENABLED=1
|
||||
- go run build\ci.go test -vet -coverage
|
||||
|
||||
53
build/ci.go
53
build/ci.go
@@ -29,8 +29,8 @@ Available commands are:
|
||||
importkeys -- imports signing keys from env
|
||||
debsrc [ -signer key-id ] [ -upload dest ] -- creates a debian source package
|
||||
nsis -- creates a Windows NSIS installer
|
||||
aar [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
||||
xcode [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
||||
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
|
||||
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
|
||||
xgo [ options ] -- cross builds according to options
|
||||
|
||||
For all commands, -n prevents execution of external programs (dry run mode).
|
||||
@@ -72,6 +72,7 @@ var (
|
||||
executablePath("abigen"),
|
||||
executablePath("evm"),
|
||||
executablePath("geth"),
|
||||
executablePath("swarm"),
|
||||
executablePath("rlpdump"),
|
||||
}
|
||||
|
||||
@@ -89,6 +90,10 @@ var (
|
||||
Name: "evm",
|
||||
Description: "Developer utility version of the EVM (Ethereum Virtual Machine) that is capable of running bytecode snippets within a configurable environment and execution mode.",
|
||||
},
|
||||
{
|
||||
Name: "swarm",
|
||||
Description: "Ethereum Swarm daemon and tools",
|
||||
},
|
||||
{
|
||||
Name: "abigen",
|
||||
Description: "Source code generator to convert Ethereum contract definitions into easy to use, compile-time type-safe Go packages.",
|
||||
@@ -97,7 +102,8 @@ var (
|
||||
|
||||
// Distros for which packages are created.
|
||||
// Note: vivid is unsupported because there is no golang-1.6 package for it.
|
||||
debDistros = []string{"trusty", "wily", "xenial", "yakkety"}
|
||||
// Note: wily is unsupported because it was officially deprecated on lanchpad.
|
||||
debDistros = []string{"trusty", "xenial", "yakkety"}
|
||||
)
|
||||
|
||||
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
|
||||
@@ -459,7 +465,7 @@ func makeWorkdir(wdflag string) string {
|
||||
}
|
||||
|
||||
func isUnstableBuild(env build.Environment) bool {
|
||||
if env.Branch != "master" && env.Tag != "" {
|
||||
if env.Tag != "" {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@@ -623,6 +629,7 @@ func doWindowsInstaller(cmdline []string) {
|
||||
build.Render("build/nsis.geth.nsi", filepath.Join(*workdir, "geth.nsi"), 0644, nil)
|
||||
build.Render("build/nsis.install.nsh", filepath.Join(*workdir, "install.nsh"), 0644, templateData)
|
||||
build.Render("build/nsis.uninstall.nsh", filepath.Join(*workdir, "uninstall.nsh"), 0644, allTools)
|
||||
build.Render("build/nsis.pathupdate.nsh", filepath.Join(*workdir, "PathUpdate.nsh"), 0644, nil)
|
||||
build.Render("build/nsis.envvarupdate.nsh", filepath.Join(*workdir, "EnvVarUpdate.nsh"), 0644, nil)
|
||||
build.CopyFile(filepath.Join(*workdir, "SimpleFC.dll"), "build/nsis.simplefc.dll", 0755)
|
||||
build.CopyFile(filepath.Join(*workdir, "COPYING"), "COPYING", 0755)
|
||||
@@ -654,6 +661,7 @@ func doWindowsInstaller(cmdline []string) {
|
||||
|
||||
func doAndroidArchive(cmdline []string) {
|
||||
var (
|
||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. ANDROID_SIGNING_KEY)`)
|
||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "https://oss.sonatype.org")`)
|
||||
upload = flag.String("upload", "", `Destination to upload the archive (usually "gethstore/builds")`)
|
||||
@@ -666,6 +674,11 @@ func doAndroidArchive(cmdline []string) {
|
||||
build.MustRun(gomobileTool("init"))
|
||||
build.MustRun(gomobileTool("bind", "--target", "android", "--javapkg", "org.ethereum", "-v", "github.com/ethereum/go-ethereum/mobile"))
|
||||
|
||||
if *local {
|
||||
// If we're building locally, copy bundle to build dir and skip Maven
|
||||
os.Rename("geth.aar", filepath.Join(GOBIN, "geth.aar"))
|
||||
return
|
||||
}
|
||||
meta := newMavenMetadata(env)
|
||||
build.Render("build/mvn.pom", meta.Package+".pom", 0755, meta)
|
||||
|
||||
@@ -744,7 +757,7 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
|
||||
continue
|
||||
}
|
||||
// Split the author and insert as a contributor
|
||||
re := regexp.MustCompile("([^<]+) <(.+>)")
|
||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
||||
parts := re.FindStringSubmatch(line)
|
||||
if len(parts) == 3 {
|
||||
contribs = append(contribs, mavenContributor{Name: parts[1], Email: parts[2]})
|
||||
@@ -768,6 +781,7 @@ func newMavenMetadata(env build.Environment) mavenMetadata {
|
||||
|
||||
func doXCodeFramework(cmdline []string) {
|
||||
var (
|
||||
local = flag.Bool("local", false, `Flag whether we're only doing a local build (skip Maven artifacts)`)
|
||||
signer = flag.String("signer", "", `Environment variable holding the signing key (e.g. IOS_SIGNING_KEY)`)
|
||||
deploy = flag.String("deploy", "", `Destination to deploy the archive (usually "trunk")`)
|
||||
upload = flag.String("upload", "", `Destination to upload the archives (usually "gethstore/builds")`)
|
||||
@@ -777,12 +791,19 @@ func doXCodeFramework(cmdline []string) {
|
||||
|
||||
// Build the iOS XCode framework
|
||||
build.MustRun(goTool("get", "golang.org/x/mobile/cmd/gomobile"))
|
||||
build.MustRun(gomobileTool("init"))
|
||||
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "i", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
|
||||
if *local {
|
||||
// If we're building locally, use the build folder and stop afterwards
|
||||
bind.Dir, _ = filepath.Abs(GOBIN)
|
||||
build.MustRun(bind)
|
||||
return
|
||||
}
|
||||
archive := "geth-" + archiveBasename("ios", env)
|
||||
if err := os.Mkdir(archive, os.ModePerm); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
bind := gomobileTool("bind", "--target", "ios", "--tags", "ios", "--prefix", "GE", "-v", "github.com/ethereum/go-ethereum/mobile")
|
||||
bind.Dir, _ = filepath.Abs(archive)
|
||||
build.MustRun(bind)
|
||||
build.MustRunCommand("tar", "-zcvf", archive+".tar.gz", archive)
|
||||
@@ -796,16 +817,16 @@ func doXCodeFramework(cmdline []string) {
|
||||
}
|
||||
// Prepare and upload a PodSpec to CocoaPods
|
||||
if *deploy != "" {
|
||||
meta := newPodMetadata(env)
|
||||
build.Render("build/pod.podspec", meta.Name+".podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", meta.Name+".podspec", "--allow-warnings", "--verbose")
|
||||
meta := newPodMetadata(env, archive)
|
||||
build.Render("build/pod.podspec", "Geth.podspec", 0755, meta)
|
||||
build.MustRunCommand("pod", *deploy, "push", "Geth.podspec", "--allow-warnings", "--verbose")
|
||||
}
|
||||
}
|
||||
|
||||
type podMetadata struct {
|
||||
Name string
|
||||
Version string
|
||||
Commit string
|
||||
Archive string
|
||||
Contributors []podContributor
|
||||
}
|
||||
|
||||
@@ -814,7 +835,7 @@ type podContributor struct {
|
||||
Email string
|
||||
}
|
||||
|
||||
func newPodMetadata(env build.Environment) podMetadata {
|
||||
func newPodMetadata(env build.Environment, archive string) podMetadata {
|
||||
// Collect the list of authors from the repo root
|
||||
contribs := []podContributor{}
|
||||
if authors, err := os.Open("AUTHORS"); err == nil {
|
||||
@@ -828,20 +849,20 @@ func newPodMetadata(env build.Environment) podMetadata {
|
||||
continue
|
||||
}
|
||||
// Split the author and insert as a contributor
|
||||
re := regexp.MustCompile("([^<]+) <(.+>)")
|
||||
re := regexp.MustCompile("([^<]+) <(.+)>")
|
||||
parts := re.FindStringSubmatch(line)
|
||||
if len(parts) == 3 {
|
||||
contribs = append(contribs, podContributor{Name: parts[1], Email: parts[2]})
|
||||
}
|
||||
}
|
||||
}
|
||||
name := "Geth"
|
||||
version := build.VERSION()
|
||||
if isUnstableBuild(env) {
|
||||
name += "Develop"
|
||||
version += "-unstable." + env.Buildnum
|
||||
}
|
||||
return podMetadata{
|
||||
Name: name,
|
||||
Version: archiveVersion(env),
|
||||
Archive: archive,
|
||||
Version: version,
|
||||
Commit: env.Commit,
|
||||
Contributors: contribs,
|
||||
}
|
||||
|
||||
@@ -17,8 +17,12 @@
|
||||
#
|
||||
# Requirements:
|
||||
# - NSIS, http://nsis.sourceforge.net/Main_Page
|
||||
# - NSIS Large Strings build, http://nsis.sourceforge.net/Special_Builds
|
||||
# - SFP, http://nsis.sourceforge.net/NSIS_Simple_Firewall_Plugin (put dll in NSIS\Plugins\x86-ansi)
|
||||
#
|
||||
# After intalling NSIS extra the NSIS Large Strings build zip and replace the makensis.exe and the
|
||||
# files found in Stub.
|
||||
#
|
||||
# based on: http://nsis.sourceforge.net/A_simple_installer_with_start_menu_shortcut_and_uninstaller
|
||||
#
|
||||
# TODO:
|
||||
@@ -37,6 +41,7 @@ RequestExecutionLevel admin
|
||||
SetCompressor /SOLID lzma
|
||||
|
||||
!include LogicLib.nsh
|
||||
!include PathUpdate.nsh
|
||||
!include EnvVarUpdate.nsh
|
||||
|
||||
!macro VerifyUserIsAdmin
|
||||
|
||||
@@ -37,8 +37,9 @@ Section "Geth" GETH_IDX
|
||||
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
|
||||
${EnvVarUpdate} $0 "ETHEREUM_SOCKET" "A" "HKLM" "\\.\pipe\geth.ipc"
|
||||
|
||||
# Add geth to PATH
|
||||
${EnvVarUpdate} $0 "PATH" "A" "HKLM" $INSTDIR
|
||||
# Add instdir to PATH
|
||||
Push "$INSTDIR"
|
||||
Call AddToPath
|
||||
SectionEnd
|
||||
|
||||
# Install optional develop tools.
|
||||
|
||||
153
build/nsis.pathupdate.nsh
Normal file
153
build/nsis.pathupdate.nsh
Normal file
@@ -0,0 +1,153 @@
|
||||
!include "WinMessages.nsh"
|
||||
|
||||
; see https://support.microsoft.com/en-us/kb/104011
|
||||
!define Environ 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"'
|
||||
; HKEY_LOCAL_MACHINE = 0x80000002
|
||||
|
||||
; AddToPath - Appends dir to PATH
|
||||
; (does not work on Win9x/ME)
|
||||
;
|
||||
; Usage:
|
||||
; Push "dir"
|
||||
; Call AddToPath
|
||||
Function AddToPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
|
||||
; NSIS ReadRegStr returns empty string on string overflow
|
||||
; Native calls are used here to check actual length of PATH
|
||||
; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
|
||||
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
|
||||
IntCmp $4 0 0 done done
|
||||
|
||||
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
|
||||
; RegCloseKey($3)
|
||||
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
|
||||
System::Call "advapi32::RegCloseKey(i $3)"
|
||||
|
||||
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
|
||||
DetailPrint "AddToPath: original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
Goto done
|
||||
|
||||
IntCmp $4 0 +5 ; $4 != NO_ERROR
|
||||
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
|
||||
DetailPrint "AddToPath: unexpected error code $4"
|
||||
Goto done
|
||||
StrCpy $1 ""
|
||||
|
||||
; Check if already in PATH
|
||||
Push "$1;"
|
||||
Push "$0;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" 0 done
|
||||
Push "$1;"
|
||||
Push "$0\;"
|
||||
Call StrStr
|
||||
Pop $2
|
||||
StrCmp $2 "" 0 done
|
||||
|
||||
; Prevent NSIS string overflow
|
||||
StrLen $2 $0
|
||||
StrLen $3 $1
|
||||
IntOp $2 $2 + $3
|
||||
IntOp $2 $2 + 2 ; $2 = strlen(dir) + strlen(PATH) + sizeof(";")
|
||||
IntCmp $2 ${NSIS_MAX_STRLEN} +4 +4 0
|
||||
DetailPrint "AddToPath: new length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK "PATH not updated, new length $2 > ${NSIS_MAX_STRLEN}."
|
||||
Goto done
|
||||
|
||||
; Append dir to PATH
|
||||
DetailPrint "Add to PATH: $0"
|
||||
StrCpy $2 $1 1 -1
|
||||
StrCmp $2 ";" 0 +2
|
||||
StrCpy $1 $1 -1 ; remove trailing ';'
|
||||
StrCmp $1 "" +2 ; no leading ';'
|
||||
StrCpy $0 "$1;$0"
|
||||
|
||||
WriteRegExpandStr ${Environ} "PATH" $0
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
|
||||
; RemoveFromPath - Removes dir from PATH
|
||||
;
|
||||
; Usage:
|
||||
; Push "dir"
|
||||
; Call RemoveFromPath
|
||||
Function un.RemoveFromPath
|
||||
Exch $0
|
||||
Push $1
|
||||
Push $2
|
||||
Push $3
|
||||
Push $4
|
||||
Push $5
|
||||
Push $6
|
||||
|
||||
; NSIS ReadRegStr returns empty string on string overflow
|
||||
; Native calls are used here to check actual length of PATH
|
||||
; $4 = RegOpenKey(HKEY_LOCAL_MACHINE, "SYSTEM\CurrentControlSet\Control\Session Manager\Environment", &$3)
|
||||
System::Call "advapi32::RegOpenKey(i 0x80000002, t'SYSTEM\CurrentControlSet\Control\Session Manager\Environment', *i.r3) i.r4"
|
||||
IntCmp $4 0 0 done done
|
||||
|
||||
; $4 = RegQueryValueEx($3, "PATH", (DWORD*)0, (DWORD*)0, &$1, ($2=NSIS_MAX_STRLEN, &$2))
|
||||
; RegCloseKey($3)
|
||||
System::Call "advapi32::RegQueryValueEx(i $3, t'PATH', i 0, i 0, t.r1, *i ${NSIS_MAX_STRLEN} r2) i.r4"
|
||||
System::Call "advapi32::RegCloseKey(i $3)"
|
||||
|
||||
IntCmp $4 234 0 +4 +4 ; $4 == ERROR_MORE_DATA
|
||||
DetailPrint "RemoveFromPath: original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
MessageBox MB_OK "PATH not updated, original length $2 > ${NSIS_MAX_STRLEN}"
|
||||
Goto done
|
||||
|
||||
IntCmp $4 0 +5 ; $4 != NO_ERROR
|
||||
IntCmp $4 2 +3 ; $4 != ERROR_FILE_NOT_FOUND
|
||||
DetailPrint "RemoveFromPath: unexpected error code $4"
|
||||
Goto done
|
||||
StrCpy $1 ""
|
||||
|
||||
; length < ${NSIS_MAX_STRLEN} -> ReadRegStr can be used
|
||||
ReadRegStr $1 ${Environ} "PATH"
|
||||
StrCpy $5 $1 1 -1
|
||||
StrCmp $5 ";" +2
|
||||
StrCpy $1 "$1;" ; ensure trailing ';'
|
||||
Push $1
|
||||
Push "$0;"
|
||||
Call un.StrStr
|
||||
Pop $2 ; pos of our dir
|
||||
StrCmp $2 "" done
|
||||
|
||||
DetailPrint "Remove from PATH: $0"
|
||||
StrLen $3 "$0;"
|
||||
StrLen $4 $2
|
||||
StrCpy $5 $1 -$4 ; $5 is now the part before the path to remove
|
||||
StrCpy $6 $2 "" $3 ; $6 is now the part after the path to remove
|
||||
StrCpy $3 "$5$6"
|
||||
StrCpy $5 $3 1 -1
|
||||
StrCmp $5 ";" 0 +2
|
||||
StrCpy $3 $3 -1 ; remove trailing ';'
|
||||
WriteRegExpandStr ${Environ} "PATH" $3
|
||||
SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000
|
||||
|
||||
done:
|
||||
Pop $6
|
||||
Pop $5
|
||||
Pop $4
|
||||
Pop $3
|
||||
Pop $2
|
||||
Pop $1
|
||||
Pop $0
|
||||
FunctionEnd
|
||||
|
||||
|
||||
@@ -25,7 +25,8 @@ Section "Uninstall"
|
||||
${un.EnvVarUpdate} $0 "ETHEREUM_SOCKET" "R" "HKLM" "\\.\pipe\geth.ipc"
|
||||
|
||||
# Remove install directory from PATH
|
||||
${un.EnvVarUpdate} $0 "PATH" "R" "HKLM" $INSTDIR
|
||||
Push "$INSTDIR"
|
||||
Call un.RemoveFromPath
|
||||
|
||||
# Cleanup registry (deletes all sub keys)
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${GROUPNAME} ${APPNAME}"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
Pod::Spec.new do |spec|
|
||||
spec.name = '{{.Name}}'
|
||||
spec.name = 'Geth'
|
||||
spec.version = '{{.Version}}'
|
||||
spec.license = { :type => 'GNU Lesser General Public License, Version 3.0' }
|
||||
spec.homepage = 'https://github.com/ethereum/go-ethereum'
|
||||
@@ -14,9 +14,9 @@ Pod::Spec.new do |spec|
|
||||
spec.ios.vendored_frameworks = 'Frameworks/Geth.framework'
|
||||
|
||||
spec.prepare_command = <<-CMD
|
||||
curl https://gethstore.blob.core.windows.net/builds/geth-ios-all-{{.Version}}.tar.gz | tar -xvz
|
||||
curl https://gethstore.blob.core.windows.net/builds/{{.Archive}}.tar.gz | tar -xvz
|
||||
mkdir Frameworks
|
||||
mv geth-ios-all-{{.Version}}/Geth.framework Frameworks
|
||||
rm -rf geth-ios-all-{{.Version}}
|
||||
mv {{.Archive}}/Geth.framework Frameworks
|
||||
rm -rf {{.Archive}}
|
||||
CMD
|
||||
end
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -39,6 +40,7 @@ func main() {
|
||||
nodeKeyFile = flag.String("nodekey", "", "private key filename")
|
||||
nodeKeyHex = flag.String("nodekeyhex", "", "private key as hex (for testing)")
|
||||
natdesc = flag.String("nat", "none", "port mapping mechanism (any|none|upnp|pmp|extip:<IP>)")
|
||||
netrestrict = flag.String("netrestrict", "", "restrict network communication to the given IP networks (CIDR masks)")
|
||||
runv5 = flag.Bool("v5", false, "run a v5 topic discovery bootnode")
|
||||
|
||||
nodeKey *ecdsa.PrivateKey
|
||||
@@ -81,12 +83,20 @@ func main() {
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
var restrictList *netutil.Netlist
|
||||
if *netrestrict != "" {
|
||||
restrictList, err = netutil.ParseNetlist(*netrestrict)
|
||||
if err != nil {
|
||||
utils.Fatalf("-netrestrict: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if *runv5 {
|
||||
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil {
|
||||
if _, err := discv5.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
} else {
|
||||
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, ""); err != nil {
|
||||
if _, err := discover.ListenUDP(nodeKey, *listenAddr, natm, "", restrictList); err != nil {
|
||||
utils.Fatalf("%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
139
cmd/evm/main.go
139
cmd/evm/main.go
@@ -20,21 +20,18 @@ package main
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/big"
|
||||
"os"
|
||||
"runtime"
|
||||
goruntime "runtime"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/core/vm/runtime"
|
||||
"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"
|
||||
)
|
||||
|
||||
@@ -129,13 +126,6 @@ func run(ctx *cli.Context) error {
|
||||
|
||||
logger := vm.NewStructLogger(nil)
|
||||
|
||||
vmenv := NewEnv(statedb, common.StringToAddress("evmuser"), common.Big(ctx.GlobalString(ValueFlag.Name)), vm.Config{
|
||||
Debug: ctx.GlobalBool(DebugFlag.Name),
|
||||
ForceJit: ctx.GlobalBool(ForceJitFlag.Name),
|
||||
EnableJit: !ctx.GlobalBool(DisableJitFlag.Name),
|
||||
Tracer: logger,
|
||||
})
|
||||
|
||||
tstart := time.Now()
|
||||
|
||||
var (
|
||||
@@ -168,25 +158,30 @@ func run(ctx *cli.Context) error {
|
||||
|
||||
if ctx.GlobalBool(CreateFlag.Name) {
|
||||
input := append(code, common.Hex2Bytes(ctx.GlobalString(InputFlag.Name))...)
|
||||
ret, _, err = vmenv.Create(
|
||||
sender,
|
||||
input,
|
||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
)
|
||||
ret, _, err = runtime.Create(input, &runtime.Config{
|
||||
Origin: sender.Address(),
|
||||
State: statedb,
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: logger,
|
||||
},
|
||||
})
|
||||
} else {
|
||||
receiver := statedb.CreateAccount(common.StringToAddress("receiver"))
|
||||
|
||||
receiver.SetCode(crypto.Keccak256Hash(code), code)
|
||||
ret, err = vmenv.Call(
|
||||
sender,
|
||||
receiver.Address(),
|
||||
common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)),
|
||||
common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
)
|
||||
|
||||
ret, err = runtime.Call(receiver.Address(), common.Hex2Bytes(ctx.GlobalString(InputFlag.Name)), &runtime.Config{
|
||||
Origin: sender.Address(),
|
||||
State: statedb,
|
||||
GasLimit: common.Big(ctx.GlobalString(GasFlag.Name)),
|
||||
GasPrice: common.Big(ctx.GlobalString(PriceFlag.Name)),
|
||||
Value: common.Big(ctx.GlobalString(ValueFlag.Name)),
|
||||
EVMConfig: vm.Config{
|
||||
Tracer: logger,
|
||||
},
|
||||
})
|
||||
}
|
||||
vmdone := time.Since(tstart)
|
||||
|
||||
@@ -197,8 +192,8 @@ func run(ctx *cli.Context) error {
|
||||
vm.StdErrFormat(logger.StructLogs())
|
||||
|
||||
if ctx.GlobalBool(SysStatFlag.Name) {
|
||||
var mem runtime.MemStats
|
||||
runtime.ReadMemStats(&mem)
|
||||
var mem goruntime.MemStats
|
||||
goruntime.ReadMemStats(&mem)
|
||||
fmt.Printf("vm took %v\n", vmdone)
|
||||
fmt.Printf(`alloc: %d
|
||||
tot alloc: %d
|
||||
@@ -223,87 +218,3 @@ func main() {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
type VMEnv struct {
|
||||
state *state.StateDB
|
||||
block *types.Block
|
||||
|
||||
transactor *common.Address
|
||||
value *big.Int
|
||||
|
||||
depth int
|
||||
Gas *big.Int
|
||||
time *big.Int
|
||||
logs []vm.StructLog
|
||||
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
||||
func NewEnv(state *state.StateDB, transactor common.Address, value *big.Int, cfg vm.Config) *VMEnv {
|
||||
env := &VMEnv{
|
||||
state: state,
|
||||
transactor: &transactor,
|
||||
value: value,
|
||||
time: big.NewInt(time.Now().Unix()),
|
||||
}
|
||||
|
||||
env.evm = vm.New(env, cfg)
|
||||
return env
|
||||
}
|
||||
|
||||
// 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) 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()
|
||||
}
|
||||
return common.Hash{}
|
||||
}
|
||||
func (self *VMEnv) AddLog(log *vm.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
||||
core.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
self.Gas = gas
|
||||
return core.Call(self, caller, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) DelegateCall(caller vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, caller, addr, data, gas, price)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, price, value)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/trie"
|
||||
"github.com/syndtr/goleveldb/leveldb/util"
|
||||
@@ -39,6 +40,18 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
initCommand = cli.Command{
|
||||
Action: initGenesis,
|
||||
Name: "init",
|
||||
Usage: "Bootstrap and initialize a new genesis block",
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The init command initializes a new genesis block and definition for the network.
|
||||
This is a destructive action and changes the network in which you will be
|
||||
participating.
|
||||
`,
|
||||
}
|
||||
importCommand = cli.Command{
|
||||
Action: importChain,
|
||||
Name: "import",
|
||||
@@ -95,13 +108,34 @@ Use "ethereum dump 0" to dump the genesis block.
|
||||
}
|
||||
)
|
||||
|
||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
|
||||
func initGenesis(ctx *cli.Context) error {
|
||||
genesisPath := ctx.Args().First()
|
||||
if len(genesisPath) == 0 {
|
||||
utils.Fatalf("must supply path to genesis JSON file")
|
||||
}
|
||||
|
||||
stack := makeFullNode(ctx)
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||
|
||||
genesisFile, err := os.Open(genesisPath)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis file: %v", err)
|
||||
}
|
||||
|
||||
block, err := core.WriteGenesisBlock(chaindb, genesisFile)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to write genesis block: %v", err)
|
||||
}
|
||||
glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash())
|
||||
return nil
|
||||
}
|
||||
|
||||
func importChain(ctx *cli.Context) error {
|
||||
if len(ctx.Args()) != 1 {
|
||||
utils.Fatalf("This command requires an argument.")
|
||||
}
|
||||
if ctx.GlobalBool(utils.TestNetFlag.Name) {
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
}
|
||||
stack := makeFullNode(ctx)
|
||||
chain, chainDb := utils.MakeChain(ctx, stack)
|
||||
defer chainDb.Close()
|
||||
|
||||
@@ -28,7 +28,7 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
@@ -46,7 +46,7 @@ func TestConsoleWelcome(t *testing.T) {
|
||||
// Gather all the infos the welcome message needs to contain
|
||||
geth.setTemplateFunc("goos", func() string { return runtime.GOOS })
|
||||
geth.setTemplateFunc("gover", runtime.Version)
|
||||
geth.setTemplateFunc("gethver", func() string { return utils.Version })
|
||||
geth.setTemplateFunc("gethver", func() string { return params.Version })
|
||||
geth.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||
geth.setTemplateFunc("apis", func() []string {
|
||||
apis := append(strings.Split(rpc.DefaultIPCApis, ","), rpc.MetadataApi)
|
||||
@@ -132,7 +132,7 @@ func testAttachWelcome(t *testing.T, geth *testgeth, endpoint string) {
|
||||
// Gather all the infos the welcome message needs to contain
|
||||
attach.setTemplateFunc("goos", func() string { return runtime.GOOS })
|
||||
attach.setTemplateFunc("gover", runtime.Version)
|
||||
attach.setTemplateFunc("gethver", func() string { return utils.Version })
|
||||
attach.setTemplateFunc("gethver", func() string { return params.Version })
|
||||
attach.setTemplateFunc("etherbase", func() string { return geth.Etherbase })
|
||||
attach.setTemplateFunc("niltime", func() string { return time.Unix(0, 0).Format(time.RFC1123) })
|
||||
attach.setTemplateFunc("ipc", func() bool { return strings.HasPrefix(endpoint, "ipc") })
|
||||
|
||||
@@ -83,75 +83,28 @@ var daoGenesisForkBlock = big.NewInt(314)
|
||||
// TestDAOForkBlockNewChain tests that the DAO hard-fork number and the nodes support/opposition is correctly
|
||||
// set in the database after various initialization procedures and invocations.
|
||||
func TestDAOForkBlockNewChain(t *testing.T) {
|
||||
for _, arg := range []struct {
|
||||
for i, arg := range []struct {
|
||||
testnet bool
|
||||
genesis string
|
||||
votes [][2]bool
|
||||
expectBlock *big.Int
|
||||
expectVote bool
|
||||
}{
|
||||
// Test DAO Default Mainnet
|
||||
{false, "", [][2]bool{{false, false}}, params.MainNetDAOForkBlock, true},
|
||||
// test DAO Support Mainnet
|
||||
{false, "", [][2]bool{{true, false}}, params.MainNetDAOForkBlock, true},
|
||||
// test DAO Oppose Mainnet
|
||||
{false, "", [][2]bool{{false, true}}, params.MainNetDAOForkBlock, false},
|
||||
// test DAO Switch To Support Mainnet
|
||||
{false, "", [][2]bool{{false, true}, {true, false}}, params.MainNetDAOForkBlock, true},
|
||||
// test DAO Switch To Oppose Mainnet
|
||||
{false, "", [][2]bool{{true, false}, {false, true}}, params.MainNetDAOForkBlock, false},
|
||||
{false, "", params.MainNetDAOForkBlock, true},
|
||||
// test DAO Default Testnet
|
||||
{true, "", [][2]bool{{false, false}}, params.TestNetDAOForkBlock, true},
|
||||
// test DAO Support Testnet
|
||||
{true, "", [][2]bool{{true, false}}, params.TestNetDAOForkBlock, true},
|
||||
// test DAO Oppose Testnet
|
||||
{true, "", [][2]bool{{false, true}}, params.TestNetDAOForkBlock, false},
|
||||
// test DAO Switch To Support Testnet
|
||||
{true, "", [][2]bool{{false, true}, {true, false}}, params.TestNetDAOForkBlock, true},
|
||||
// test DAO Switch To Oppose Testnet
|
||||
{true, "", [][2]bool{{true, false}, {false, true}}, params.TestNetDAOForkBlock, false},
|
||||
{true, "", params.TestNetDAOForkBlock, true},
|
||||
// test DAO Init Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{}, nil, false},
|
||||
// test DAO Default Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{{false, false}}, nil, false},
|
||||
// test DAO Support Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{{true, false}}, nil, true},
|
||||
// test DAO Oppose Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{{false, true}}, nil, false},
|
||||
// test DAO Switch To Support Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{{false, true}, {true, false}}, nil, true},
|
||||
// test DAO Switch To Oppose Old Privnet
|
||||
{false, daoOldGenesis, [][2]bool{{true, false}, {false, true}}, nil, false},
|
||||
// test DAO Init No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{}, daoGenesisForkBlock, false},
|
||||
{false, daoOldGenesis, nil, false},
|
||||
// test DAO Default No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{{false, false}}, daoGenesisForkBlock, false},
|
||||
// test DAO Support No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{{true, false}}, daoGenesisForkBlock, true},
|
||||
// test DAO Oppose No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{{false, true}}, daoGenesisForkBlock, false},
|
||||
// test DAO Switch To Support No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{{false, true}, {true, false}}, daoGenesisForkBlock, true},
|
||||
// test DAO Switch To Oppose No Fork Privnet
|
||||
{false, daoNoForkGenesis, [][2]bool{{true, false}, {false, true}}, daoGenesisForkBlock, false},
|
||||
// test DAO Init Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{}, daoGenesisForkBlock, true},
|
||||
{false, daoNoForkGenesis, daoGenesisForkBlock, false},
|
||||
// test DAO Default Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{{false, false}}, daoGenesisForkBlock, true},
|
||||
// test DAO Support Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{{true, false}}, daoGenesisForkBlock, true},
|
||||
// test DAO Oppose Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{{false, true}}, daoGenesisForkBlock, false},
|
||||
// test DAO Switch To Support Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{{false, true}, {true, false}}, daoGenesisForkBlock, true},
|
||||
// test DAO Switch To Oppose Pro Fork Privnet
|
||||
{false, daoProForkGenesis, [][2]bool{{true, false}, {false, true}}, daoGenesisForkBlock, false},
|
||||
{false, daoProForkGenesis, daoGenesisForkBlock, true},
|
||||
} {
|
||||
testDAOForkBlockNewChain(t, arg.testnet, arg.genesis, arg.votes, arg.expectBlock, arg.expectVote)
|
||||
testDAOForkBlockNewChain(t, i, arg.testnet, arg.genesis, arg.expectBlock, arg.expectVote)
|
||||
}
|
||||
}
|
||||
|
||||
func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes [][2]bool, expectBlock *big.Int, expectVote bool) {
|
||||
func testDAOForkBlockNewChain(t *testing.T, test int, testnet bool, genesis string, expectBlock *big.Int, expectVote bool) {
|
||||
// Create a temporary data directory to use and inspect later
|
||||
datadir := tmpdir(t)
|
||||
defer os.RemoveAll(datadir)
|
||||
@@ -160,21 +113,15 @@ func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes
|
||||
if genesis != "" {
|
||||
json := filepath.Join(datadir, "genesis.json")
|
||||
if err := ioutil.WriteFile(json, []byte(genesis), 0600); err != nil {
|
||||
t.Fatalf("failed to write genesis file: %v", err)
|
||||
t.Fatalf("test %d: failed to write genesis file: %v", test, err)
|
||||
}
|
||||
runGeth(t, "--datadir", datadir, "init", json).cmd.Wait()
|
||||
}
|
||||
for _, vote := range votes {
|
||||
} else {
|
||||
// Force chain initialization
|
||||
args := []string{"--port", "0", "--maxpeers", "0", "--nodiscover", "--nat", "none", "--ipcdisable", "--datadir", datadir}
|
||||
if testnet {
|
||||
args = append(args, "--testnet")
|
||||
}
|
||||
if vote[0] {
|
||||
args = append(args, "--support-dao-fork")
|
||||
}
|
||||
if vote[1] {
|
||||
args = append(args, "--oppose-dao-fork")
|
||||
}
|
||||
geth := runGeth(t, append(args, []string{"--exec", "2+2", "console"}...)...)
|
||||
geth.cmd.Wait()
|
||||
}
|
||||
@@ -185,32 +132,33 @@ func testDAOForkBlockNewChain(t *testing.T, testnet bool, genesis string, votes
|
||||
}
|
||||
db, err := ethdb.NewLDBDatabase(path, 0, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to open test database: %v", err)
|
||||
t.Fatalf("test %d: failed to open test database: %v", test, err)
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
genesisHash := common.HexToHash("0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3")
|
||||
if testnet {
|
||||
genesisHash = common.HexToHash("0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303")
|
||||
genesisHash = common.HexToHash("0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1ca4a2d")
|
||||
}
|
||||
if genesis != "" {
|
||||
genesisHash = daoGenesisHash
|
||||
}
|
||||
config, err := core.GetChainConfig(db, genesisHash)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to retrieve chain config: %v", err)
|
||||
t.Errorf("test %d: failed to retrieve chain config: %v", test, err)
|
||||
return // we want to return here, the other checks can't make it past this point (nil panic).
|
||||
}
|
||||
// Validate the DAO hard-fork block number against the expected value
|
||||
if config.DAOForkBlock == nil {
|
||||
if expectBlock != nil {
|
||||
t.Errorf("dao hard-fork block mismatch: have nil, want %v", expectBlock)
|
||||
t.Errorf("test %d: dao hard-fork block mismatch: have nil, want %v", test, expectBlock)
|
||||
}
|
||||
} else if expectBlock == nil {
|
||||
t.Errorf("dao hard-fork block mismatch: have %v, want nil", config.DAOForkBlock)
|
||||
t.Errorf("test %d: dao hard-fork block mismatch: have %v, want nil", test, config.DAOForkBlock)
|
||||
} else if config.DAOForkBlock.Cmp(expectBlock) != 0 {
|
||||
t.Errorf("dao hard-fork block mismatch: have %v, want %v", config.DAOForkBlock, expectBlock)
|
||||
t.Errorf("test %d: dao hard-fork block mismatch: have %v, want %v", test, config.DAOForkBlock, expectBlock)
|
||||
}
|
||||
if config.DAOForkSupport != expectVote {
|
||||
t.Errorf("dao hard-fork support mismatch: have %v, want %v", config.DAOForkSupport, expectVote)
|
||||
t.Errorf("test %d: dao hard-fork support mismatch: have %v, want %v", test, config.DAOForkSupport, expectVote)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,24 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Simple wrapper to translate the API exposed methods and types to inthernal
|
||||
// Go versions of the same types.
|
||||
|
||||
#include "_cgo_export.h"
|
||||
|
||||
int run(const char* args) {
|
||||
return doRun((char*)args);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Contains specialized code for running Geth on Android.
|
||||
|
||||
package main
|
||||
|
||||
// #include <android/log.h>
|
||||
// #cgo LDFLAGS: -llog
|
||||
import "C"
|
||||
import (
|
||||
"bufio"
|
||||
"os"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Redirect the standard output and error to logcat
|
||||
oldStdout, oldStderr := os.Stdout, os.Stderr
|
||||
|
||||
outRead, outWrite, _ := os.Pipe()
|
||||
errRead, errWrite, _ := os.Pipe()
|
||||
|
||||
os.Stdout = outWrite
|
||||
os.Stderr = errWrite
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(outRead)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stdout"), C.CString(line))
|
||||
oldStdout.WriteString(line + "\n")
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
scanner := bufio.NewScanner(errRead)
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
C.__android_log_write(C.ANDROID_LOG_INFO, C.CString("Stderr"), C.CString(line))
|
||||
oldStderr.WriteString(line + "\n")
|
||||
}
|
||||
}()
|
||||
}
|
||||
183
cmd/geth/main.go
183
cmd/geth/main.go
@@ -20,27 +20,23 @@ package main
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/console"
|
||||
"github.com/ethereum/go-ethereum/contracts/release"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/internal/debug"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
"github.com/ethereum/go-ethereum/node"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
@@ -63,59 +59,26 @@ func init() {
|
||||
app.HideVersion = true // we have a command to print the version
|
||||
app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
|
||||
app.Commands = []cli.Command{
|
||||
// See chaincmd.go:
|
||||
initCommand,
|
||||
importCommand,
|
||||
exportCommand,
|
||||
upgradedbCommand,
|
||||
removedbCommand,
|
||||
dumpCommand,
|
||||
// See monitorcmd.go:
|
||||
monitorCommand,
|
||||
// See accountcmd.go:
|
||||
accountCommand,
|
||||
walletCommand,
|
||||
// See consolecmd.go:
|
||||
consoleCommand,
|
||||
attachCommand,
|
||||
javascriptCommand,
|
||||
{
|
||||
Action: makedag,
|
||||
Name: "makedag",
|
||||
Usage: "Generate ethash DAG (for testing)",
|
||||
ArgsUsage: "<blockNum> <outputDir>",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
Description: `
|
||||
The makedag command generates an ethash DAG in /tmp/dag.
|
||||
|
||||
This command exists to support the system testing project.
|
||||
Regular users do not need to execute it.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: version,
|
||||
Name: "version",
|
||||
Usage: "Print version numbers",
|
||||
ArgsUsage: " ",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
Description: `
|
||||
The output of this command is supposed to be machine-readable.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: initGenesis,
|
||||
Name: "init",
|
||||
Usage: "Bootstrap and initialize a new genesis block",
|
||||
ArgsUsage: "<genesisPath>",
|
||||
Category: "BLOCKCHAIN COMMANDS",
|
||||
Description: `
|
||||
The init command initializes a new genesis block and definition for the network.
|
||||
This is a destructive action and changes the network in which you will be
|
||||
participating.
|
||||
`,
|
||||
},
|
||||
{
|
||||
Action: license,
|
||||
Name: "license",
|
||||
Usage: "Display license information",
|
||||
ArgsUsage: " ",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
},
|
||||
// See misccmd.go:
|
||||
makedagCommand,
|
||||
versionCommand,
|
||||
licenseCommand,
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
@@ -139,8 +102,6 @@ participating.
|
||||
utils.MaxPendingPeersFlag,
|
||||
utils.EtherbaseFlag,
|
||||
utils.GasPriceFlag,
|
||||
utils.SupportDAOFork,
|
||||
utils.OpposeDAOFork,
|
||||
utils.MinerThreadsFlag,
|
||||
utils.MiningEnabledFlag,
|
||||
utils.AutoDAGFlag,
|
||||
@@ -149,6 +110,7 @@ participating.
|
||||
utils.NatspecEnabledFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.RPCEnabledFlag,
|
||||
@@ -173,6 +135,7 @@ participating.
|
||||
utils.VMEnableJitFlag,
|
||||
utils.NetworkIdFlag,
|
||||
utils.RPCCORSDomainFlag,
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
utils.SolcPathFlag,
|
||||
@@ -229,37 +192,25 @@ func geth(ctx *cli.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// initGenesis will initialise the given JSON format genesis file and writes it as
|
||||
// the zero'd block (i.e. genesis) or will fail hard if it can't succeed.
|
||||
func initGenesis(ctx *cli.Context) error {
|
||||
genesisPath := ctx.Args().First()
|
||||
if len(genesisPath) == 0 {
|
||||
utils.Fatalf("must supply path to genesis JSON file")
|
||||
}
|
||||
|
||||
if ctx.GlobalBool(utils.TestNetFlag.Name) {
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
}
|
||||
|
||||
stack := makeFullNode(ctx)
|
||||
chaindb := utils.MakeChainDatabase(ctx, stack)
|
||||
|
||||
genesisFile, err := os.Open(genesisPath)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to read genesis file: %v", err)
|
||||
}
|
||||
|
||||
block, err := core.WriteGenesisBlock(chaindb, genesisFile)
|
||||
if err != nil {
|
||||
utils.Fatalf("failed to write genesis block: %v", err)
|
||||
}
|
||||
glog.V(logger.Info).Infof("successfully wrote genesis block and/or chain rule set: %x", block.Hash())
|
||||
return nil
|
||||
}
|
||||
|
||||
func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
// Create the default extradata and construct the base node
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(params.VersionMajor<<16 | params.VersionMinor<<8 | params.VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
extra = nil
|
||||
}
|
||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
||||
utils.RegisterEthService(ctx, stack, utils.MakeDefaultExtraData(clientIdentifier))
|
||||
utils.RegisterEthService(ctx, stack, extra)
|
||||
|
||||
// Whisper must be explicitly enabled, but is auto-enabled in --dev mode.
|
||||
shhEnabled := ctx.GlobalBool(utils.WhisperEnabledFlag.Name)
|
||||
@@ -267,14 +218,17 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
if shhEnabled || shhAutoEnabled {
|
||||
utils.RegisterShhService(stack)
|
||||
}
|
||||
|
||||
// Add the Ethereum Stats daemon if requested
|
||||
if url := ctx.GlobalString(utils.EthStatsURLFlag.Name); url != "" {
|
||||
utils.RegisterEthStatsService(stack, url)
|
||||
}
|
||||
// Add the release oracle service so it boots along with node.
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
config := release.Config{
|
||||
Oracle: relOracle,
|
||||
Major: uint32(utils.VersionMajor),
|
||||
Minor: uint32(utils.VersionMinor),
|
||||
Patch: uint32(utils.VersionPatch),
|
||||
Major: uint32(params.VersionMajor),
|
||||
Minor: uint32(params.VersionMinor),
|
||||
Patch: uint32(params.VersionPatch),
|
||||
}
|
||||
commit, _ := hex.DecodeString(gitCommit)
|
||||
copy(config.Commit[:], commit)
|
||||
@@ -282,7 +236,6 @@ func makeFullNode(ctx *cli.Context) *node.Node {
|
||||
}); err != nil {
|
||||
utils.Fatalf("Failed to register the Geth release oracle service: %v", err)
|
||||
}
|
||||
|
||||
return stack
|
||||
}
|
||||
|
||||
@@ -313,65 +266,3 @@ func startNode(ctx *cli.Context, stack *node.Node) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func makedag(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
wrongArgs := func() {
|
||||
utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`)
|
||||
}
|
||||
switch {
|
||||
case len(args) == 2:
|
||||
blockNum, err := strconv.ParseUint(args[0], 0, 64)
|
||||
dir := args[1]
|
||||
if err != nil {
|
||||
wrongArgs()
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
// seems to require a trailing slash
|
||||
if !strings.HasSuffix(dir, "/") {
|
||||
dir = dir + "/"
|
||||
}
|
||||
_, err = ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func version(ctx *cli.Context) error {
|
||||
fmt.Println(strings.Title(clientIdentifier))
|
||||
fmt.Println("Version:", utils.Version)
|
||||
if gitCommit != "" {
|
||||
fmt.Println("Git Commit:", gitCommit)
|
||||
}
|
||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||
fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
|
||||
fmt.Println("Go Version:", runtime.Version())
|
||||
fmt.Println("OS:", runtime.GOOS)
|
||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
||||
return nil
|
||||
}
|
||||
|
||||
func license(_ *cli.Context) error {
|
||||
fmt.Println(`Geth is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Geth 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with geth. If not, see <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
return nil
|
||||
}
|
||||
|
||||
128
cmd/geth/misccmd.go
Normal file
128
cmd/geth/misccmd.go
Normal file
@@ -0,0 +1,128 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
var (
|
||||
makedagCommand = cli.Command{
|
||||
Action: makedag,
|
||||
Name: "makedag",
|
||||
Usage: "Generate ethash DAG (for testing)",
|
||||
ArgsUsage: "<blockNum> <outputDir>",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
Description: `
|
||||
The makedag command generates an ethash DAG in /tmp/dag.
|
||||
|
||||
This command exists to support the system testing project.
|
||||
Regular users do not need to execute it.
|
||||
`,
|
||||
}
|
||||
versionCommand = cli.Command{
|
||||
Action: version,
|
||||
Name: "version",
|
||||
Usage: "Print version numbers",
|
||||
ArgsUsage: " ",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
Description: `
|
||||
The output of this command is supposed to be machine-readable.
|
||||
`,
|
||||
}
|
||||
licenseCommand = cli.Command{
|
||||
Action: license,
|
||||
Name: "license",
|
||||
Usage: "Display license information",
|
||||
ArgsUsage: " ",
|
||||
Category: "MISCELLANEOUS COMMANDS",
|
||||
}
|
||||
)
|
||||
|
||||
func makedag(ctx *cli.Context) error {
|
||||
args := ctx.Args()
|
||||
wrongArgs := func() {
|
||||
utils.Fatalf(`Usage: geth makedag <block number> <outputdir>`)
|
||||
}
|
||||
switch {
|
||||
case len(args) == 2:
|
||||
blockNum, err := strconv.ParseUint(args[0], 0, 64)
|
||||
dir := args[1]
|
||||
if err != nil {
|
||||
wrongArgs()
|
||||
} else {
|
||||
dir = filepath.Clean(dir)
|
||||
// seems to require a trailing slash
|
||||
if !strings.HasSuffix(dir, "/") {
|
||||
dir = dir + "/"
|
||||
}
|
||||
_, err = ioutil.ReadDir(dir)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't find dir")
|
||||
}
|
||||
fmt.Println("making DAG, this could take awhile...")
|
||||
ethash.MakeDAG(blockNum, dir)
|
||||
}
|
||||
default:
|
||||
wrongArgs()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func version(ctx *cli.Context) error {
|
||||
fmt.Println(strings.Title(clientIdentifier))
|
||||
fmt.Println("Version:", params.Version)
|
||||
if gitCommit != "" {
|
||||
fmt.Println("Git Commit:", gitCommit)
|
||||
}
|
||||
fmt.Println("Protocol Versions:", eth.ProtocolVersions)
|
||||
fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
|
||||
fmt.Println("Go Version:", runtime.Version())
|
||||
fmt.Println("OS:", runtime.GOOS)
|
||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
||||
return nil
|
||||
}
|
||||
|
||||
func license(_ *cli.Context) error {
|
||||
fmt.Println(`Geth is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
Geth 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 General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with geth. If not, see <http://www.gnu.org/licenses/>.
|
||||
`)
|
||||
return nil
|
||||
}
|
||||
@@ -161,6 +161,7 @@ var AppHelpFlagGroups = []flagGroup{
|
||||
{
|
||||
Name: "LOGGING AND DEBUGGING",
|
||||
Flags: append([]cli.Flag{
|
||||
utils.EthStatsURLFlag,
|
||||
utils.MetricsEnabledFlag,
|
||||
utils.FakePoWFlag,
|
||||
}, debug.Flags...),
|
||||
|
||||
@@ -19,22 +19,21 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"github.com/ethereum/go-ethereum/swarm/storage"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
runtime.GOMAXPROCS(runtime.NumCPU())
|
||||
|
||||
if len(os.Args) < 2 {
|
||||
fmt.Println("Usage: bzzhash <file name>")
|
||||
os.Exit(0)
|
||||
func hash(ctx *cli.Context) {
|
||||
args := ctx.Args()
|
||||
if len(args) < 1 {
|
||||
log.Fatal("Usage: swarm hash <file name>")
|
||||
}
|
||||
f, err := os.Open(os.Args[1])
|
||||
f, err := os.Open(args[0])
|
||||
if err != nil {
|
||||
fmt.Println("Error opening file " + os.Args[1])
|
||||
fmt.Println("Error opening file " + args[1])
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -42,7 +41,7 @@ func main() {
|
||||
chunker := storage.NewTreeChunker(storage.NewChunkerParams())
|
||||
key, err := chunker.Split(f, stat.Size(), nil, nil, nil)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||
log.Fatalf("%v\n", err)
|
||||
} else {
|
||||
fmt.Printf("%v\n", key)
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/cmd/utils"
|
||||
@@ -38,14 +39,25 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/swarm"
|
||||
bzzapi "github.com/ethereum/go-ethereum/swarm/api"
|
||||
"github.com/ethereum/go-ethereum/swarm/network"
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
const clientIdentifier = "bzzd"
|
||||
const (
|
||||
clientIdentifier = "swarm"
|
||||
versionString = "0.2"
|
||||
)
|
||||
|
||||
var (
|
||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||
app = utils.NewApp(gitCommit, "Ethereum Swarm server daemon")
|
||||
gitCommit string // Git SHA1 commit hash of the release (set via linker flags)
|
||||
app = utils.NewApp(gitCommit, "Ethereum Swarm")
|
||||
testbetBootNodes = []string{
|
||||
"enode://ec8ae764f7cb0417bdfb009b9d0f18ab3818a3a4e8e7c67dd5f18971a93510a2e6f43cd0b69a27e439a9629457ea804104f37c85e41eed057d3faabbf7744cdf@13.74.157.139:30429",
|
||||
"enode://c2e1fceb3bf3be19dff71eec6cccf19f2dbf7567ee017d130240c670be8594bc9163353ca55dd8df7a4f161dd94b36d0615c17418b5a3cdcbb4e9d99dfa4de37@13.74.157.139:30430",
|
||||
"enode://fe29b82319b734ce1ec68b84657d57145fee237387e63273989d354486731e59f78858e452ef800a020559da22dcca759536e6aa5517c53930d29ce0b1029286@13.74.157.139:30431",
|
||||
"enode://1d7187e7bde45cf0bee489ce9852dd6d1a0d9aa67a33a6b8e6db8a4fbc6fcfa6f0f1a5419343671521b863b187d1c73bad3603bae66421d157ffef357669ddb8@13.74.157.139:30432",
|
||||
"enode://0e4cba800f7b1ee73673afa6a4acead4018f0149d2e3216be3f133318fd165b324cd71b81fbe1e80deac8dbf56e57a49db7be67f8b9bc81bd2b7ee496434fb5d@13.74.157.139:30433",
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -61,56 +73,117 @@ var (
|
||||
Name: "bzzport",
|
||||
Usage: "Swarm local http api port",
|
||||
}
|
||||
SwarmNetworkIdFlag = cli.IntFlag{
|
||||
Name: "bzznetworkid",
|
||||
Usage: "Network identifier (integer, default 3=swarm testnet)",
|
||||
Value: network.NetworkId,
|
||||
}
|
||||
SwarmConfigPathFlag = cli.StringFlag{
|
||||
Name: "bzzconfig",
|
||||
Usage: "Swarm config file path (datadir/bzz)",
|
||||
}
|
||||
SwarmSwapDisabled = cli.BoolFlag{
|
||||
Name: "bzznoswap",
|
||||
Usage: "Swarm SWAP disabled (default false)",
|
||||
SwarmSwapEnabled = cli.BoolFlag{
|
||||
Name: "swap",
|
||||
Usage: "Swarm SWAP enabled (default false)",
|
||||
}
|
||||
SwarmSyncDisabled = cli.BoolFlag{
|
||||
Name: "bzznosync",
|
||||
Usage: "Swarm Syncing disabled (default false)",
|
||||
SwarmSyncEnabled = cli.BoolTFlag{
|
||||
Name: "sync",
|
||||
Usage: "Swarm Syncing enabled (default true)",
|
||||
}
|
||||
EthAPI = cli.StringFlag{
|
||||
Name: "ethapi",
|
||||
Usage: "URL of the Ethereum API provider",
|
||||
Value: node.DefaultIPCEndpoint("geth"),
|
||||
}
|
||||
SwarmApiFlag = cli.StringFlag{
|
||||
Name: "bzzapi",
|
||||
Usage: "Swarm HTTP endpoint",
|
||||
Value: "http://127.0.0.1:8500",
|
||||
}
|
||||
SwarmRecursiveUploadFlag = cli.BoolFlag{
|
||||
Name: "recursive",
|
||||
Usage: "Upload directories recursively",
|
||||
}
|
||||
SwarmWantManifestFlag = cli.BoolTFlag{
|
||||
Name: "manifest",
|
||||
Usage: "Automatic manifest upload",
|
||||
}
|
||||
SwarmUploadDefaultPath = cli.StringFlag{
|
||||
Name: "defaultpath",
|
||||
Usage: "path to file served for empty url path (none)",
|
||||
}
|
||||
)
|
||||
|
||||
var defaultBootnodes = []string{}
|
||||
|
||||
func init() {
|
||||
// Override flag defaults so bzzd can run alongside geth.
|
||||
utils.ListenPortFlag.Value = 30399
|
||||
utils.IPCPathFlag.Value = utils.DirectoryString{Value: "bzzd.ipc"}
|
||||
utils.IPCApiFlag.Value = "admin, bzz, chequebook, debug, rpc, web3"
|
||||
|
||||
// Set up the cli app.
|
||||
app.Commands = nil
|
||||
app.Action = bzzd
|
||||
app.HideVersion = true // we have a command to print the version
|
||||
app.Copyright = "Copyright 2013-2016 The go-ethereum Authors"
|
||||
app.Commands = []cli.Command{
|
||||
cli.Command{
|
||||
Action: version,
|
||||
Name: "version",
|
||||
Usage: "Print version numbers",
|
||||
ArgsUsage: " ",
|
||||
Description: `
|
||||
The output of this command is supposed to be machine-readable.
|
||||
`,
|
||||
},
|
||||
cli.Command{
|
||||
Action: upload,
|
||||
Name: "up",
|
||||
Usage: "upload a file or directory to swarm using the HTTP API",
|
||||
ArgsUsage: " <file>",
|
||||
Description: `
|
||||
"upload a file or directory to swarm using the HTTP API and prints the root hash",
|
||||
`,
|
||||
},
|
||||
cli.Command{
|
||||
Action: hash,
|
||||
Name: "hash",
|
||||
Usage: "print the swarm hash of a file or directory",
|
||||
ArgsUsage: " <file>",
|
||||
Description: `
|
||||
Prints the swarm hash of file or directory.
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
app.Flags = []cli.Flag{
|
||||
utils.IdentityFlag,
|
||||
utils.DataDirFlag,
|
||||
utils.BootnodesFlag,
|
||||
utils.KeyStoreDirFlag,
|
||||
utils.ListenPortFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.NoDiscoverFlag,
|
||||
utils.DiscoveryV5Flag,
|
||||
utils.NetrestrictFlag,
|
||||
utils.NodeKeyFileFlag,
|
||||
utils.NodeKeyHexFlag,
|
||||
utils.MaxPeersFlag,
|
||||
utils.NATFlag,
|
||||
utils.IPCDisabledFlag,
|
||||
utils.IPCApiFlag,
|
||||
utils.IPCPathFlag,
|
||||
// bzzd-specific flags
|
||||
EthAPI,
|
||||
SwarmConfigPathFlag,
|
||||
SwarmSwapDisabled,
|
||||
SwarmSyncDisabled,
|
||||
SwarmSwapEnabled,
|
||||
SwarmSyncEnabled,
|
||||
SwarmPortFlag,
|
||||
SwarmAccountFlag,
|
||||
SwarmNetworkIdFlag,
|
||||
ChequebookAddrFlag,
|
||||
// upload flags
|
||||
SwarmApiFlag,
|
||||
SwarmRecursiveUploadFlag,
|
||||
SwarmWantManifestFlag,
|
||||
SwarmUploadDefaultPath,
|
||||
}
|
||||
app.Flags = append(app.Flags, debug.Flags...)
|
||||
app.Before = func(ctx *cli.Context) error {
|
||||
@@ -130,16 +203,33 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
func version(ctx *cli.Context) error {
|
||||
fmt.Println(strings.Title(clientIdentifier))
|
||||
fmt.Println("Version:", versionString)
|
||||
if gitCommit != "" {
|
||||
fmt.Println("Git Commit:", gitCommit)
|
||||
}
|
||||
fmt.Println("Network Id:", ctx.GlobalInt(utils.NetworkIdFlag.Name))
|
||||
fmt.Println("Go Version:", runtime.Version())
|
||||
fmt.Println("OS:", runtime.GOOS)
|
||||
fmt.Printf("GOPATH=%s\n", os.Getenv("GOPATH"))
|
||||
fmt.Printf("GOROOT=%s\n", runtime.GOROOT())
|
||||
return nil
|
||||
}
|
||||
|
||||
func bzzd(ctx *cli.Context) error {
|
||||
stack := utils.MakeNode(ctx, clientIdentifier, gitCommit)
|
||||
registerBzzService(ctx, stack)
|
||||
utils.StartNode(stack)
|
||||
|
||||
networkId := ctx.GlobalUint64(SwarmNetworkIdFlag.Name)
|
||||
// Add bootnodes as initial peers.
|
||||
if ctx.GlobalIsSet(utils.BootnodesFlag.Name) {
|
||||
injectBootnodes(stack.Server(), ctx.GlobalStringSlice(utils.BootnodesFlag.Name))
|
||||
bootnodes := strings.Split(ctx.GlobalString(utils.BootnodesFlag.Name), ",")
|
||||
injectBootnodes(stack.Server(), bootnodes)
|
||||
} else {
|
||||
injectBootnodes(stack.Server(), defaultBootnodes)
|
||||
if networkId == 3 {
|
||||
injectBootnodes(stack.Server(), testbetBootNodes)
|
||||
}
|
||||
}
|
||||
|
||||
stack.Wait()
|
||||
@@ -154,7 +244,7 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
||||
if bzzdir == "" {
|
||||
bzzdir = stack.InstanceDir()
|
||||
}
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey)
|
||||
bzzconfig, err := bzzapi.NewConfig(bzzdir, chbookaddr, prvkey, ctx.GlobalUint64(SwarmNetworkIdFlag.Name))
|
||||
if err != nil {
|
||||
utils.Fatalf("unable to configure swarm: %v", err)
|
||||
}
|
||||
@@ -162,18 +252,18 @@ func registerBzzService(ctx *cli.Context, stack *node.Node) {
|
||||
if len(bzzport) > 0 {
|
||||
bzzconfig.Port = bzzport
|
||||
}
|
||||
swapEnabled := !ctx.GlobalBool(SwarmSwapDisabled.Name)
|
||||
syncEnabled := !ctx.GlobalBool(SwarmSyncDisabled.Name)
|
||||
swapEnabled := ctx.GlobalBool(SwarmSwapEnabled.Name)
|
||||
syncEnabled := ctx.GlobalBoolT(SwarmSyncEnabled.Name)
|
||||
|
||||
ethapi := ctx.GlobalString(EthAPI.Name)
|
||||
if ethapi == "" {
|
||||
utils.Fatalf("Option %q must not be empty", EthAPI.Name)
|
||||
}
|
||||
|
||||
boot := func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
client, err := ethclient.Dial(ethapi)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't connect: %v", err)
|
||||
var client *ethclient.Client
|
||||
if len(ethapi) > 0 {
|
||||
client, err = ethclient.Dial(ethapi)
|
||||
if err != nil {
|
||||
utils.Fatalf("Can't connect: %v", err)
|
||||
}
|
||||
}
|
||||
return swarm.NewSwarm(ctx, client, bzzconfig, swapEnabled, syncEnabled)
|
||||
}
|
||||
@@ -240,6 +330,7 @@ func injectBootnodes(srv *p2p.Server, nodes []string) {
|
||||
n, err := discover.ParseNode(url)
|
||||
if err != nil {
|
||||
glog.Errorf("invalid bootnode %q", err)
|
||||
continue
|
||||
}
|
||||
srv.AddPeer(n)
|
||||
}
|
||||
@@ -20,7 +20,6 @@ package main
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -28,58 +27,83 @@ import (
|
||||
"mime"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/urfave/cli.v1"
|
||||
)
|
||||
|
||||
func main() {
|
||||
func upload(ctx *cli.Context) {
|
||||
args := ctx.Args()
|
||||
var (
|
||||
bzzapiFlag = flag.String("bzzapi", "http://127.0.0.1:8500", "Swarm HTTP endpoint")
|
||||
recursiveFlag = flag.Bool("recursive", false, "Upload directories recursively")
|
||||
manifestFlag = flag.Bool("manifest", true, "Skip automatic manifest upload")
|
||||
bzzapi = strings.TrimRight(ctx.GlobalString(SwarmApiFlag.Name), "/")
|
||||
recursive = ctx.GlobalBool(SwarmRecursiveUploadFlag.Name)
|
||||
wantManifest = ctx.GlobalBoolT(SwarmWantManifestFlag.Name)
|
||||
defaultPath = ctx.GlobalString(SwarmUploadDefaultPath.Name)
|
||||
)
|
||||
log.SetOutput(os.Stderr)
|
||||
log.SetFlags(0)
|
||||
flag.Parse()
|
||||
if flag.NArg() != 1 {
|
||||
if len(args) != 1 {
|
||||
log.Fatal("need filename as the first and only argument")
|
||||
}
|
||||
|
||||
var (
|
||||
file = flag.Arg(0)
|
||||
client = &client{api: *bzzapiFlag}
|
||||
file = args[0]
|
||||
client = &client{api: bzzapi}
|
||||
mroot manifest
|
||||
entry manifestEntry
|
||||
)
|
||||
fi, err := os.Stat(file)
|
||||
fi, err := os.Stat(expandPath(file))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if fi.IsDir() {
|
||||
if !*recursiveFlag {
|
||||
if !recursive {
|
||||
log.Fatal("argument is a directory and recursive upload is disabled")
|
||||
}
|
||||
mroot, err = client.uploadDirectory(file)
|
||||
mroot, err = client.uploadDirectory(file, defaultPath)
|
||||
} else {
|
||||
mroot, err = client.uploadFile(file, fi)
|
||||
if *manifestFlag {
|
||||
// Wrap the raw file entry in a proper manifest so both hashes get printed.
|
||||
mroot = manifest{Entries: []manifest{mroot}}
|
||||
}
|
||||
entry, err = client.uploadFile(file, fi)
|
||||
mroot = manifest{[]manifestEntry{entry}}
|
||||
}
|
||||
if err != nil {
|
||||
log.Fatalln("upload failed:", err)
|
||||
}
|
||||
if *manifestFlag {
|
||||
hash, err := client.uploadManifest(mroot)
|
||||
if err != nil {
|
||||
log.Fatalln("manifest upload failed:", err)
|
||||
}
|
||||
mroot.Hash = hash
|
||||
if !wantManifest {
|
||||
// Print the manifest. This is the only output to stdout.
|
||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
||||
fmt.Println(string(mrootJSON))
|
||||
return
|
||||
}
|
||||
hash, err := client.uploadManifest(mroot)
|
||||
if err != nil {
|
||||
log.Fatalln("manifest upload failed:", err)
|
||||
}
|
||||
fmt.Println(hash)
|
||||
}
|
||||
|
||||
// Print the manifest. This is the only output to stdout.
|
||||
mrootJSON, _ := json.MarshalIndent(mroot, "", " ")
|
||||
fmt.Println(string(mrootJSON))
|
||||
// Expands a file path
|
||||
// 1. replace tilde with users home dir
|
||||
// 2. expands embedded environment variables
|
||||
// 3. cleans the path, e.g. /a/b/../c -> /a/c
|
||||
// Note, it has limitations, e.g. ~someuser/tmp will not be expanded
|
||||
func expandPath(p string) string {
|
||||
if strings.HasPrefix(p, "~/") || strings.HasPrefix(p, "~\\") {
|
||||
if home := homeDir(); home != "" {
|
||||
p = home + p[1:]
|
||||
}
|
||||
}
|
||||
return path.Clean(os.ExpandEnv(p))
|
||||
}
|
||||
|
||||
func homeDir() string {
|
||||
if home := os.Getenv("HOME"); home != "" {
|
||||
return home
|
||||
}
|
||||
if usr, err := user.Current(); err == nil {
|
||||
return usr.HomeDir
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// client wraps interaction with the swarm HTTP gateway.
|
||||
@@ -88,25 +112,41 @@ type client struct {
|
||||
}
|
||||
|
||||
// manifest is the JSON representation of a swarm manifest.
|
||||
type manifest struct {
|
||||
Hash string `json:"hash,omitempty"`
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
Entries []manifest `json:"entries,omitempty"`
|
||||
type manifestEntry struct {
|
||||
Hash string `json:"hash,omitempty"`
|
||||
ContentType string `json:"contentType,omitempty"`
|
||||
Path string `json:"path,omitempty"`
|
||||
}
|
||||
|
||||
func (c *client) uploadFile(file string, fi os.FileInfo) (manifest, error) {
|
||||
// manifest is the JSON representation of a swarm manifest.
|
||||
type manifest struct {
|
||||
Entries []manifestEntry `json:"entries,omitempty"`
|
||||
}
|
||||
|
||||
func (c *client) uploadFile(file string, fi os.FileInfo) (manifestEntry, error) {
|
||||
hash, err := c.uploadFileContent(file, fi)
|
||||
m := manifest{
|
||||
m := manifestEntry{
|
||||
Hash: hash,
|
||||
ContentType: mime.TypeByExtension(filepath.Ext(fi.Name())),
|
||||
}
|
||||
return m, err
|
||||
}
|
||||
|
||||
func (c *client) uploadDirectory(dir string) (manifest, error) {
|
||||
func (c *client) uploadDirectory(dir string, defaultPath string) (manifest, error) {
|
||||
dirm := manifest{}
|
||||
prefix := filepath.ToSlash(dir) + "/"
|
||||
if len(defaultPath) > 0 {
|
||||
fi, err := os.Stat(defaultPath)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
entry, err := c.uploadFile(defaultPath, fi)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
entry.Path = ""
|
||||
dirm.Entries = append(dirm.Entries, entry)
|
||||
}
|
||||
prefix := filepath.ToSlash(filepath.Clean(dir)) + "/"
|
||||
err := filepath.Walk(dir, func(path string, fi os.FileInfo, err error) error {
|
||||
if err != nil || fi.IsDir() {
|
||||
return err
|
||||
@@ -115,7 +155,7 @@ func (c *client) uploadDirectory(dir string) (manifest, error) {
|
||||
return fmt.Errorf("path %s outside directory %s", path, dir)
|
||||
}
|
||||
entry, err := c.uploadFile(path, fi)
|
||||
entry.Path = strings.TrimPrefix(filepath.ToSlash(path), prefix)
|
||||
entry.Path = strings.TrimPrefix(filepath.ToSlash(filepath.Clean(path)), prefix)
|
||||
dirm.Entries = append(dirm.Entries, entry)
|
||||
return err
|
||||
})
|
||||
@@ -18,12 +18,14 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
@@ -133,7 +135,15 @@ func ImportChain(chain *core.BlockChain, fn string) error {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
stream := rlp.NewStream(fh, 0)
|
||||
|
||||
var reader io.Reader = fh
|
||||
if strings.HasSuffix(fn, ".gz") {
|
||||
if reader, err = gzip.NewReader(reader); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
stream := rlp.NewStream(reader, 0)
|
||||
|
||||
// Run actual the import.
|
||||
blocks := make(types.Blocks, importBatchSize)
|
||||
@@ -195,10 +205,18 @@ func ExportChain(blockchain *core.BlockChain, fn string) error {
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
if err := blockchain.Export(fh); err != nil {
|
||||
|
||||
var writer io.Writer = fh
|
||||
if strings.HasSuffix(fn, ".gz") {
|
||||
writer = gzip.NewWriter(writer)
|
||||
defer writer.(*gzip.Writer).Close()
|
||||
}
|
||||
|
||||
if err := blockchain.Export(writer); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infoln("Exported blockchain to ", fn)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -210,7 +228,14 @@ func ExportAppendChain(blockchain *core.BlockChain, fn string, first uint64, las
|
||||
return err
|
||||
}
|
||||
defer fh.Close()
|
||||
if err := blockchain.ExportN(fh, first, last); err != nil {
|
||||
|
||||
var writer io.Writer = fh
|
||||
if strings.HasSuffix(fn, ".gz") {
|
||||
writer = gzip.NewWriter(writer)
|
||||
defer writer.(*gzip.Writer).Close()
|
||||
}
|
||||
|
||||
if err := blockchain.ExportN(writer, first, last); err != nil {
|
||||
return err
|
||||
}
|
||||
glog.Infoln("Exported blockchain to ", fn)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utils contains internal helper functions for go-ethereum commands.
|
||||
package utils
|
||||
|
||||
import (
|
||||
@@ -36,9 +37,9 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/eth"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/ethstats"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/les"
|
||||
"github.com/ethereum/go-ethereum/light"
|
||||
"github.com/ethereum/go-ethereum/logger"
|
||||
"github.com/ethereum/go-ethereum/logger/glog"
|
||||
"github.com/ethereum/go-ethereum/metrics"
|
||||
@@ -46,6 +47,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/p2p/discover"
|
||||
"github.com/ethereum/go-ethereum/p2p/discv5"
|
||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/pow"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@@ -86,7 +88,7 @@ func NewApp(gitCommit, usage string) *cli.App {
|
||||
app.Author = ""
|
||||
//app.Authors = nil
|
||||
app.Email = ""
|
||||
app.Version = Version
|
||||
app.Version = params.Version
|
||||
if gitCommit != "" {
|
||||
app.Version += "-" + gitCommit[:8]
|
||||
}
|
||||
@@ -114,7 +116,7 @@ var (
|
||||
}
|
||||
NetworkIdFlag = cli.IntFlag{
|
||||
Name: "networkid",
|
||||
Usage: "Network identifier (integer, 0=Olympic, 1=Frontier, 2=Morden)",
|
||||
Usage: "Network identifier (integer, 0=Olympic (disused), 1=Frontier, 2=Morden (disused), 3=Ropsten)",
|
||||
Value: eth.NetworkId,
|
||||
}
|
||||
OlympicFlag = cli.BoolFlag{
|
||||
@@ -123,7 +125,7 @@ var (
|
||||
}
|
||||
TestNetFlag = cli.BoolFlag{
|
||||
Name: "testnet",
|
||||
Usage: "Morden network: pre-configured test network with modified starting nonces (replay protection)",
|
||||
Usage: "Ropsten network: pre-configured test network",
|
||||
}
|
||||
DevModeFlag = cli.BoolFlag{
|
||||
Name: "dev",
|
||||
@@ -175,15 +177,6 @@ var (
|
||||
Usage: "Number of trie node generations to keep in memory",
|
||||
Value: int(state.MaxTrieCacheGen),
|
||||
}
|
||||
// Fork settings
|
||||
SupportDAOFork = cli.BoolFlag{
|
||||
Name: "support-dao-fork",
|
||||
Usage: "Updates the chain rules to support the DAO hard-fork",
|
||||
}
|
||||
OpposeDAOFork = cli.BoolFlag{
|
||||
Name: "oppose-dao-fork",
|
||||
Usage: "Updates the chain rules to oppose the DAO hard-fork",
|
||||
}
|
||||
// Miner settings
|
||||
MiningEnabledFlag = cli.BoolFlag{
|
||||
Name: "mine",
|
||||
@@ -242,8 +235,11 @@ var (
|
||||
Name: "jitvm",
|
||||
Usage: "Enable the JIT VM",
|
||||
}
|
||||
|
||||
// logging and debug settings
|
||||
// Logging and debug settings
|
||||
EthStatsURLFlag = cli.StringFlag{
|
||||
Name: "ethstats",
|
||||
Usage: "Reporting URL of a ethstats service (nodename:secret@host:port)",
|
||||
}
|
||||
MetricsEnabledFlag = cli.BoolFlag{
|
||||
Name: metrics.MetricsEnabledFlag,
|
||||
Usage: "Enable metrics collection and reporting",
|
||||
@@ -367,14 +363,20 @@ var (
|
||||
Name: "v5disc",
|
||||
Usage: "Enables the experimental RLPx V5 (Topic Discovery) mechanism",
|
||||
}
|
||||
NetrestrictFlag = cli.StringFlag{
|
||||
Name: "netrestrict",
|
||||
Usage: "Restricts network communication to the given IP networks (CIDR masks)",
|
||||
}
|
||||
|
||||
WhisperEnabledFlag = cli.BoolFlag{
|
||||
Name: "shh",
|
||||
Usage: "Enable Whisper",
|
||||
}
|
||||
|
||||
// ATM the url is left to the user and deployment to
|
||||
JSpathFlag = cli.StringFlag{
|
||||
Name: "jspath",
|
||||
Usage: "JavaScript root path for `loadScript` and document root for `admin.httpGet`",
|
||||
Usage: "JavaScript root path for `loadScript`",
|
||||
Value: ".",
|
||||
}
|
||||
SolcPathFlag = cli.StringFlag{
|
||||
@@ -654,7 +656,7 @@ func MakePasswordList(ctx *cli.Context) []string {
|
||||
|
||||
// MakeNode configures a node with no services from command line flags.
|
||||
func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
||||
vsn := Version
|
||||
vsn := params.Version
|
||||
if gitCommit != "" {
|
||||
vsn += "-" + gitCommit[:8]
|
||||
}
|
||||
@@ -694,6 +696,14 @@ func MakeNode(ctx *cli.Context, name, gitCommit string) *node.Node {
|
||||
config.MaxPeers = 0
|
||||
config.ListenAddr = ":0"
|
||||
}
|
||||
if netrestrict := ctx.GlobalString(NetrestrictFlag.Name); netrestrict != "" {
|
||||
list, err := netutil.ParseNetlist(netrestrict)
|
||||
if err != nil {
|
||||
Fatalf("Option %q: %v", NetrestrictFlag.Name, err)
|
||||
}
|
||||
config.NetRestrict = list
|
||||
}
|
||||
|
||||
stack, err := node.New(config)
|
||||
if err != nil {
|
||||
Fatalf("Failed to create the protocol stack: %v", err)
|
||||
@@ -751,11 +761,9 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
|
||||
case ctx.GlobalBool(TestNetFlag.Name):
|
||||
if !ctx.GlobalIsSet(NetworkIdFlag.Name) {
|
||||
ethConf.NetworkId = 2
|
||||
ethConf.NetworkId = 3
|
||||
}
|
||||
ethConf.Genesis = core.TestNetGenesisBlock()
|
||||
state.StartingNonce = 1048576 // (2**20)
|
||||
light.StartingNonce = 1048576 // (2**20)
|
||||
ethConf.Genesis = core.DefaultTestnetGenesisBlock()
|
||||
|
||||
case ctx.GlobalBool(DevModeFlag.Name):
|
||||
ethConf.Genesis = core.OlympicGenesisBlock()
|
||||
@@ -789,13 +797,30 @@ func RegisterEthService(ctx *cli.Context, stack *node.Node, extra []byte) {
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterShhService configures whisper and adds it to the given node.
|
||||
// RegisterShhService configures Whisper and adds it to the given node.
|
||||
func RegisterShhService(stack *node.Node) {
|
||||
if err := stack.Register(func(*node.ServiceContext) (node.Service, error) { return whisper.New(), nil }); err != nil {
|
||||
Fatalf("Failed to register the Whisper service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterEthStatsService configures the Ethereum Stats daemon and adds it to
|
||||
// th egiven node.
|
||||
func RegisterEthStatsService(stack *node.Node, url string) {
|
||||
if err := stack.Register(func(ctx *node.ServiceContext) (node.Service, error) {
|
||||
// Retrieve both eth and les services
|
||||
var ethServ *eth.Ethereum
|
||||
ctx.Service(ðServ)
|
||||
|
||||
var lesServ *les.LightEthereum
|
||||
ctx.Service(&lesServ)
|
||||
|
||||
return ethstats.New(url, ethServ, lesServ)
|
||||
}); err != nil {
|
||||
Fatalf("Failed to register the Ethereum Stats service: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
// SetupNetwork configures the system for either the main net or some test network.
|
||||
func SetupNetwork(ctx *cli.Context) {
|
||||
switch {
|
||||
@@ -848,46 +873,25 @@ func MakeChainConfigFromDb(ctx *cli.Context, db ethdb.Database) *params.ChainCon
|
||||
(genesis.Hash() == params.TestNetGenesisHash && ctx.GlobalBool(TestNetFlag.Name))
|
||||
|
||||
if defaults {
|
||||
// Homestead fork
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.HomesteadBlock = params.TestNetHomesteadBlock
|
||||
config = params.TestnetChainConfig
|
||||
} else {
|
||||
// Homestead fork
|
||||
config.HomesteadBlock = params.MainNetHomesteadBlock
|
||||
}
|
||||
// DAO fork
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.DAOForkBlock = params.TestNetDAOForkBlock
|
||||
} else {
|
||||
// DAO fork
|
||||
config.DAOForkBlock = params.MainNetDAOForkBlock
|
||||
}
|
||||
config.DAOForkSupport = true
|
||||
config.DAOForkSupport = true
|
||||
|
||||
// DoS reprice fork
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP150Block = params.TestNetHomesteadGasRepriceBlock
|
||||
config.EIP150Hash = params.TestNetHomesteadGasRepriceHash
|
||||
} else {
|
||||
// DoS reprice fork
|
||||
config.EIP150Block = params.MainNetHomesteadGasRepriceBlock
|
||||
config.EIP150Hash = params.MainNetHomesteadGasRepriceHash
|
||||
}
|
||||
// DoS state cleanup fork
|
||||
if ctx.GlobalBool(TestNetFlag.Name) {
|
||||
config.EIP155Block = params.TestNetSpuriousDragon
|
||||
config.EIP158Block = params.TestNetSpuriousDragon
|
||||
config.ChainId = params.TestNetChainID
|
||||
} else {
|
||||
|
||||
// DoS state cleanup fork
|
||||
config.EIP155Block = params.MainNetSpuriousDragon
|
||||
config.EIP158Block = params.MainNetSpuriousDragon
|
||||
config.ChainId = params.MainNetChainID
|
||||
}
|
||||
}
|
||||
// Force override any existing configs if explicitly requested
|
||||
switch {
|
||||
case ctx.GlobalBool(SupportDAOFork.Name):
|
||||
config.DAOForkSupport = true
|
||||
case ctx.GlobalBool(OpposeDAOFork.Name):
|
||||
config.DAOForkSupport = false
|
||||
}
|
||||
return config
|
||||
}
|
||||
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// Copyright 2016 The go-ethereum Authors
|
||||
// This file is part of go-ethereum.
|
||||
//
|
||||
// go-ethereum is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// go-ethereum 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
// Package utils contains internal helper functions for go-ethereum commands.
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
const (
|
||||
VersionMajor = 1 // Major version component of the current release
|
||||
VersionMinor = 5 // Minor version component of the current release
|
||||
VersionPatch = 2 // Patch version component of the current release
|
||||
VersionMeta = "stable" // Version metadata to append to the version string
|
||||
)
|
||||
|
||||
// Version holds the textual version string.
|
||||
var Version = func() string {
|
||||
v := fmt.Sprintf("%d.%d.%d", VersionMajor, VersionMinor, VersionPatch)
|
||||
if VersionMeta != "" {
|
||||
v += "-" + VersionMeta
|
||||
}
|
||||
return v
|
||||
}()
|
||||
|
||||
// MakeDefaultExtraData returns the default Ethereum block extra data blob.
|
||||
func MakeDefaultExtraData(clientIdentifier string) []byte {
|
||||
var clientInfo = struct {
|
||||
Version uint
|
||||
Name string
|
||||
GoVersion string
|
||||
Os string
|
||||
}{uint(VersionMajor<<16 | VersionMinor<<8 | VersionPatch), clientIdentifier, runtime.Version(), runtime.GOOS}
|
||||
extra, err := rlp.EncodeToBytes(clientInfo)
|
||||
if err != nil {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information:", err)
|
||||
}
|
||||
if uint64(len(extra)) > params.MaximumExtraDataSize.Uint64() {
|
||||
glog.V(logger.Warn).Infoln("error setting canonical miner information: extra exceeds", params.MaximumExtraDataSize)
|
||||
glog.V(logger.Debug).Infof("extra: %x\n", extra)
|
||||
return nil
|
||||
}
|
||||
return extra
|
||||
}
|
||||
232
common/hexutil/hexutil.go
Normal file
232
common/hexutil/hexutil.go
Normal file
@@ -0,0 +1,232 @@
|
||||
// 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 hexutil implements hex encoding with 0x prefix.
|
||||
This encoding is used by the Ethereum RPC API to transport binary data in JSON payloads.
|
||||
|
||||
Encoding Rules
|
||||
|
||||
All hex data must have prefix "0x".
|
||||
|
||||
For byte slices, the hex data must be of even length. An empty byte slice
|
||||
encodes as "0x".
|
||||
|
||||
Integers are encoded using the least amount of digits (no leading zero digits). Their
|
||||
encoding may be of uneven length. The number zero encodes as "0x0".
|
||||
*/
|
||||
package hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
const uintBits = 32 << (uint64(^uint(0)) >> 63)
|
||||
|
||||
var (
|
||||
ErrEmptyString = errors.New("empty hex string")
|
||||
ErrMissingPrefix = errors.New("missing 0x prefix for hex data")
|
||||
ErrSyntax = errors.New("invalid hex")
|
||||
ErrEmptyNumber = errors.New("hex number has no digits after 0x")
|
||||
ErrLeadingZero = errors.New("hex number has leading zero digits after 0x")
|
||||
ErrOddLength = errors.New("hex string has odd length")
|
||||
ErrUint64Range = errors.New("hex number does not fit into 64 bits")
|
||||
ErrUintRange = fmt.Errorf("hex number does not fit into %d bits", uintBits)
|
||||
)
|
||||
|
||||
// Decode decodes a hex string with 0x prefix.
|
||||
func Decode(input string) ([]byte, error) {
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
return hex.DecodeString(input[2:])
|
||||
}
|
||||
|
||||
// MustDecode decodes a hex string with 0x prefix. It panics for invalid input.
|
||||
func MustDecode(input string) []byte {
|
||||
dec, err := Decode(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// Encode encodes b as a hex string with 0x prefix.
|
||||
func Encode(b []byte) string {
|
||||
enc := make([]byte, len(b)*2+2)
|
||||
copy(enc, "0x")
|
||||
hex.Encode(enc[2:], b)
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
// DecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeUint64(input string) (uint64, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
dec, err := strconv.ParseUint(raw, 16, 64)
|
||||
if err != nil {
|
||||
err = mapError(err)
|
||||
}
|
||||
return dec, err
|
||||
}
|
||||
|
||||
// MustDecodeUint64 decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeUint64(input string) uint64 {
|
||||
dec, err := DecodeUint64(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeUint64 encodes i as a hex string with 0x prefix.
|
||||
func EncodeUint64(i uint64) string {
|
||||
enc := make([]byte, 2, 10)
|
||||
copy(enc, "0x")
|
||||
return string(strconv.AppendUint(enc, i, 16))
|
||||
}
|
||||
|
||||
var bigWordNibbles int
|
||||
|
||||
func init() {
|
||||
// This is a weird way to compute the number of nibbles required for big.Word.
|
||||
// The usual way would be to use constant arithmetic but go vet can't handle that.
|
||||
b, _ := new(big.Int).SetString("FFFFFFFFFF", 16)
|
||||
switch len(b.Bits()) {
|
||||
case 1:
|
||||
bigWordNibbles = 16
|
||||
case 2:
|
||||
bigWordNibbles = 8
|
||||
default:
|
||||
panic("weird big.Word size")
|
||||
}
|
||||
}
|
||||
|
||||
// DecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
func DecodeBig(input string) (*big.Int, error) {
|
||||
raw, err := checkNumber(input)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return nil, ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
dec := new(big.Int).SetBits(words)
|
||||
return dec, nil
|
||||
}
|
||||
|
||||
// MustDecodeBig decodes a hex string with 0x prefix as a quantity.
|
||||
// It panics for invalid input.
|
||||
func MustDecodeBig(input string) *big.Int {
|
||||
dec, err := DecodeBig(input)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return dec
|
||||
}
|
||||
|
||||
// EncodeBig encodes bigint as a hex string with 0x prefix.
|
||||
// The sign of the integer is ignored.
|
||||
func EncodeBig(bigint *big.Int) string {
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return "0x0"
|
||||
}
|
||||
enc := make([]byte, 2, (nbits/8)*2+2)
|
||||
copy(enc, "0x")
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
return string(enc)
|
||||
}
|
||||
|
||||
func has0xPrefix(input string) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkNumber(input string) (raw string, err error) {
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyString
|
||||
}
|
||||
if !has0xPrefix(input) {
|
||||
return "", ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return "", ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return "", ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
const badNibble = ^uint64(0)
|
||||
|
||||
func decodeNibble(in byte) uint64 {
|
||||
switch {
|
||||
case in >= '0' && in <= '9':
|
||||
return uint64(in - '0')
|
||||
case in >= 'A' && in <= 'F':
|
||||
return uint64(in - 'A' + 10)
|
||||
case in >= 'a' && in <= 'f':
|
||||
return uint64(in - 'a' + 10)
|
||||
default:
|
||||
return badNibble
|
||||
}
|
||||
}
|
||||
|
||||
func mapError(err error) error {
|
||||
if err, ok := err.(*strconv.NumError); ok {
|
||||
switch err.Err {
|
||||
case strconv.ErrRange:
|
||||
return ErrUint64Range
|
||||
case strconv.ErrSyntax:
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
if _, ok := err.(hex.InvalidByteError); ok {
|
||||
return ErrSyntax
|
||||
}
|
||||
if err == hex.ErrLength {
|
||||
return ErrOddLength
|
||||
}
|
||||
return err
|
||||
}
|
||||
186
common/hexutil/hexutil_test.go
Normal file
186
common/hexutil/hexutil_test.go
Normal file
@@ -0,0 +1,186 @@
|
||||
// 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 hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type marshalTest struct {
|
||||
input interface{}
|
||||
want string
|
||||
}
|
||||
|
||||
type unmarshalTest struct {
|
||||
input string
|
||||
want interface{}
|
||||
wantErr error
|
||||
}
|
||||
|
||||
var (
|
||||
encodeBytesTests = []marshalTest{
|
||||
{[]byte{}, "0x"},
|
||||
{[]byte{0}, "0x00"},
|
||||
{[]byte{0, 0, 1, 2}, "0x00000102"},
|
||||
}
|
||||
|
||||
encodeBigTests = []marshalTest{
|
||||
{referenceBig("0"), "0x0"},
|
||||
{referenceBig("1"), "0x1"},
|
||||
{referenceBig("ff"), "0xff"},
|
||||
{referenceBig("112233445566778899aabbccddeeff"), "0x112233445566778899aabbccddeeff"},
|
||||
}
|
||||
|
||||
encodeUint64Tests = []marshalTest{
|
||||
{uint64(0), "0x0"},
|
||||
{uint64(1), "0x1"},
|
||||
{uint64(0xff), "0xff"},
|
||||
{uint64(0x1122334455667788), "0x1122334455667788"},
|
||||
}
|
||||
|
||||
decodeBytesTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: ``, wantErr: ErrEmptyString},
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x0`, wantErr: hex.ErrLength},
|
||||
{input: `0x023`, wantErr: hex.ErrLength},
|
||||
{input: `0xxx`, wantErr: hex.InvalidByteError('x')},
|
||||
{input: `0x01zz01`, wantErr: hex.InvalidByteError('z')},
|
||||
// valid
|
||||
{input: `0x`, want: []byte{}},
|
||||
{input: `0X`, want: []byte{}},
|
||||
{input: `0x02`, want: []byte{0x02}},
|
||||
{input: `0X02`, want: []byte{0x02}},
|
||||
{input: `0xffffffffff`, want: []byte{0xff, 0xff, 0xff, 0xff, 0xff}},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
|
||||
},
|
||||
}
|
||||
|
||||
decodeBigTests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: big.NewInt(0)},
|
||||
{input: `0x2`, want: big.NewInt(0x2)},
|
||||
{input: `0x2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0X2F2`, want: big.NewInt(0x2f2)},
|
||||
{input: `0x1122aaff`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `0xbBb`, want: big.NewInt(0xbbb)},
|
||||
{input: `0xfffffffff`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `0x112233445566778899aabbccddeeff`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `0xffffffffffffffffffffffffffffffffffff`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
decodeUint64Tests = []unmarshalTest{
|
||||
// invalid
|
||||
{input: `0`, wantErr: ErrMissingPrefix},
|
||||
{input: `0x`, wantErr: ErrEmptyNumber},
|
||||
{input: `0x01`, wantErr: ErrLeadingZero},
|
||||
{input: `0xfffffffffffffffff`, wantErr: ErrUint64Range},
|
||||
{input: `0xx`, wantErr: ErrSyntax},
|
||||
{input: `0x1zz01`, wantErr: ErrSyntax},
|
||||
// valid
|
||||
{input: `0x0`, want: uint64(0)},
|
||||
{input: `0x2`, want: uint64(0x2)},
|
||||
{input: `0x2F2`, want: uint64(0x2f2)},
|
||||
{input: `0X2F2`, want: uint64(0x2f2)},
|
||||
{input: `0x1122aaff`, want: uint64(0x1122aaff)},
|
||||
{input: `0xbbb`, want: uint64(0xbbb)},
|
||||
{input: `0xffffffffffffffff`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
)
|
||||
|
||||
func TestEncode(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
enc := Encode(test.input.([]byte))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecode(t *testing.T) {
|
||||
for _, test := range decodeBytesTests {
|
||||
dec, err := Decode(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), dec) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
enc := EncodeBig(test.input.(*big.Int))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeBig(t *testing.T) {
|
||||
for _, test := range decodeBigTests {
|
||||
dec, err := DecodeBig(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec.Cmp(test.want.(*big.Int)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEncodeUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
enc := EncodeUint64(test.input.(uint64))
|
||||
if enc != test.want {
|
||||
t.Errorf("input %x: wrong encoding %s", test.input, enc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDecodeUint64(t *testing.T) {
|
||||
for _, test := range decodeUint64Tests {
|
||||
dec, err := DecodeUint64(test.input)
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if dec != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, dec, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
271
common/hexutil/json.go
Normal file
271
common/hexutil/json.go
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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 hexutil
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
var (
|
||||
jsonNull = []byte("null")
|
||||
jsonZero = []byte(`"0x0"`)
|
||||
errNonString = errors.New("cannot unmarshal non-string as hex data")
|
||||
errNegativeBigInt = errors.New("hexutil.Big: can't marshal negative integer")
|
||||
)
|
||||
|
||||
// Bytes marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The empty slice marshals as "0x".
|
||||
type Bytes []byte
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Bytes) MarshalJSON() ([]byte, error) {
|
||||
result := make([]byte, len(b)*2+4)
|
||||
copy(result, `"0x`)
|
||||
hex.Encode(result[3:], b)
|
||||
result[len(result)-1] = '"'
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Bytes) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec := make([]byte, len(raw)/2)
|
||||
if _, err = hex.Decode(dec, raw); err != nil {
|
||||
err = mapError(err)
|
||||
} else {
|
||||
*b = dec
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Bytes) String() string {
|
||||
return Encode(b)
|
||||
}
|
||||
|
||||
// UnmarshalJSON decodes input as a JSON string with 0x prefix. The length of out
|
||||
// determines the required input length. This function is commonly used to implement the
|
||||
// UnmarshalJSON method for fixed-size types:
|
||||
//
|
||||
// type Foo [8]byte
|
||||
//
|
||||
// func (f *Foo) UnmarshalJSON(input []byte) error {
|
||||
// return hexutil.UnmarshalJSON("Foo", input, f[:])
|
||||
// }
|
||||
func UnmarshalJSON(typname string, input, out []byte) error {
|
||||
raw, err := checkJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw)/2 != len(out) {
|
||||
return fmt.Errorf("hex string has length %d, want %d for %s", len(raw), len(out)*2, typname)
|
||||
}
|
||||
// Pre-verify syntax before modifying out.
|
||||
for _, b := range raw {
|
||||
if decodeNibble(b) == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
}
|
||||
hex.Decode(out, raw)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Big marshals/unmarshals as a JSON string with 0x prefix. The zero value marshals as
|
||||
// "0x0". Negative integers are not supported at this time. Attempting to marshal them
|
||||
// will return an error.
|
||||
type Big big.Int
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b *Big) MarshalJSON() ([]byte, error) {
|
||||
if b == nil {
|
||||
return jsonNull, nil
|
||||
}
|
||||
bigint := (*big.Int)(b)
|
||||
if bigint.Sign() == -1 {
|
||||
return nil, errNegativeBigInt
|
||||
}
|
||||
nbits := bigint.BitLen()
|
||||
if nbits == 0 {
|
||||
return jsonZero, nil
|
||||
}
|
||||
enc := make([]byte, 3, (nbits/8)*2+4)
|
||||
copy(enc, `"0x`)
|
||||
for i := len(bigint.Bits()) - 1; i >= 0; i-- {
|
||||
enc = strconv.AppendUint(enc, uint64(bigint.Bits()[i]), 16)
|
||||
}
|
||||
enc = append(enc, '"')
|
||||
return enc, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Big) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
words := make([]big.Word, len(raw)/bigWordNibbles+1)
|
||||
end := len(raw)
|
||||
for i := range words {
|
||||
start := end - bigWordNibbles
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
for ri := start; ri < end; ri++ {
|
||||
nib := decodeNibble(raw[ri])
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
words[i] *= 16
|
||||
words[i] += big.Word(nib)
|
||||
}
|
||||
end = start
|
||||
}
|
||||
var dec big.Int
|
||||
dec.SetBits(words)
|
||||
*b = (Big)(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// ToInt converts b to a big.Int.
|
||||
func (b *Big) ToInt() *big.Int {
|
||||
return (*big.Int)(b)
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b *Big) String() string {
|
||||
return EncodeBig(b.ToInt())
|
||||
}
|
||||
|
||||
// Uint64 marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint64 uint64
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint64) MarshalJSON() ([]byte, error) {
|
||||
buf := make([]byte, 3, 12)
|
||||
copy(buf, `"0x`)
|
||||
buf = strconv.AppendUint(buf, uint64(b), 16)
|
||||
buf = append(buf, '"')
|
||||
return buf, nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint64) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkNumberJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(raw) > 16 {
|
||||
return ErrUint64Range
|
||||
}
|
||||
var dec uint64
|
||||
for _, byte := range raw {
|
||||
nib := decodeNibble(byte)
|
||||
if nib == badNibble {
|
||||
return ErrSyntax
|
||||
}
|
||||
dec *= 16
|
||||
dec += uint64(nib)
|
||||
}
|
||||
*b = Uint64(dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint64) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
// Uint marshals/unmarshals as a JSON string with 0x prefix.
|
||||
// The zero value marshals as "0x0".
|
||||
type Uint uint
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (b Uint) MarshalJSON() ([]byte, error) {
|
||||
return Uint64(b).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler.
|
||||
func (b *Uint) UnmarshalJSON(input []byte) error {
|
||||
var u64 Uint64
|
||||
err := u64.UnmarshalJSON(input)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if u64 > Uint64(^uint(0)) {
|
||||
return ErrUintRange
|
||||
}
|
||||
*b = Uint(u64)
|
||||
return nil
|
||||
}
|
||||
|
||||
// String returns the hex encoding of b.
|
||||
func (b Uint) String() string {
|
||||
return EncodeUint64(uint64(b))
|
||||
}
|
||||
|
||||
func isString(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '"' && input[len(input)-1] == '"'
|
||||
}
|
||||
|
||||
func bytesHave0xPrefix(input []byte) bool {
|
||||
return len(input) >= 2 && input[0] == '0' && (input[1] == 'x' || input[1] == 'X')
|
||||
}
|
||||
|
||||
func checkJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
if len(input) == 2 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input[1:]) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[3 : len(input)-1]
|
||||
if len(input)%2 != 0 {
|
||||
return nil, ErrOddLength
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
|
||||
func checkNumberJSON(input []byte) (raw []byte, err error) {
|
||||
if !isString(input) {
|
||||
return nil, errNonString
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyString
|
||||
}
|
||||
if !bytesHave0xPrefix(input) {
|
||||
return nil, ErrMissingPrefix
|
||||
}
|
||||
input = input[2:]
|
||||
if len(input) == 0 {
|
||||
return nil, ErrEmptyNumber
|
||||
}
|
||||
if len(input) > 1 && input[0] == '0' {
|
||||
return nil, ErrLeadingZero
|
||||
}
|
||||
return input, nil
|
||||
}
|
||||
258
common/hexutil/json_test.go
Normal file
258
common/hexutil/json_test.go
Normal file
@@ -0,0 +1,258 @@
|
||||
// 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 hexutil
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"math/big"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func checkError(t *testing.T, input string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("input %s: got no error, want %q", input, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("input %s: unexpected error %q", input, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("input %s: got error %q, want %q", input, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func referenceBig(s string) *big.Int {
|
||||
b, ok := new(big.Int).SetString(s, 16)
|
||||
if !ok {
|
||||
panic("invalid")
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func referenceBytes(s string) []byte {
|
||||
b, err := hex.DecodeString(s)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
||||
var unmarshalBytesTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x0"`, wantErr: ErrOddLength},
|
||||
{input: `"0xxx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x01zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x"`, want: referenceBytes("")},
|
||||
{input: `"0x02"`, want: referenceBytes("02")},
|
||||
{input: `"0X02"`, want: referenceBytes("02")},
|
||||
{input: `"0xffffffffff"`, want: referenceBytes("ffffffffff")},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBytes("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBytes(t *testing.T) {
|
||||
for _, test := range unmarshalBytesTests {
|
||||
var v Bytes
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if !bytes.Equal(test.want.([]byte), []byte(v)) {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, &v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBytes(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Bytes
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBytes(t *testing.T) {
|
||||
for _, test := range encodeBytesTests {
|
||||
in := test.input.([]byte)
|
||||
out, err := Bytes(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%x: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%x: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := Bytes(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalBigTests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: big.NewInt(0)},
|
||||
{input: `"0x2"`, want: big.NewInt(0x2)},
|
||||
{input: `"0x2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0X2F2"`, want: big.NewInt(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: big.NewInt(0x1122aaff)},
|
||||
{input: `"0xbBb"`, want: big.NewInt(0xbbb)},
|
||||
{input: `"0xfffffffff"`, want: big.NewInt(0xfffffffff)},
|
||||
{
|
||||
input: `"0x112233445566778899aabbccddeeff"`,
|
||||
want: referenceBig("112233445566778899aabbccddeeff"),
|
||||
},
|
||||
{
|
||||
input: `"0xffffffffffffffffffffffffffffffffffff"`,
|
||||
want: referenceBig("ffffffffffffffffffffffffffffffffffff"),
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalBig(t *testing.T) {
|
||||
for _, test := range unmarshalBigTests {
|
||||
var v Big
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if test.want != nil && test.want.(*big.Int).Cmp((*big.Int)(&v)) != 0 {
|
||||
t.Errorf("input %s: value mismatch: got %x, want %x", test.input, (*big.Int)(&v), test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalBig(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdef123456789abcdef"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Big
|
||||
if err := v.UnmarshalJSON(input); err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalBig(t *testing.T) {
|
||||
for _, test := range encodeBigTests {
|
||||
in := test.input.(*big.Int)
|
||||
out, err := (*Big)(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (*Big)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalUint64Tests = []unmarshalTest{
|
||||
// invalid encoding
|
||||
{input: "", wantErr: errNonString},
|
||||
{input: "null", wantErr: errNonString},
|
||||
{input: "10", wantErr: errNonString},
|
||||
{input: `""`, wantErr: ErrEmptyString},
|
||||
{input: `"0"`, wantErr: ErrMissingPrefix},
|
||||
{input: `"0x"`, wantErr: ErrEmptyNumber},
|
||||
{input: `"0x01"`, wantErr: ErrLeadingZero},
|
||||
{input: `"0xfffffffffffffffff"`, wantErr: ErrUint64Range},
|
||||
{input: `"0xx"`, wantErr: ErrSyntax},
|
||||
{input: `"0x1zz01"`, wantErr: ErrSyntax},
|
||||
|
||||
// valid encoding
|
||||
{input: `"0x0"`, want: uint64(0)},
|
||||
{input: `"0x2"`, want: uint64(0x2)},
|
||||
{input: `"0x2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0X2F2"`, want: uint64(0x2f2)},
|
||||
{input: `"0x1122aaff"`, want: uint64(0x1122aaff)},
|
||||
{input: `"0xbbb"`, want: uint64(0xbbb)},
|
||||
{input: `"0xffffffffffffffff"`, want: uint64(0xffffffffffffffff)},
|
||||
}
|
||||
|
||||
func TestUnmarshalUint64(t *testing.T) {
|
||||
for _, test := range unmarshalUint64Tests {
|
||||
var v Uint64
|
||||
err := v.UnmarshalJSON([]byte(test.input))
|
||||
if !checkError(t, test.input, err, test.wantErr) {
|
||||
continue
|
||||
}
|
||||
if uint64(v) != test.want.(uint64) {
|
||||
t.Errorf("input %s: value mismatch: got %d, want %d", test.input, v, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkUnmarshalUint64(b *testing.B) {
|
||||
input := []byte(`"0x123456789abcdf"`)
|
||||
for i := 0; i < b.N; i++ {
|
||||
var v Uint64
|
||||
v.UnmarshalJSON(input)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalUint64(t *testing.T) {
|
||||
for _, test := range encodeUint64Tests {
|
||||
in := test.input.(uint64)
|
||||
out, err := Uint64(in).MarshalJSON()
|
||||
if err != nil {
|
||||
t.Errorf("%d: %v", in, err)
|
||||
continue
|
||||
}
|
||||
if want := `"` + test.want + `"`; string(out) != want {
|
||||
t.Errorf("%d: MarshalJSON output mismatch: got %q, want %q", in, out, want)
|
||||
continue
|
||||
}
|
||||
if out := (Uint64)(in).String(); out != test.want {
|
||||
t.Errorf("%x: String mismatch: got %q, want %q", in, out, test.want)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,124 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type HTTPClient struct {
|
||||
*http.Transport
|
||||
DocRoot string
|
||||
schemes []string
|
||||
}
|
||||
|
||||
func New(docRoot string) (self *HTTPClient) {
|
||||
self = &HTTPClient{
|
||||
Transport: &http.Transport{},
|
||||
DocRoot: docRoot,
|
||||
schemes: []string{"file"},
|
||||
}
|
||||
self.RegisterProtocol("file", http.NewFileTransport(http.Dir(self.DocRoot)))
|
||||
return
|
||||
}
|
||||
|
||||
// Clients should be reused instead of created as needed. Clients are safe for concurrent use by multiple goroutines.
|
||||
|
||||
// A Client is higher-level than a RoundTripper (such as Transport) and additionally handles HTTP details such as cookies and redirects.
|
||||
|
||||
func (self *HTTPClient) Client() *http.Client {
|
||||
return &http.Client{
|
||||
Transport: self,
|
||||
}
|
||||
}
|
||||
|
||||
func (self *HTTPClient) RegisterScheme(scheme string, rt http.RoundTripper) {
|
||||
self.schemes = append(self.schemes, scheme)
|
||||
self.RegisterProtocol(scheme, rt)
|
||||
}
|
||||
|
||||
func (self *HTTPClient) HasScheme(scheme string) bool {
|
||||
for _, s := range self.schemes {
|
||||
if s == scheme {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (self *HTTPClient) GetAuthContent(uri string, hash common.Hash) ([]byte, error) {
|
||||
// retrieve content
|
||||
content, err := self.Get(uri, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check hash to authenticate content
|
||||
chash := crypto.Keccak256Hash(content)
|
||||
if chash != hash {
|
||||
return nil, fmt.Errorf("content hash mismatch %x != %x (exp)", hash[:], chash[:])
|
||||
}
|
||||
|
||||
return content, nil
|
||||
|
||||
}
|
||||
|
||||
// Get(uri, path) downloads the document at uri, if path is non-empty it
|
||||
// is interpreted as a filepath to which the contents are saved
|
||||
func (self *HTTPClient) Get(uri, path string) ([]byte, error) {
|
||||
// retrieve content
|
||||
resp, err := self.Client().Get(uri)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer func() {
|
||||
if resp != nil {
|
||||
resp.Body.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
var content []byte
|
||||
content, err = ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if resp.StatusCode/100 != 2 {
|
||||
return content, fmt.Errorf("HTTP error: %s", resp.Status)
|
||||
}
|
||||
|
||||
if path != "" {
|
||||
var abspath string
|
||||
abspath, err = filepath.Abs(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = ioutil.WriteFile(abspath, content, 0600)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return content, nil
|
||||
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package httpclient
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
func TestGetAuthContent(t *testing.T) {
|
||||
dir, err := ioutil.TempDir("", "httpclient-test")
|
||||
if err != nil {
|
||||
t.Fatal("cannot create temporary directory:", err)
|
||||
}
|
||||
defer os.RemoveAll(dir)
|
||||
client := New(dir)
|
||||
|
||||
text := "test"
|
||||
hash := crypto.Keccak256Hash([]byte(text))
|
||||
if err := ioutil.WriteFile(path.Join(dir, "test.content"), []byte(text), os.ModePerm); err != nil {
|
||||
t.Fatal("could not write test file", err)
|
||||
}
|
||||
content, err := client.GetAuthContent("file:///test.content", hash)
|
||||
if err != nil {
|
||||
t.Errorf("no error expected, got %v", err)
|
||||
}
|
||||
if string(content) != text {
|
||||
t.Errorf("incorrect content. expected %v, got %v", text, string(content))
|
||||
}
|
||||
|
||||
hash = common.Hash{}
|
||||
content, err = client.GetAuthContent("file:///test.content", hash)
|
||||
expected := "content hash mismatch 0000000000000000000000000000000000000000000000000000000000000000 != 9c22ff5f21f0b81b113e63f7db6da94fedef11b2119b4088b89664fb9a3cb658 (exp)"
|
||||
if err == nil {
|
||||
t.Errorf("expected error, got nothing")
|
||||
} else {
|
||||
if err.Error() != expected {
|
||||
t.Errorf("expected error '%s' got '%v'", expected, err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
type rt struct{}
|
||||
|
||||
func (rt) RoundTrip(req *http.Request) (resp *http.Response, err error) { return }
|
||||
|
||||
func TestRegisterScheme(t *testing.T) {
|
||||
client := New("/tmp/")
|
||||
if client.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme not to be registered")
|
||||
}
|
||||
client.RegisterScheme("scheme", rt{})
|
||||
if !client.HasScheme("scheme") {
|
||||
t.Errorf("expected scheme to be registered")
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
@@ -1,279 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package ethreg
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/compiler"
|
||||
"github.com/ethereum/go-ethereum/common/registrar"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/state"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/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 *params.ChainConfig
|
||||
bc *core.BlockChain
|
||||
chainDb ethdb.Database
|
||||
txPool *core.TxPool
|
||||
am *accounts.Manager
|
||||
}
|
||||
|
||||
// PrivateRegistarAPI offers various functions to access the Ethereum registry.
|
||||
type PrivateRegistarAPI struct {
|
||||
config *params.ChainConfig
|
||||
be *registryAPIBackend
|
||||
}
|
||||
|
||||
// NewPrivateRegistarAPI creates a new PrivateRegistarAPI instance.
|
||||
func NewPrivateRegistarAPI(config *params.ChainConfig, bc *core.BlockChain, chainDb ethdb.Database, txPool *core.TxPool, am *accounts.Manager) *PrivateRegistarAPI {
|
||||
return &PrivateRegistarAPI{
|
||||
config: config,
|
||||
be: ®istryAPIBackend{
|
||||
config: config,
|
||||
bc: bc,
|
||||
chainDb: chainDb,
|
||||
txPool: txPool,
|
||||
am: am,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// SetGlobalRegistrar allows clients to set the global registry for the node.
|
||||
// This method can be used to deploy a new registry. First zero out the current
|
||||
// address by calling the method with namereg = '0x0' and then call this method
|
||||
// again with '' as namereg. This will submit a transaction to the network which
|
||||
// will deploy a new registry on execution. The TX hash is returned. When called
|
||||
// with namereg '' and the current address is not zero the current global is
|
||||
// address is returned..
|
||||
func (api *PrivateRegistarAPI) SetGlobalRegistrar(namereg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetGlobalRegistrar(namereg, from)
|
||||
}
|
||||
|
||||
// SetHashReg queries the registry for a hash.
|
||||
func (api *PrivateRegistarAPI) SetHashReg(hashreg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetHashReg(hashreg, from)
|
||||
}
|
||||
|
||||
// SetUrlHint queries the registry for an url.
|
||||
func (api *PrivateRegistarAPI) SetUrlHint(hashreg string, from common.Address) (string, error) {
|
||||
return registrar.New(api.be).SetUrlHint(hashreg, from)
|
||||
}
|
||||
|
||||
// SaveInfo stores contract information on the local file system.
|
||||
func (api *PrivateRegistarAPI) SaveInfo(info *compiler.ContractInfo, filename string) (contenthash common.Hash, err error) {
|
||||
return compiler.SaveInfo(info, filename)
|
||||
}
|
||||
|
||||
// Register registers a new content hash in the registry.
|
||||
func (api *PrivateRegistarAPI) Register(sender common.Address, addr common.Address, contentHashHex string) (bool, error) {
|
||||
block := api.be.bc.CurrentBlock()
|
||||
state, err := state.New(block.Root(), api.be.chainDb)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
codeb := state.GetCode(addr)
|
||||
codeHash := common.BytesToHash(crypto.Keccak256(codeb))
|
||||
contentHash := common.HexToHash(contentHashHex)
|
||||
|
||||
_, err = registrar.New(api.be).SetHashToHash(sender, codeHash, contentHash)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// RegisterUrl registers a new url in the registry.
|
||||
func (api *PrivateRegistarAPI) RegisterUrl(sender common.Address, contentHashHex string, url string) (bool, error) {
|
||||
_, err := registrar.New(api.be).SetUrlToHash(sender, common.HexToHash(contentHashHex), url)
|
||||
return err == nil, err
|
||||
}
|
||||
|
||||
// callmsg is the message type used for call transations.
|
||||
type callmsg struct {
|
||||
from *state.StateObject
|
||||
to *common.Address
|
||||
gas, gasPrice *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
}
|
||||
|
||||
// accessor boilerplate to implement core.Message
|
||||
func (m callmsg) From() (common.Address, error) {
|
||||
return m.from.Address(), nil
|
||||
}
|
||||
func (m callmsg) FromFrontier() (common.Address, error) {
|
||||
return m.from.Address(), nil
|
||||
}
|
||||
func (m callmsg) Nonce() uint64 {
|
||||
return 0
|
||||
}
|
||||
func (m callmsg) CheckNonce() bool {
|
||||
return false
|
||||
}
|
||||
func (m callmsg) To() *common.Address {
|
||||
return m.to
|
||||
}
|
||||
func (m callmsg) GasPrice() *big.Int {
|
||||
return m.gasPrice
|
||||
}
|
||||
func (m callmsg) Gas() *big.Int {
|
||||
return m.gas
|
||||
}
|
||||
func (m callmsg) Value() *big.Int {
|
||||
return m.value
|
||||
}
|
||||
func (m callmsg) Data() []byte {
|
||||
return m.data
|
||||
}
|
||||
|
||||
// Call forms a transaction from the given arguments and tries to execute it on
|
||||
// a private VM with a copy of the state. Any changes are therefore only temporary
|
||||
// and not part of the actual state. This allows for local execution/queries.
|
||||
func (be *registryAPIBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, dataStr string) (string, string, error) {
|
||||
block := be.bc.CurrentBlock()
|
||||
statedb, err := state.New(block.Root(), be.chainDb)
|
||||
if err != nil {
|
||||
return "", "", err
|
||||
}
|
||||
|
||||
var from *state.StateObject
|
||||
if len(fromStr) == 0 {
|
||||
accounts := be.am.Accounts()
|
||||
if len(accounts) == 0 {
|
||||
from = statedb.GetOrNewStateObject(common.Address{})
|
||||
} else {
|
||||
from = statedb.GetOrNewStateObject(accounts[0].Address)
|
||||
}
|
||||
} else {
|
||||
from = statedb.GetOrNewStateObject(common.HexToAddress(fromStr))
|
||||
}
|
||||
|
||||
from.SetBalance(common.MaxBig)
|
||||
|
||||
var to *common.Address
|
||||
if len(toStr) > 0 {
|
||||
addr := common.HexToAddress(toStr)
|
||||
to = &addr
|
||||
}
|
||||
gas := common.Big(gasStr)
|
||||
if gas.BitLen() == 0 {
|
||||
gas = big.NewInt(50000000)
|
||||
}
|
||||
gasPrice := common.Big(gasPriceStr)
|
||||
if gasPrice.BitLen() == 0 {
|
||||
gasPrice = new(big.Int).Mul(big.NewInt(50), common.Shannon)
|
||||
}
|
||||
msg := types.NewMessage(from.Address(), to, 0, common.Big(valueStr), gas, gasPrice, common.FromHex(dataStr), false)
|
||||
|
||||
header := be.bc.CurrentBlock().Header()
|
||||
vmenv := core.NewEnv(statedb, be.config, be.bc, msg, header, vm.Config{})
|
||||
gp := new(core.GasPool).AddGas(common.MaxBig)
|
||||
res, gas, err := core.ApplyMessage(vmenv, msg, gp)
|
||||
|
||||
return common.ToHex(res), gas.String(), err
|
||||
}
|
||||
|
||||
// StorageAt returns the data stores in the state for the given address and location.
|
||||
func (be *registryAPIBackend) StorageAt(addr string, storageAddr string) string {
|
||||
block := be.bc.CurrentBlock()
|
||||
state, err := state.New(block.Root(), be.chainDb)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return state.GetState(common.HexToAddress(addr), common.HexToHash(storageAddr)).Hex()
|
||||
}
|
||||
|
||||
// Transact forms a transaction from the given arguments and submits it to the
|
||||
// transactio pool for execution.
|
||||
func (be *registryAPIBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
||||
if len(toStr) > 0 && toStr != "0x" && !common.IsHexAddress(toStr) {
|
||||
return "", errors.New("invalid address")
|
||||
}
|
||||
|
||||
var (
|
||||
from = common.HexToAddress(fromStr)
|
||||
to = common.HexToAddress(toStr)
|
||||
value = common.Big(valueStr)
|
||||
gas *big.Int
|
||||
price *big.Int
|
||||
data []byte
|
||||
contractCreation bool
|
||||
)
|
||||
|
||||
if len(gasStr) == 0 {
|
||||
gas = big.NewInt(90000)
|
||||
} else {
|
||||
gas = common.Big(gasStr)
|
||||
}
|
||||
|
||||
if len(gasPriceStr) == 0 {
|
||||
price = big.NewInt(10000000000000)
|
||||
} else {
|
||||
price = common.Big(gasPriceStr)
|
||||
}
|
||||
|
||||
data = common.FromHex(codeStr)
|
||||
if len(toStr) == 0 {
|
||||
contractCreation = true
|
||||
}
|
||||
|
||||
nonce := be.txPool.State().GetNonce(from)
|
||||
if len(nonceStr) != 0 {
|
||||
nonce = common.Big(nonceStr).Uint64()
|
||||
}
|
||||
|
||||
var tx *types.Transaction
|
||||
if contractCreation {
|
||||
tx = types.NewContractCreation(nonce, value, gas, price, data)
|
||||
} else {
|
||||
tx = types.NewTransaction(nonce, to, value, gas, price, data)
|
||||
}
|
||||
|
||||
sigHash := (types.HomesteadSigner{}).Hash(tx)
|
||||
signature, err := be.am.SignEthereum(from, sigHash.Bytes())
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
signedTx, err := tx.WithSignature(types.HomesteadSigner{}, signature)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
be.txPool.SetLocal(signedTx)
|
||||
if err := be.txPool.Add(signedTx); err != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if contractCreation {
|
||||
addr := crypto.CreateAddress(from, nonce)
|
||||
glog.V(logger.Info).Infof("Tx(%s) created: %s\n", signedTx.Hash().Hex(), addr.Hex())
|
||||
} else {
|
||||
glog.V(logger.Info).Infof("Tx(%s) to: %s\n", signedTx.Hash().Hex(), tx.To().Hex())
|
||||
}
|
||||
|
||||
return signedTx.Hash().Hex(), nil
|
||||
}
|
||||
@@ -1,436 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package registrar
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"regexp"
|
||||
|
||||
"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"
|
||||
)
|
||||
|
||||
/*
|
||||
Registrar implements the Ethereum name registrar services mapping
|
||||
- arbitrary strings to ethereum addresses
|
||||
- hashes to hashes
|
||||
- hashes to arbitrary strings
|
||||
(likely will provide lookup service for all three)
|
||||
|
||||
The Registrar is used by
|
||||
* the roundtripper transport implementation of
|
||||
url schemes to resolve domain names and services that register these names
|
||||
* contract info retrieval (NatSpec).
|
||||
|
||||
The Registrar uses 3 contracts on the blockchain:
|
||||
* GlobalRegistrar: Name (string) -> Address (Owner)
|
||||
* HashReg : Key Hash (hash of domain name or contract code) -> Content Hash
|
||||
* UrlHint : Content Hash -> Url Hint
|
||||
|
||||
These contracts are (currently) not included in the genesis block.
|
||||
Each Set<X> needs to be called once on each blockchain/network once.
|
||||
|
||||
Contract addresses need to be set the first time any Registrar method is called
|
||||
in a client session.
|
||||
This is done for frontier by default, otherwise the caller needs to make sure
|
||||
the relevant environment initialised the desired contracts
|
||||
*/
|
||||
var (
|
||||
// GlobalRegistrarAddr = "0xc6d9d2cd449a754c494264e1809c50e34d64562b" // olympic
|
||||
GlobalRegistrarAddr = "0x33990122638b9132ca29c723bdf037f1a891a70c" // frontier
|
||||
HashRegAddr = "0x23bf622b5a65f6060d855fca401133ded3520620" // frontier
|
||||
UrlHintAddr = "0x73ed5ef6c010727dfd2671dbb70faac19ec18626" // frontier
|
||||
|
||||
zero = regexp.MustCompile("^(0x)?0*$")
|
||||
)
|
||||
|
||||
const (
|
||||
trueHex = "0000000000000000000000000000000000000000000000000000000000000001"
|
||||
falseHex = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
)
|
||||
|
||||
func abiSignature(s string) string {
|
||||
return common.ToHex(crypto.Keccak256([]byte(s))[:4])
|
||||
}
|
||||
|
||||
var (
|
||||
HashRegName = "HashReg"
|
||||
UrlHintName = "UrlHint"
|
||||
|
||||
registerContentHashAbi = abiSignature("register(uint256,uint256)")
|
||||
registerUrlAbi = abiSignature("register(uint256,uint8,uint256)")
|
||||
setOwnerAbi = abiSignature("setowner()")
|
||||
reserveAbi = abiSignature("reserve(bytes32)")
|
||||
resolveAbi = abiSignature("addr(bytes32)")
|
||||
registerAbi = abiSignature("setAddress(bytes32,address,bool)")
|
||||
addressAbiPrefix = falseHex[:24]
|
||||
)
|
||||
|
||||
// Registrar's backend is defined as an interface (implemented by xeth, but could be remote)
|
||||
type Backend interface {
|
||||
StorageAt(string, string) string
|
||||
Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error)
|
||||
Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error)
|
||||
}
|
||||
|
||||
// TODO Registrar should also just implement The Resolver and Registry interfaces
|
||||
// Simplify for now.
|
||||
type VersionedRegistrar interface {
|
||||
Resolver(*big.Int) *Registrar
|
||||
Registry() *Registrar
|
||||
}
|
||||
|
||||
type Registrar struct {
|
||||
backend Backend
|
||||
}
|
||||
|
||||
func New(b Backend) (res *Registrar) {
|
||||
res = &Registrar{b}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetGlobalRegistrar(namereg string, addr common.Address) (txhash string, err error) {
|
||||
if namereg != "" {
|
||||
GlobalRegistrarAddr = namereg
|
||||
return
|
||||
}
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation not given")
|
||||
return
|
||||
} else {
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "800000", "", GlobalRegistrarCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("GlobalRegistrar address not found and sender for creation failed: %v", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetHashReg(hashreg string, addr common.Address) (txhash string, err error) {
|
||||
if hashreg != "" {
|
||||
HashRegAddr = hashreg
|
||||
} else {
|
||||
if !zero.MatchString(HashRegAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(HashRegName, 2)
|
||||
hashRegAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("\ncall HashRegAddr %v with %v\n", GlobalRegistrarAddr, hashRegAbi)
|
||||
var res string
|
||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", hashRegAbi)
|
||||
if len(res) >= 40 {
|
||||
HashRegAddr = "0x" + res[len(res)-40:len(res)]
|
||||
}
|
||||
if err != nil || zero.MatchString(HashRegAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "", "", HashRegCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("HashReg address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created HashRegAddr @ txhash %v\n", txhash)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("HashRegAddr found at @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (self *Registrar) SetUrlHint(urlhint string, addr common.Address) (txhash string, err error) {
|
||||
if urlhint != "" {
|
||||
UrlHintAddr = urlhint
|
||||
} else {
|
||||
if !zero.MatchString(UrlHintAddr) {
|
||||
return
|
||||
}
|
||||
nameHex, extra := encodeName(UrlHintName, 2)
|
||||
urlHintAbi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("UrlHint address query data: %s to %s", urlHintAbi, GlobalRegistrarAddr)
|
||||
var res string
|
||||
res, _, err = self.backend.Call("", GlobalRegistrarAddr, "", "", "", urlHintAbi)
|
||||
if len(res) >= 40 {
|
||||
UrlHintAddr = "0x" + res[len(res)-40:len(res)]
|
||||
}
|
||||
if err != nil || zero.MatchString(UrlHintAddr) {
|
||||
if (addr == common.Address{}) {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation not given")
|
||||
return
|
||||
}
|
||||
txhash, err = self.backend.Transact(addr.Hex(), "", "", "", "210000", "", UrlHintCode)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("UrlHint address not found and sender for creation failed: %v", err)
|
||||
}
|
||||
glog.V(logger.Detail).Infof("created UrlHint @ txhash %v\n", txhash)
|
||||
} else {
|
||||
glog.V(logger.Detail).Infof("UrlHint found @ %v\n", HashRegAddr)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// ReserveName(from, name) reserves name for the sender address in the globalRegistrar
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) ReserveName(address common.Address, name string) (txh string, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := reserveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("Reserve data: %s", abi)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// SetAddressToName(from, name, addr) will set the Address to address for name
|
||||
// in the globalRegistrar using from as the sender of the transaction
|
||||
// the tx needs to be mined to take effect
|
||||
func (self *Registrar) SetAddressToName(from common.Address, name string, address common.Address) (txh string, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return "", fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
|
||||
nameHex, extra := encodeName(name, 6)
|
||||
addrHex := encodeAddress(address)
|
||||
|
||||
abi := registerAbi + nameHex + addrHex + trueHex + extra
|
||||
glog.V(logger.Detail).Infof("SetAddressToName data: %s to %s ", abi, GlobalRegistrarAddr)
|
||||
|
||||
return self.backend.Transact(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "", "",
|
||||
abi,
|
||||
)
|
||||
}
|
||||
|
||||
// NameToAddr(from, name) queries the registrar for the address on name
|
||||
func (self *Registrar) NameToAddr(from common.Address, name string) (address common.Address, err error) {
|
||||
if zero.MatchString(GlobalRegistrarAddr) {
|
||||
return address, fmt.Errorf("GlobalRegistrar address is not set")
|
||||
}
|
||||
|
||||
nameHex, extra := encodeName(name, 2)
|
||||
abi := resolveAbi + nameHex + extra
|
||||
glog.V(logger.Detail).Infof("NameToAddr data: %s", abi)
|
||||
res, _, err := self.backend.Call(
|
||||
from.Hex(),
|
||||
GlobalRegistrarAddr,
|
||||
"", "", "",
|
||||
abi,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
address = common.HexToAddress(res)
|
||||
return
|
||||
}
|
||||
|
||||
// called as first step in the registration process on HashReg
|
||||
func (self *Registrar) SetOwner(address common.Address) (txh string, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return "", fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
setOwnerAbi,
|
||||
)
|
||||
}
|
||||
|
||||
// registers some content hash to a key/code hash
|
||||
// e.g., the contract Info combined Json Doc's ContentHash
|
||||
// to CodeHash of a contract or hash of a domain
|
||||
func (self *Registrar) SetHashToHash(address common.Address, codehash, dochash common.Hash) (txh string, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return "", fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
|
||||
_, err = self.SetOwner(address)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
codehex := common.Bytes2Hex(codehash[:])
|
||||
dochex := common.Bytes2Hex(dochash[:])
|
||||
|
||||
data := registerContentHashAbi + codehex + dochex
|
||||
glog.V(logger.Detail).Infof("SetHashToHash data: %s sent to %v\n", data, HashRegAddr)
|
||||
return self.backend.Transact(
|
||||
address.Hex(),
|
||||
HashRegAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
}
|
||||
|
||||
// SetUrlToHash(from, hash, url) registers a url to a content hash so that the content can be fetched
|
||||
// address is used as sender for the transaction and will be the owner of a new
|
||||
// registry entry on first time use
|
||||
// FIXME: silently doing nothing if sender is not the owner
|
||||
// note that with content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) SetUrlToHash(address common.Address, hash common.Hash, url string) (txh string, err error) {
|
||||
if zero.MatchString(UrlHintAddr) {
|
||||
return "", fmt.Errorf("UrlHint address is not set")
|
||||
}
|
||||
|
||||
hashHex := common.Bytes2Hex(hash[:])
|
||||
var urlHex string
|
||||
urlb := []byte(url)
|
||||
var cnt byte
|
||||
n := len(urlb)
|
||||
|
||||
for n > 0 {
|
||||
if n > 32 {
|
||||
n = 32
|
||||
}
|
||||
urlHex = common.Bytes2Hex(urlb[:n])
|
||||
urlb = urlb[n:]
|
||||
n = len(urlb)
|
||||
bcnt := make([]byte, 32)
|
||||
bcnt[31] = cnt
|
||||
data := registerUrlAbi +
|
||||
hashHex +
|
||||
common.Bytes2Hex(bcnt) +
|
||||
common.Bytes2Hex(common.Hex2BytesFixed(urlHex, 32))
|
||||
txh, err = self.backend.Transact(
|
||||
address.Hex(),
|
||||
UrlHintAddr,
|
||||
"", "", "", "",
|
||||
data,
|
||||
)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
cnt++
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// HashToHash(key) resolves contenthash for key (a hash) using HashReg
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
func (self *Registrar) HashToHash(khash common.Hash) (chash common.Hash, err error) {
|
||||
if zero.MatchString(HashRegAddr) {
|
||||
return common.Hash{}, fmt.Errorf("HashReg address is not set")
|
||||
}
|
||||
|
||||
// look up in hashReg
|
||||
at := HashRegAddr[2:]
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), khash[:]))
|
||||
hash := self.backend.StorageAt(at, key)
|
||||
|
||||
if hash == "0x0" || len(hash) < 3 || (hash == common.Hash{}.Hex()) {
|
||||
err = fmt.Errorf("HashToHash: content hash not found for '%v'", khash.Hex())
|
||||
return
|
||||
}
|
||||
copy(chash[:], common.Hex2BytesFixed(hash[2:], 32))
|
||||
return
|
||||
}
|
||||
|
||||
// HashToUrl(contenthash) resolves the url for contenthash using UrlHint
|
||||
// resolution is costless non-transactional
|
||||
// implemented as direct retrieval from db
|
||||
// if we use content addressed storage, this step is no longer necessary
|
||||
func (self *Registrar) HashToUrl(chash common.Hash) (uri string, err error) {
|
||||
if zero.MatchString(UrlHintAddr) {
|
||||
return "", fmt.Errorf("UrlHint address is not set")
|
||||
}
|
||||
// look up in URL reg
|
||||
var str string = " "
|
||||
var idx uint32
|
||||
for len(str) > 0 {
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), chash[:])
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(idx)))
|
||||
hex := self.backend.StorageAt(UrlHintAddr[2:], key)
|
||||
str = string(common.Hex2Bytes(hex[2:]))
|
||||
l := 0
|
||||
for (l < len(str)) && (str[l] == 0) {
|
||||
l++
|
||||
}
|
||||
|
||||
str = str[l:]
|
||||
uri = uri + str
|
||||
idx++
|
||||
}
|
||||
|
||||
if len(uri) == 0 {
|
||||
err = fmt.Errorf("HashToUrl: URL hint not found for '%v'", chash.Hex())
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func storageIdx2Addr(varidx uint32) []byte {
|
||||
data := make([]byte, 32)
|
||||
binary.BigEndian.PutUint32(data[28:32], varidx)
|
||||
return data
|
||||
}
|
||||
|
||||
func storageMapping(addr, key []byte) []byte {
|
||||
data := make([]byte, 64)
|
||||
copy(data[0:32], key[0:32])
|
||||
copy(data[32:64], addr[0:32])
|
||||
sha := crypto.Keccak256(data)
|
||||
return sha
|
||||
}
|
||||
|
||||
func storageFixedArray(addr, idx []byte) []byte {
|
||||
var carry byte
|
||||
for i := 31; i >= 0; i-- {
|
||||
var b byte = addr[i] + idx[i] + carry
|
||||
if b < addr[i] {
|
||||
carry = 1
|
||||
} else {
|
||||
carry = 0
|
||||
}
|
||||
addr[i] = b
|
||||
}
|
||||
return addr
|
||||
}
|
||||
|
||||
func storageAddress(addr []byte) string {
|
||||
return common.ToHex(addr)
|
||||
}
|
||||
|
||||
func encodeAddress(address common.Address) string {
|
||||
return addressAbiPrefix + address.Hex()[2:]
|
||||
}
|
||||
|
||||
func encodeName(name string, index uint8) (string, string) {
|
||||
extra := common.Bytes2Hex([]byte(name))
|
||||
if len(name) > 32 {
|
||||
return fmt.Sprintf("%064x", index), extra
|
||||
}
|
||||
return extra + falseHex[len(extra):], ""
|
||||
}
|
||||
@@ -1,158 +0,0 @@
|
||||
// Copyright 2015 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package registrar
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
|
||||
type testBackend struct {
|
||||
// contracts mock
|
||||
contracts map[string](map[string]string)
|
||||
}
|
||||
|
||||
var (
|
||||
text = "test"
|
||||
codehash = common.StringToHash("1234")
|
||||
hash = common.BytesToHash(crypto.Keccak256([]byte(text)))
|
||||
url = "bzz://bzzhash/my/path/contr.act"
|
||||
)
|
||||
|
||||
func NewTestBackend() *testBackend {
|
||||
self := &testBackend{}
|
||||
self.contracts = make(map[string](map[string]string))
|
||||
return self
|
||||
}
|
||||
|
||||
func (self *testBackend) initHashReg() {
|
||||
self.contracts[HashRegAddr[2:]] = make(map[string]string)
|
||||
key := storageAddress(storageMapping(storageIdx2Addr(1), codehash[:]))
|
||||
self.contracts[HashRegAddr[2:]][key] = hash.Hex()
|
||||
}
|
||||
|
||||
func (self *testBackend) initUrlHint() {
|
||||
self.contracts[UrlHintAddr[2:]] = make(map[string]string)
|
||||
mapaddr := storageMapping(storageIdx2Addr(1), hash[:])
|
||||
|
||||
key := storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(0)))
|
||||
self.contracts[UrlHintAddr[2:]][key] = common.ToHex([]byte(url))
|
||||
key = storageAddress(storageFixedArray(mapaddr, storageIdx2Addr(1)))
|
||||
self.contracts[UrlHintAddr[2:]][key] = "0x0"
|
||||
}
|
||||
|
||||
func (self *testBackend) StorageAt(ca, sa string) (res string) {
|
||||
c := self.contracts[ca]
|
||||
if c == nil {
|
||||
return "0x0"
|
||||
}
|
||||
res = c[sa]
|
||||
return
|
||||
}
|
||||
|
||||
func (self *testBackend) Transact(fromStr, toStr, nonceStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, error) {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (self *testBackend) Call(fromStr, toStr, valueStr, gasStr, gasPriceStr, codeStr string) (string, string, error) {
|
||||
return "", "", nil
|
||||
}
|
||||
|
||||
func TestSetGlobalRegistrar(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
_, err := res.SetGlobalRegistrar("addresshex", common.BigToAddress(common.Big1))
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v'", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToHash(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
|
||||
HashRegAddr = "0x0"
|
||||
got, err := res.HashToHash(codehash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashReg address is not set"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
HashRegAddr = common.BigToAddress(common.Big1).Hex() //[2:]
|
||||
got, err = res.HashToHash(codehash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashToHash: content hash not found for '" + codehash.Hex() + "'"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
b.initHashReg()
|
||||
got, err = res.HashToHash(codehash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
if got != hash {
|
||||
t.Errorf("incorrect result, expected '%v', got '%v'", hash.Hex(), got.Hex())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestHashToUrl(t *testing.T) {
|
||||
b := NewTestBackend()
|
||||
res := New(b)
|
||||
|
||||
UrlHintAddr = "0x0"
|
||||
got, err := res.HashToUrl(hash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "UrlHint address is not set"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
UrlHintAddr = common.BigToAddress(common.Big2).Hex() //[2:]
|
||||
got, err = res.HashToUrl(hash)
|
||||
if err == nil {
|
||||
t.Errorf("expected error")
|
||||
} else {
|
||||
exp := "HashToUrl: URL hint not found for '" + hash.Hex() + "'"
|
||||
if err.Error() != exp {
|
||||
t.Errorf("incorrect error, expected '%v', got '%v'", exp, err.Error())
|
||||
}
|
||||
}
|
||||
|
||||
b.initUrlHint()
|
||||
got, err = res.HashToUrl(hash)
|
||||
if err != nil {
|
||||
t.Errorf("expected no error, got %v", err)
|
||||
} else {
|
||||
if got != url {
|
||||
t.Errorf("incorrect result, expected '%v', got '%s'", url, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,14 +17,12 @@
|
||||
package common
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"math/rand"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -32,8 +30,6 @@ const (
|
||||
AddressLength = 20
|
||||
)
|
||||
|
||||
var hashJsonLengthErr = errors.New("common: unmarshalJSON failed: hash must be exactly 32 bytes")
|
||||
|
||||
type (
|
||||
// Hash represents the 32 byte Keccak256 hash of arbitrary data.
|
||||
Hash [HashLength]byte
|
||||
@@ -57,30 +53,16 @@ func HexToHash(s string) Hash { return BytesToHash(FromHex(s)) }
|
||||
func (h Hash) Str() string { return string(h[:]) }
|
||||
func (h Hash) Bytes() []byte { return h[:] }
|
||||
func (h Hash) Big() *big.Int { return Bytes2Big(h[:]) }
|
||||
func (h Hash) Hex() string { return "0x" + Bytes2Hex(h[:]) }
|
||||
func (h Hash) Hex() string { return hexutil.Encode(h[:]) }
|
||||
|
||||
// UnmarshalJSON parses a hash in its hex from to a hash.
|
||||
func (h *Hash) UnmarshalJSON(input []byte) error {
|
||||
length := len(input)
|
||||
if length >= 2 && input[0] == '"' && input[length-1] == '"' {
|
||||
input = input[1 : length-1]
|
||||
}
|
||||
// strip "0x" for length check
|
||||
if len(input) > 1 && strings.ToLower(string(input[:2])) == "0x" {
|
||||
input = input[2:]
|
||||
}
|
||||
|
||||
// validate the length of the input hash
|
||||
if len(input) != HashLength*2 {
|
||||
return hashJsonLengthErr
|
||||
}
|
||||
h.SetBytes(FromHex(string(input)))
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("Hash", input, h[:])
|
||||
}
|
||||
|
||||
// Serialize given hash to JSON
|
||||
func (h Hash) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(h.Hex())
|
||||
return hexutil.Bytes(h[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Sets the hash to the value of b. If b is larger than len(h) it will panic
|
||||
@@ -142,7 +124,7 @@ func (a Address) Str() string { return string(a[:]) }
|
||||
func (a Address) Bytes() []byte { return a[:] }
|
||||
func (a Address) Big() *big.Int { return Bytes2Big(a[:]) }
|
||||
func (a Address) Hash() Hash { return BytesToHash(a[:]) }
|
||||
func (a Address) Hex() string { return "0x" + Bytes2Hex(a[:]) }
|
||||
func (a Address) Hex() string { return hexutil.Encode(a[:]) }
|
||||
|
||||
// Sets the address to the value of b. If b is larger than len(a) it will panic
|
||||
func (a *Address) SetBytes(b []byte) {
|
||||
@@ -164,34 +146,12 @@ func (a *Address) Set(other Address) {
|
||||
|
||||
// Serialize given address to JSON
|
||||
func (a Address) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(a.Hex())
|
||||
return hexutil.Bytes(a[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// Parse address from raw json data
|
||||
func (a *Address) UnmarshalJSON(data []byte) error {
|
||||
if len(data) > 2 && data[0] == '"' && data[len(data)-1] == '"' {
|
||||
data = data[1 : len(data)-1]
|
||||
}
|
||||
|
||||
if len(data) > 2 && data[0] == '0' && data[1] == 'x' {
|
||||
data = data[2:]
|
||||
}
|
||||
|
||||
if len(data) != 2*AddressLength {
|
||||
return fmt.Errorf("Invalid address length, expected %d got %d bytes", 2*AddressLength, len(data))
|
||||
}
|
||||
|
||||
n, err := hex.Decode(a[:], data)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if n != AddressLength {
|
||||
return fmt.Errorf("Invalid address")
|
||||
}
|
||||
|
||||
a.Set(HexToAddress(string(data)))
|
||||
return nil
|
||||
func (a *Address) UnmarshalJSON(input []byte) error {
|
||||
return hexutil.UnmarshalJSON("Address", input, a[:])
|
||||
}
|
||||
|
||||
// PP Pretty Prints a byte slice in the following format:
|
||||
|
||||
@@ -18,7 +18,10 @@ package common
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
func TestBytesConversion(t *testing.T) {
|
||||
@@ -38,19 +41,26 @@ func TestHashJsonValidation(t *testing.T) {
|
||||
var tests = []struct {
|
||||
Prefix string
|
||||
Size int
|
||||
Error error
|
||||
Error string
|
||||
}{
|
||||
{"", 2, hashJsonLengthErr},
|
||||
{"", 62, hashJsonLengthErr},
|
||||
{"", 66, hashJsonLengthErr},
|
||||
{"", 65, hashJsonLengthErr},
|
||||
{"0X", 64, nil},
|
||||
{"0x", 64, nil},
|
||||
{"0x", 62, hashJsonLengthErr},
|
||||
{"", 62, hexutil.ErrMissingPrefix.Error()},
|
||||
{"0x", 66, "hex string has length 66, want 64 for Hash"},
|
||||
{"0x", 63, hexutil.ErrOddLength.Error()},
|
||||
{"0x", 0, "hex string has length 0, want 64 for Hash"},
|
||||
{"0x", 64, ""},
|
||||
{"0X", 64, ""},
|
||||
}
|
||||
for i, test := range tests {
|
||||
if err := h.UnmarshalJSON(append([]byte(test.Prefix), make([]byte, test.Size)...)); err != test.Error {
|
||||
t.Errorf("test #%d: error mismatch: have %v, want %v", i, err, test.Error)
|
||||
for _, test := range tests {
|
||||
input := `"` + test.Prefix + strings.Repeat("0", test.Size) + `"`
|
||||
err := h.UnmarshalJSON([]byte(input))
|
||||
if err == nil {
|
||||
if test.Error != "" {
|
||||
t.Errorf("%s: error mismatch: have nil, want %q", input, test.Error)
|
||||
}
|
||||
} else {
|
||||
if err.Error() != test.Error {
|
||||
t.Errorf("%s: error mismatch: have %q, want %q", input, err, test.Error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,14 +93,14 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
// Verify UncleHash before running other uncle validations
|
||||
unclesSha := types.CalcUncleHash(block.Uncles())
|
||||
if unclesSha != header.UncleHash {
|
||||
return fmt.Errorf("invalid uncles root hash. received=%x calculated=%x", header.UncleHash, unclesSha)
|
||||
return fmt.Errorf("invalid uncles root hash (remote: %x local: %x)", header.UncleHash, unclesSha)
|
||||
}
|
||||
|
||||
// The transactions Trie's root (R = (Tr [[i, RLP(T1)], [i, RLP(T2)], ... [n, RLP(Tn)]]))
|
||||
// can be used by light clients to make sure they've received the correct Txs
|
||||
txSha := types.DeriveSha(block.Transactions())
|
||||
if txSha != header.TxHash {
|
||||
return fmt.Errorf("invalid transaction root hash. received=%x calculated=%x", header.TxHash, txSha)
|
||||
return fmt.Errorf("invalid transaction root hash (remote: %x local: %x)", header.TxHash, txSha)
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -113,23 +113,23 @@ func (v *BlockValidator) ValidateBlock(block *types.Block) error {
|
||||
func (v *BlockValidator) ValidateState(block, parent *types.Block, statedb *state.StateDB, receipts types.Receipts, usedGas *big.Int) (err error) {
|
||||
header := block.Header()
|
||||
if block.GasUsed().Cmp(usedGas) != 0 {
|
||||
return ValidationError(fmt.Sprintf("gas used error (%v / %v)", block.GasUsed(), usedGas))
|
||||
return ValidationError(fmt.Sprintf("invalid gas used (remote: %v local: %v)", block.GasUsed(), usedGas))
|
||||
}
|
||||
// Validate the received block's bloom with the one derived from the generated receipts.
|
||||
// For valid blocks this should always validate to true.
|
||||
rbloom := types.CreateBloom(receipts)
|
||||
if rbloom != header.Bloom {
|
||||
return fmt.Errorf("unable to replicate block's bloom=%x vs calculated bloom=%x", header.Bloom, rbloom)
|
||||
return fmt.Errorf("invalid bloom (remote: %x local: %x)", header.Bloom, rbloom)
|
||||
}
|
||||
// Tre receipt Trie's root (R = (Tr [[H1, R1], ... [Hn, R1]]))
|
||||
receiptSha := types.DeriveSha(receipts)
|
||||
if receiptSha != header.ReceiptHash {
|
||||
return fmt.Errorf("invalid receipt root hash. received=%x calculated=%x", header.ReceiptHash, receiptSha)
|
||||
return fmt.Errorf("invalid receipt root hash (remote: %x local: %x)", header.ReceiptHash, receiptSha)
|
||||
}
|
||||
// Validate the state root against the received state root and throw
|
||||
// an error if they don't match.
|
||||
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 fmt.Errorf("invalid merkle root (remote: %x local: %x)", header.Root, root)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -223,7 +223,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||
|
||||
expd := CalcDifficulty(config, header.Time.Uint64(), parent.Time.Uint64(), parent.Number, parent.Difficulty)
|
||||
if expd.Cmp(header.Difficulty) != 0 {
|
||||
return fmt.Errorf("Difficulty check failed for header %v, %v", header.Difficulty, expd)
|
||||
return fmt.Errorf("Difficulty check failed for header (remote: %v local: %v)", header.Difficulty, expd)
|
||||
}
|
||||
|
||||
a := new(big.Int).Set(parent.GasLimit)
|
||||
@@ -232,7 +232,7 @@ func ValidateHeader(config *params.ChainConfig, pow pow.PoW, header *types.Heade
|
||||
b := new(big.Int).Set(parent.GasLimit)
|
||||
b = b.Div(b, params.GasLimitBoundDivisor)
|
||||
if !(a.Cmp(b) < 0) || (header.GasLimit.Cmp(params.MinGasLimit) == -1) {
|
||||
return fmt.Errorf("GasLimit check failed for header %v (%v > %v)", header.GasLimit, a, b)
|
||||
return fmt.Errorf("GasLimit check failed for header (remote: %v local_max: %v)", header.GasLimit, b)
|
||||
}
|
||||
|
||||
num := new(big.Int).Set(parent.Number)
|
||||
|
||||
@@ -152,9 +152,14 @@ func NewBlockChain(chainDb ethdb.Database, config *params.ChainConfig, pow pow.P
|
||||
// Check the current state of the block hashes and make sure that we do not have any of the bad blocks in our chain
|
||||
for hash, _ := range BadHashes {
|
||||
if header := bc.GetHeaderByHash(hash); header != nil {
|
||||
glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4])
|
||||
bc.SetHead(header.Number.Uint64() - 1)
|
||||
glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation")
|
||||
// get the canonical block corresponding to the offending header's number
|
||||
headerByNumber := bc.GetHeaderByNumber(header.Number.Uint64())
|
||||
// make sure the headerByNumber (if present) is in our current canonical chain
|
||||
if headerByNumber != nil && headerByNumber.Hash() == header.Hash() {
|
||||
glog.V(logger.Error).Infof("Found bad hash, rewinding chain to block #%d [%x…]", header.Number, header.ParentHash[:4])
|
||||
bc.SetHead(header.Number.Uint64() - 1)
|
||||
glog.V(logger.Error).Infoln("Chain rewind was successful, resuming normal operation")
|
||||
}
|
||||
}
|
||||
}
|
||||
// Take ownership of this particular state
|
||||
@@ -878,7 +883,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
|
||||
if BadHashes[block.Hash()] {
|
||||
err := BadHashError(block.Hash())
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
return i, err
|
||||
}
|
||||
// Stage 1 validation of the block using the chain's validator
|
||||
@@ -910,7 +915,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
continue
|
||||
}
|
||||
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
|
||||
return i, err
|
||||
}
|
||||
@@ -924,19 +929,19 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
err = self.stateCache.Reset(chain[i-1].Root())
|
||||
}
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, nil, err)
|
||||
return i, err
|
||||
}
|
||||
// Process block using the parent state as reference point.
|
||||
receipts, logs, usedGas, err := self.processor.Process(block, self.stateCache, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, receipts, err)
|
||||
return i, err
|
||||
}
|
||||
// Validate the state using the default validator
|
||||
err = self.Validator().ValidateState(block, self.GetBlock(block.ParentHash(), block.NumberU64()-1), self.stateCache, receipts, usedGas)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
self.reportBlock(block, receipts, err)
|
||||
return i, err
|
||||
}
|
||||
// Write state changes to database
|
||||
@@ -983,7 +988,7 @@ func (self *BlockChain) InsertChain(chain types.Blocks) (int, error) {
|
||||
glog.Infof("inserted forked block #%d [%x…] (TD=%v) in %9v: %3d txs %d uncles.", block.Number(), block.Hash().Bytes()[0:4], block.Difficulty(), common.PrettyDuration(time.Since(bstart)), len(block.Transactions()), len(block.Uncles()))
|
||||
}
|
||||
blockInsertTimer.UpdateSince(bstart)
|
||||
events = append(events, ChainSideEvent{block, logs})
|
||||
events = append(events, ChainSideEvent{block})
|
||||
|
||||
case SplitStatTy:
|
||||
events = append(events, ChainSplitEvent{block, logs})
|
||||
@@ -1057,24 +1062,25 @@ func countTransactions(chain []*types.Block) (c int) {
|
||||
// event about them
|
||||
func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
var (
|
||||
newChain types.Blocks
|
||||
oldChain types.Blocks
|
||||
commonBlock *types.Block
|
||||
oldStart = oldBlock
|
||||
newStart = newBlock
|
||||
deletedTxs types.Transactions
|
||||
deletedLogs vm.Logs
|
||||
deletedLogsByHash = make(map[common.Hash]vm.Logs)
|
||||
newChain types.Blocks
|
||||
oldChain types.Blocks
|
||||
commonBlock *types.Block
|
||||
oldStart = oldBlock
|
||||
newStart = newBlock
|
||||
deletedTxs types.Transactions
|
||||
deletedLogs vm.Logs
|
||||
// collectLogs collects the logs that were generated during the
|
||||
// processing of the block that corresponds with the given hash.
|
||||
// These logs are later announced as deleted.
|
||||
collectLogs = func(h common.Hash) {
|
||||
// Coalesce logs
|
||||
// Coalesce logs and set 'Removed'.
|
||||
receipts := GetBlockReceipts(self.chainDb, h, self.hc.GetBlockNumber(h))
|
||||
for _, receipt := range receipts {
|
||||
deletedLogs = append(deletedLogs, receipt.Logs...)
|
||||
|
||||
deletedLogsByHash[h] = receipt.Logs
|
||||
for _, log := range receipt.Logs {
|
||||
del := *log
|
||||
del.Removed = true
|
||||
deletedLogs = append(deletedLogs, &del)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -1168,7 +1174,7 @@ func (self *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
|
||||
if len(oldChain) > 0 {
|
||||
go func() {
|
||||
for _, block := range oldChain {
|
||||
self.eventMux.Post(ChainSideEvent{Block: block, Logs: deletedLogsByHash[block.Hash()]})
|
||||
self.eventMux.Post(ChainSideEvent{Block: block})
|
||||
}
|
||||
}()
|
||||
}
|
||||
@@ -1207,10 +1213,23 @@ func (self *BlockChain) update() {
|
||||
}
|
||||
|
||||
// reportBlock logs a bad block error.
|
||||
func reportBlock(block *types.Block, err error) {
|
||||
func (bc *BlockChain) reportBlock(block *types.Block, receipts types.Receipts, err error) {
|
||||
if glog.V(logger.Error) {
|
||||
glog.Errorf("Bad block #%v (%s)\n", block.Number(), block.Hash().Hex())
|
||||
glog.Errorf(" %v", err)
|
||||
var receiptString string
|
||||
for _, receipt := range receipts {
|
||||
receiptString += fmt.Sprintf("\t%v\n", receipt)
|
||||
}
|
||||
glog.Errorf(`
|
||||
########## BAD BLOCK #########
|
||||
Chain config: %v
|
||||
|
||||
Number: %v
|
||||
Hash: 0x%x
|
||||
%v
|
||||
|
||||
Error: %v
|
||||
##############################
|
||||
`, bc.config, block.Number(), block.Hash(), receiptString, err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -143,12 +143,12 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
|
||||
}
|
||||
receipts, _, usedGas, err := blockchain.Processor().Process(block, statedb, vm.Config{})
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
err = blockchain.Validator().ValidateState(block, blockchain.GetBlockByHash(block.ParentHash()), statedb, receipts, usedGas)
|
||||
if err != nil {
|
||||
reportBlock(block, err)
|
||||
blockchain.reportBlock(block, receipts, err)
|
||||
return err
|
||||
}
|
||||
blockchain.mu.Lock()
|
||||
@@ -1136,13 +1136,14 @@ func TestCanonicalBlockRetrieval(t *testing.T) {
|
||||
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
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
funds = big.NewInt(1000000000)
|
||||
deleteAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds}, GenesisAccount{deleteAddr, new(big.Int)})
|
||||
config = ¶ms.ChainConfig{ChainId: big.NewInt(1), EIP155Block: big.NewInt(2), HomesteadBlock: new(big.Int)}
|
||||
mux event.TypeMux
|
||||
)
|
||||
|
||||
blockchain, _ := NewBlockChain(db, config, FakePow{}, &mux)
|
||||
@@ -1231,3 +1232,66 @@ func TestEIP155Transition(t *testing.T) {
|
||||
t.Error("expected error:", types.ErrInvalidChainId)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEIP161AccountRemoval(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)
|
||||
theAddr = common.Address{1}
|
||||
genesis = WriteGenesisBlockForTesting(db, GenesisAccount{address, funds})
|
||||
config = ¶ms.ChainConfig{
|
||||
ChainId: big.NewInt(1),
|
||||
HomesteadBlock: new(big.Int),
|
||||
EIP155Block: new(big.Int),
|
||||
EIP158Block: big.NewInt(2),
|
||||
}
|
||||
mux event.TypeMux
|
||||
|
||||
blockchain, _ = NewBlockChain(db, config, FakePow{}, &mux)
|
||||
)
|
||||
blocks, _ := GenerateChain(config, genesis, db, 3, func(i int, block *BlockGen) {
|
||||
var (
|
||||
tx *types.Transaction
|
||||
err error
|
||||
signer = types.NewEIP155Signer(config.ChainId)
|
||||
)
|
||||
switch i {
|
||||
case 0:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
case 1:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
case 2:
|
||||
tx, err = types.NewTransaction(block.TxNonce(address), theAddr, new(big.Int), big.NewInt(21000), new(big.Int), nil).SignECDSA(signer, key)
|
||||
}
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
block.AddTx(tx)
|
||||
})
|
||||
// account must exist pre eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[0]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("expected account to exist")
|
||||
}
|
||||
|
||||
// account needs to be deleted post eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[1]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
}
|
||||
|
||||
// account musn't be created post eip 161
|
||||
if _, err := blockchain.InsertChain(types.Blocks{blocks[2]}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if blockchain.stateCache.Exist(theAddr) {
|
||||
t.Error("account should not expect")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,4 +21,5 @@ import "github.com/ethereum/go-ethereum/common"
|
||||
// Set of manually tracked bad hashes (usually hard forks)
|
||||
var BadHashes = map[common.Hash]bool{
|
||||
common.HexToHash("05bef30ef572270f654746da22639a7a0c97dd97a7050b9e252391996aaeb689"): true,
|
||||
common.HexToHash("7d05d08cbc596a2e5e4f13b80a743e53e09221b5323c3a61946b20873e58583f"): true,
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -38,9 +38,6 @@ type PendingLogsEvent struct {
|
||||
// PendingStateEvent is posted pre mining and notifies of pending state changes.
|
||||
type PendingStateEvent struct{}
|
||||
|
||||
// NewBlockEvent is posted when a block has been imported.
|
||||
type NewBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// NewMinedBlockEvent is posted when a block has been imported.
|
||||
type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
@@ -64,7 +61,6 @@ type ChainEvent struct {
|
||||
|
||||
type ChainSideEvent struct {
|
||||
Block *types.Block
|
||||
Logs vm.Logs
|
||||
}
|
||||
|
||||
type PendingBlockEvent struct {
|
||||
|
||||
73
core/evm.go
Normal file
73
core/evm.go
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
)
|
||||
|
||||
// BlockFetcher retrieves headers by their hash
|
||||
type HeaderFetcher interface {
|
||||
// GetHeader returns the hash corresponding to their hash
|
||||
GetHeader(common.Hash, uint64) *types.Header
|
||||
}
|
||||
|
||||
// NewEVMContext creates a new context for use in the EVM.
|
||||
func NewEVMContext(msg Message, header *types.Header, chain HeaderFetcher) vm.Context {
|
||||
return vm.Context{
|
||||
CanTransfer: CanTransfer,
|
||||
Transfer: Transfer,
|
||||
GetHash: GetHashFn(header, chain),
|
||||
|
||||
Origin: msg.From(),
|
||||
Coinbase: header.Coinbase,
|
||||
BlockNumber: new(big.Int).Set(header.Number),
|
||||
Time: new(big.Int).Set(header.Time),
|
||||
Difficulty: new(big.Int).Set(header.Difficulty),
|
||||
GasLimit: new(big.Int).Set(header.GasLimit),
|
||||
GasPrice: new(big.Int).Set(msg.GasPrice()),
|
||||
}
|
||||
}
|
||||
|
||||
// GetHashFn returns a GetHashFunc which retrieves header hashes by number
|
||||
func GetHashFn(ref *types.Header, chain HeaderFetcher) func(n uint64) common.Hash {
|
||||
return func(n uint64) common.Hash {
|
||||
for header := chain.GetHeader(ref.ParentHash, ref.Number.Uint64()-1); header != nil; header = chain.GetHeader(header.ParentHash, header.Number.Uint64()-1) {
|
||||
if header.Number.Uint64() == n {
|
||||
return header.Hash()
|
||||
}
|
||||
}
|
||||
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
|
||||
// CanTransfer checks wether there are enough funds in the address' account to make a transfer.
|
||||
// This does not take the necessary gas in to account to make the transfer valid.
|
||||
func CanTransfer(db vm.StateDB, addr common.Address, amount *big.Int) bool {
|
||||
return db.GetBalance(addr).Cmp(amount) >= 0
|
||||
}
|
||||
|
||||
// Transfer subtracts amount from sender and adds amount to recipient using the given Db
|
||||
func Transfer(db vm.StateDB, sender, recipient common.Address, amount *big.Int) {
|
||||
db.SubBalance(sender, amount)
|
||||
db.AddBalance(recipient, amount)
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// 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) {
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, vm.DepthError
|
||||
}
|
||||
if !env.CanTransfer(caller.Address(), value) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
snapshotPreTransfer := env.SnapshotDatabase()
|
||||
var (
|
||||
from = env.Db().GetAccount(caller.Address())
|
||||
to vm.Account
|
||||
)
|
||||
if !env.Db().Exist(addr) {
|
||||
if vm.Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber()) && value.BitLen() == 0 {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
to = env.Db().CreateAccount(addr)
|
||||
} else {
|
||||
to = env.Db().GetAccount(addr)
|
||||
}
|
||||
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(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = env.Vm().Run(contract, input)
|
||||
// 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 {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.RevertToSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
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) {
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, vm.DepthError
|
||||
}
|
||||
if !env.CanTransfer(caller.Address(), value) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
var (
|
||||
snapshotPreTransfer = env.SnapshotDatabase()
|
||||
to = env.Db().GetAccount(caller.Address())
|
||||
)
|
||||
// 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(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = env.Vm().Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.RevertToSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
|
||||
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) {
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, common.Address{}, vm.DepthError
|
||||
}
|
||||
if !env.CanTransfer(caller.Address(), value) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
|
||||
return nil, common.Address{}, ValueTransferErr("insufficient funds to transfer value. Req %v, has %v", value, env.Db().GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
// Create a new account on the state
|
||||
nonce := env.Db().GetNonce(caller.Address())
|
||||
env.Db().SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
snapshotPreTransfer := env.SnapshotDatabase()
|
||||
var (
|
||||
addr = crypto.CreateAddress(caller.Address(), nonce)
|
||||
from = env.Db().GetAccount(caller.Address())
|
||||
to = env.Db().CreateAccount(addr)
|
||||
)
|
||||
if env.ChainConfig().IsEIP158(env.BlockNumber()) {
|
||||
env.Db().SetNonce(addr, 1)
|
||||
}
|
||||
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(&addr, crypto.Keccak256Hash(code), code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = env.Vm().Run(contract, nil)
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
// calculate the gas required to store the code. If the code could not
|
||||
// be stored due to not enough gas set an error and let it be handled
|
||||
// by the error checking condition below.
|
||||
if err == nil && !maxCodeSizeExceeded {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if contract.UseGas(dataGas) {
|
||||
env.Db().SetCode(addr, ret)
|
||||
} else {
|
||||
err = vm.CodeStoreOutOfGasError
|
||||
}
|
||||
}
|
||||
|
||||
// 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 maxCodeSizeExceeded ||
|
||||
(err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber()) || err != vm.CodeStoreOutOfGasError)) {
|
||||
contract.UseGas(contract.Gas)
|
||||
env.RevertToSnapshot(snapshotPreTransfer)
|
||||
|
||||
// Nothing should be returned when an error is thrown.
|
||||
return nil, addr, err
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
}
|
||||
|
||||
// DelegateCall is equivalent to CallCode except that sender and value propagates from parent scope to child scope
|
||||
func DelegateCall(env vm.Environment, caller vm.ContractRef, addr common.Address, input []byte, gas, gasPrice *big.Int) (ret []byte, err error) {
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth() > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas, gasPrice)
|
||||
return nil, vm.DepthError
|
||||
}
|
||||
|
||||
var (
|
||||
snapshot = env.SnapshotDatabase()
|
||||
to = env.Db().GetAccount(caller.Address())
|
||||
)
|
||||
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := vm.NewContract(caller, to, caller.Value(), gas, gasPrice).AsDelegate()
|
||||
contract.SetCallCode(&addr, env.Db().GetCodeHash(addr), env.Db().GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err = env.Vm().Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// generic transfer method
|
||||
func Transfer(from, to vm.Account, amount *big.Int) {
|
||||
from.SubBalance(amount)
|
||||
to.AddBalance(amount)
|
||||
}
|
||||
@@ -17,6 +17,7 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
@@ -174,7 +175,7 @@ func WriteDefaultGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
// WriteTestNetGenesisBlock assembles the Morden test network genesis block and
|
||||
// writes it - along with all associated state - into a chain database.
|
||||
func WriteTestNetGenesisBlock(chainDb ethdb.Database) (*types.Block, error) {
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(TestNetGenesisBlock()))
|
||||
return WriteGenesisBlock(chainDb, strings.NewReader(DefaultTestnetGenesisBlock()))
|
||||
}
|
||||
|
||||
// WriteOlympicGenesisBlock assembles the Olympic genesis block and writes it
|
||||
@@ -197,6 +198,15 @@ func DefaultGenesisBlock() string {
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
func DefaultTestnetGenesisBlock() string {
|
||||
reader := bzip2.NewReader(base64.NewDecoder(base64.StdEncoding, strings.NewReader(defaultTestnetGenesisBlock)))
|
||||
blob, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("failed to load default genesis: %v", err))
|
||||
}
|
||||
return string(blob)
|
||||
}
|
||||
|
||||
// OlympicGenesisBlock assembles a JSON string representing the Olympic genesis
|
||||
// block.
|
||||
func OlympicGenesisBlock() string {
|
||||
@@ -220,25 +230,3 @@ func OlympicGenesisBlock() string {
|
||||
}
|
||||
}`, types.EncodeNonce(42), params.GenesisGasLimit.Bytes(), params.GenesisDifficulty.Bytes())
|
||||
}
|
||||
|
||||
// TestNetGenesisBlock assembles a JSON string representing the Morden test net
|
||||
// genenis block.
|
||||
func TestNetGenesisBlock() string {
|
||||
return fmt.Sprintf(`{
|
||||
"nonce": "0x%x",
|
||||
"difficulty": "0x20000",
|
||||
"mixhash": "0x00000000000000000000000000000000000000647572616c65787365646c6578",
|
||||
"coinbase": "0x0000000000000000000000000000000000000000",
|
||||
"timestamp": "0x00",
|
||||
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
||||
"extraData": "0x",
|
||||
"gasLimit": "0x2FEFD8",
|
||||
"alloc": {
|
||||
"0000000000000000000000000000000000000001": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000002": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000003": { "balance": "1" },
|
||||
"0000000000000000000000000000000000000004": { "balance": "1" },
|
||||
"102e61f5d8f9bc71d0ad4a084df4e65e05ce0e1c": { "balance": "1606938044258990275541962092341162602522202993782792835301376" }
|
||||
}
|
||||
}`, types.EncodeNonce(0x6d6f7264656e))
|
||||
}
|
||||
|
||||
@@ -67,10 +67,13 @@ type (
|
||||
addLogChange struct {
|
||||
txhash common.Hash
|
||||
}
|
||||
touchChange struct {
|
||||
account *common.Address
|
||||
prev bool
|
||||
}
|
||||
)
|
||||
|
||||
func (ch createObjectChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).deleted = true
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
@@ -87,6 +90,15 @@ func (ch suicideChange) undo(s *StateDB) {
|
||||
}
|
||||
}
|
||||
|
||||
var ripemd = common.HexToAddress("0000000000000000000000000000000000000003")
|
||||
|
||||
func (ch touchChange) undo(s *StateDB) {
|
||||
if !ch.prev && *ch.account != ripemd {
|
||||
delete(s.stateObjects, *ch.account)
|
||||
delete(s.stateObjectsDirty, *ch.account)
|
||||
}
|
||||
}
|
||||
|
||||
func (ch balanceChange) undo(s *StateDB) {
|
||||
s.GetStateObject(*ch.account).setBalance(ch.prev)
|
||||
}
|
||||
|
||||
@@ -87,6 +87,7 @@ type StateObject struct {
|
||||
// during the "update" phase of the state transition.
|
||||
dirtyCode bool // true if the code was updated
|
||||
suicided bool
|
||||
touched bool
|
||||
deleted bool
|
||||
onDirty func(addr common.Address) // Callback method to mark a state object newly dirty
|
||||
}
|
||||
@@ -139,6 +140,18 @@ func (self *StateObject) markSuicided() {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StateObject) touch() {
|
||||
c.db.journal = append(c.db.journal, touchChange{
|
||||
account: &c.address,
|
||||
prev: c.touched,
|
||||
})
|
||||
if c.onDirty != nil {
|
||||
c.onDirty(c.Address())
|
||||
c.onDirty = nil
|
||||
}
|
||||
c.touched = true
|
||||
}
|
||||
|
||||
func (c *StateObject) getTrie(db trie.Database) *trie.SecureTrie {
|
||||
if c.trie == nil {
|
||||
var err error
|
||||
@@ -231,7 +244,11 @@ func (self *StateObject) CommitTrie(db trie.Database, dbw trie.DatabaseWriter) e
|
||||
func (c *StateObject) AddBalance(amount *big.Int) {
|
||||
// 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() {
|
||||
if amount.Cmp(common.Big0) == 0 {
|
||||
if c.empty() {
|
||||
c.touch()
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
c.SetBalance(new(big.Int).Add(c.Balance(), amount))
|
||||
@@ -271,7 +288,7 @@ 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 (c *StateObject) ReturnGas(gas *big.Int) {}
|
||||
|
||||
func (self *StateObject) deepCopy(db *StateDB, onDirty func(addr common.Address)) *StateObject {
|
||||
stateObject := newObject(db, self.address, self.data, onDirty)
|
||||
|
||||
@@ -34,10 +34,6 @@ import (
|
||||
lru "github.com/hashicorp/golang-lru"
|
||||
)
|
||||
|
||||
// The starting nonce determines the default nonce when new accounts are being
|
||||
// created.
|
||||
var StartingNonce uint64
|
||||
|
||||
// Trie cache generation limit after which to evic trie nodes from memory.
|
||||
var MaxTrieCacheGen = uint16(120)
|
||||
|
||||
@@ -239,7 +235,7 @@ func (self *StateDB) GetNonce(addr common.Address) uint64 {
|
||||
return stateObject.Nonce()
|
||||
}
|
||||
|
||||
return StartingNonce
|
||||
return 0
|
||||
}
|
||||
|
||||
func (self *StateDB) GetCode(addr common.Address) []byte {
|
||||
@@ -297,6 +293,7 @@ func (self *StateDB) HasSuicided(addr common.Address) bool {
|
||||
* SETTERS
|
||||
*/
|
||||
|
||||
// AddBalance adds amount to the account associated with addr
|
||||
func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@@ -304,6 +301,14 @@ func (self *StateDB) AddBalance(addr common.Address, amount *big.Int) {
|
||||
}
|
||||
}
|
||||
|
||||
// SubBalance subtracts amount from the account associated with addr
|
||||
func (self *StateDB) SubBalance(addr common.Address, amount *big.Int) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
stateObject.SubBalance(amount)
|
||||
}
|
||||
}
|
||||
|
||||
func (self *StateDB) SetBalance(addr common.Address, amount *big.Int) {
|
||||
stateObject := self.GetOrNewStateObject(addr)
|
||||
if stateObject != nil {
|
||||
@@ -423,7 +428,7 @@ func (self *StateDB) MarkStateObjectDirty(addr common.Address) {
|
||||
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
|
||||
newobj.setNonce(0) // sets the object to dirty
|
||||
if prev == nil {
|
||||
if glog.V(logger.Core) {
|
||||
glog.Infof("(+) %x\n", addr)
|
||||
|
||||
@@ -116,6 +116,7 @@ func TestIntermediateLeaks(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSnapshotRandom(t *testing.T) {
|
||||
t.Skip("@fjl fix me please")
|
||||
config := &quick.Config{MaxCount: 1000}
|
||||
err := quick.Check((*snapshotTest).run, config)
|
||||
if cerr, ok := err.(*quick.CheckError); ok {
|
||||
@@ -354,3 +355,22 @@ func (test *snapshotTest) checkEqual(state, checkstate *StateDB) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestTouchDelete(t *testing.T) {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
state, _ := New(common.Hash{}, db)
|
||||
state.GetOrNewStateObject(common.Address{})
|
||||
root, _ := state.Commit(false)
|
||||
state.Reset(root)
|
||||
|
||||
snapshot := state.Snapshot()
|
||||
state.AddBalance(common.Address{}, new(big.Int))
|
||||
if len(state.stateObjectsDirty) != 1 {
|
||||
t.Fatal("expected one dirty state object")
|
||||
}
|
||||
|
||||
state.RevertToSnapshot(snapshot)
|
||||
if len(state.stateObjectsDirty) != 0 {
|
||||
t.Fatal("expected no dirty state object")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,6 +72,7 @@ func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg
|
||||
}
|
||||
// Iterate over and process the individual transactions
|
||||
for i, tx := range block.Transactions() {
|
||||
//fmt.Println("tx:", i)
|
||||
statedb.StartRecord(tx.Hash(), block.Hash(), i)
|
||||
receipt, logs, _, err := ApplyTransaction(p.config, p.bc, gp, statedb, header, tx, totalUsedGas, cfg)
|
||||
if err != nil {
|
||||
@@ -95,28 +96,36 @@ func ApplyTransaction(config *params.ChainConfig, bc *BlockChain, gp *GasPool, s
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
_, gas, err := ApplyMessage(NewEnv(statedb, config, bc, msg, header, cfg), msg, gp)
|
||||
// Create a new context to be used in the EVM environment
|
||||
context := NewEVMContext(msg, header, bc)
|
||||
// Create a new environment which holds all relevant information
|
||||
// about the transaction and calling mechanisms.
|
||||
vmenv := vm.NewEnvironment(context, statedb, config, vm.Config{})
|
||||
// Apply the transaction to the current state (included in the env)
|
||||
_, gas, err := ApplyMessage(vmenv, msg, gp)
|
||||
if err != nil {
|
||||
return nil, nil, nil, err
|
||||
}
|
||||
|
||||
// Update the state with pending changes
|
||||
usedGas.Add(usedGas, gas)
|
||||
// Create a new receipt for the transaction, storing the intermediate root and gas used by the tx
|
||||
// based on the eip phase, we're passing wether the root touch-delete accounts.
|
||||
receipt := types.NewReceipt(statedb.IntermediateRoot(config.IsEIP158(header.Number)).Bytes(), usedGas)
|
||||
receipt.TxHash = tx.Hash()
|
||||
receipt.GasUsed = new(big.Int).Set(gas)
|
||||
if MessageCreatesContract(msg) {
|
||||
receipt.ContractAddress = crypto.CreateAddress(msg.From(), tx.Nonce())
|
||||
// if the transaction created a contract, store the creation address in the receipt.
|
||||
if msg.To() == nil {
|
||||
receipt.ContractAddress = crypto.CreateAddress(vmenv.Context.Origin, tx.Nonce())
|
||||
}
|
||||
|
||||
logs := statedb.GetLogs(tx.Hash())
|
||||
receipt.Logs = logs
|
||||
// Set the receipt logs and create a bloom for filtering
|
||||
receipt.Logs = statedb.GetLogs(tx.Hash())
|
||||
receipt.Bloom = types.CreateBloom(types.Receipts{receipt})
|
||||
|
||||
glog.V(logger.Debug).Infoln(receipt)
|
||||
|
||||
return receipt, logs, gas, err
|
||||
return receipt, receipt.Logs, gas, err
|
||||
}
|
||||
|
||||
// AccumulateRewards credits the coinbase of the given block with the
|
||||
|
||||
@@ -55,9 +55,9 @@ type StateTransition struct {
|
||||
initialGas *big.Int
|
||||
value *big.Int
|
||||
data []byte
|
||||
state vm.Database
|
||||
state vm.StateDB
|
||||
|
||||
env vm.Environment
|
||||
env *vm.Environment
|
||||
}
|
||||
|
||||
// Message represents a message sent to a contract.
|
||||
@@ -106,7 +106,7 @@ func IntrinsicGas(data []byte, contractCreation, homestead bool) *big.Int {
|
||||
}
|
||||
|
||||
// NewStateTransition initialises and returns a new state transition object.
|
||||
func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTransition {
|
||||
func NewStateTransition(env *vm.Environment, msg Message, gp *GasPool) *StateTransition {
|
||||
return &StateTransition{
|
||||
gp: gp,
|
||||
env: env,
|
||||
@@ -116,7 +116,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
|
||||
initialGas: new(big.Int),
|
||||
value: msg.Value(),
|
||||
data: msg.Data(),
|
||||
state: env.Db(),
|
||||
state: env.StateDB,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ func NewStateTransition(env vm.Environment, msg Message, gp *GasPool) *StateTran
|
||||
// the gas used (which includes gas refunds) and an error if it failed. An error always
|
||||
// indicates a core error meaning that the message would always fail for that particular
|
||||
// state and would never be accepted within a block.
|
||||
func ApplyMessage(env vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
|
||||
func ApplyMessage(env *vm.Environment, msg Message, gp *GasPool) ([]byte, *big.Int, error) {
|
||||
st := NewStateTransition(env, msg, gp)
|
||||
|
||||
ret, _, gasUsed, err := st.TransitionDb()
|
||||
@@ -217,47 +217,44 @@ func (self *StateTransition) TransitionDb() (ret []byte, requiredGas, usedGas *b
|
||||
msg := self.msg
|
||||
sender := self.from() // err checked in preCheck
|
||||
|
||||
homestead := self.env.ChainConfig().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 {
|
||||
return nil, nil, nil, InvalidTxError(err)
|
||||
}
|
||||
|
||||
vmenv := self.env
|
||||
//var addr common.Address
|
||||
var (
|
||||
vmenv = self.env
|
||||
// vm errors do not effect consensus and are therefor
|
||||
// not assigned to err, except for insufficient balance
|
||||
// error.
|
||||
vmerr error
|
||||
)
|
||||
if contractCreation {
|
||||
ret, _, err = vmenv.Create(sender, self.data, self.gas, self.gasPrice, self.value)
|
||||
ret, _, vmerr = vmenv.Create(sender, self.data, self.gas, self.value)
|
||||
if homestead && err == vm.CodeStoreOutOfGasError {
|
||||
self.gas = Big0
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
ret = nil
|
||||
glog.V(logger.Core).Infoln("VM create err:", err)
|
||||
}
|
||||
} else {
|
||||
// Increment the nonce for the next transaction
|
||||
self.state.SetNonce(sender.Address(), self.state.GetNonce(sender.Address())+1)
|
||||
ret, err = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.gasPrice, self.value)
|
||||
if err != nil {
|
||||
glog.V(logger.Core).Infoln("VM call err:", err)
|
||||
ret, vmerr = vmenv.Call(sender, self.to().Address(), self.data, self.gas, self.value)
|
||||
}
|
||||
if vmerr != nil {
|
||||
glog.V(logger.Core).Infoln("vm returned with error:", err)
|
||||
// The only possible consensus-error would be if there wasn't
|
||||
// sufficient balance to make the transfer happen. The first
|
||||
// balance transfer may never fail.
|
||||
if vmerr == vm.ErrInsufficientBalance {
|
||||
return nil, nil, nil, InvalidTxError(vmerr)
|
||||
}
|
||||
}
|
||||
|
||||
if err != nil && IsValueTransferErr(err) {
|
||||
return nil, nil, nil, InvalidTxError(err)
|
||||
}
|
||||
|
||||
// We aren't interested in errors here. Errors returned by the VM are non-consensus errors and therefor shouldn't bubble up
|
||||
if err != nil {
|
||||
err = nil
|
||||
}
|
||||
|
||||
requiredGas = new(big.Int).Set(self.gasUsed())
|
||||
|
||||
self.refundGas()
|
||||
self.state.AddBalance(self.env.Coinbase(), new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
self.state.AddBalance(self.env.Coinbase, new(big.Int).Mul(self.gasUsed(), self.gasPrice))
|
||||
|
||||
return ret, requiredGas, self.gasUsed(), err
|
||||
}
|
||||
|
||||
@@ -124,6 +124,8 @@ func NewTxPool(config *params.ChainConfig, eventMux *event.TypeMux, currentState
|
||||
quit: make(chan struct{}),
|
||||
}
|
||||
|
||||
pool.resetState()
|
||||
|
||||
pool.wg.Add(2)
|
||||
go pool.eventLoop()
|
||||
go pool.expirationLoop()
|
||||
@@ -176,7 +178,7 @@ 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.demoteUnexecutables()
|
||||
pool.demoteUnexecutables(currentState)
|
||||
|
||||
// Update all accounts to the latest known pending nonce
|
||||
for addr, list := range pool.pending {
|
||||
@@ -185,7 +187,7 @@ func (pool *TxPool) resetState() {
|
||||
}
|
||||
// Check the queue and move transactions over to the pending if possible
|
||||
// or remove those that have become invalid
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(currentState)
|
||||
}
|
||||
|
||||
func (pool *TxPool) Stop() {
|
||||
@@ -237,21 +239,26 @@ func (pool *TxPool) Content() (map[common.Address]types.Transactions, map[common
|
||||
// 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 {
|
||||
func (pool *TxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
state, err := pool.currentState()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// check queue first
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(state)
|
||||
|
||||
// invalidate any txs
|
||||
pool.demoteUnexecutables()
|
||||
pool.demoteUnexecutables(state)
|
||||
|
||||
pending := make(map[common.Address]types.Transactions)
|
||||
for addr, list := range pool.pending {
|
||||
pending[addr] = list.Flatten()
|
||||
}
|
||||
return pending
|
||||
return pending, nil
|
||||
}
|
||||
|
||||
// SetLocal marks a transaction as local, skipping gas price
|
||||
@@ -372,10 +379,6 @@ func (pool *TxPool) enqueueTx(hash common.Hash, tx *types.Transaction) {
|
||||
//
|
||||
// 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()
|
||||
}
|
||||
// Try to insert the transaction into the pending queue
|
||||
if pool.pending[addr] == nil {
|
||||
pool.pending[addr] = newTxList(true)
|
||||
@@ -410,13 +413,19 @@ func (pool *TxPool) Add(tx *types.Transaction) error {
|
||||
if err := pool.add(tx); err != nil {
|
||||
return err
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
|
||||
state, err := pool.currentState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pool.promoteExecutables(state)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// AddBatch attempts to queue a batch of transactions.
|
||||
func (pool *TxPool) AddBatch(txs []*types.Transaction) {
|
||||
func (pool *TxPool) AddBatch(txs []*types.Transaction) error {
|
||||
pool.mu.Lock()
|
||||
defer pool.mu.Unlock()
|
||||
|
||||
@@ -425,7 +434,15 @@ func (pool *TxPool) AddBatch(txs []*types.Transaction) {
|
||||
glog.V(logger.Debug).Infoln("tx error:", err)
|
||||
}
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
|
||||
state, err := pool.currentState()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pool.promoteExecutables(state)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Get returns a transaction if it is contained in the pool
|
||||
@@ -499,17 +516,7 @@ func (pool *TxPool) removeTx(hash common.Hash) {
|
||||
// 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()
|
||||
}
|
||||
// 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
|
||||
}
|
||||
func (pool *TxPool) promoteExecutables(state *state.StateDB) {
|
||||
// Iterate over all accounts and promote any executable transactions
|
||||
queued := uint64(0)
|
||||
for addr, list := range pool.queue {
|
||||
@@ -645,13 +652,7 @@ func (pool *TxPool) promoteExecutables() {
|
||||
// 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
|
||||
}
|
||||
func (pool *TxPool) demoteUnexecutables(state *state.StateDB) {
|
||||
// Iterate over all accounts and demote any non-executable transactions
|
||||
for addr, list := range pool.pending {
|
||||
nonce := state.GetNonce(addr)
|
||||
|
||||
@@ -51,6 +51,80 @@ func deriveSender(tx *types.Transaction) (common.Address, error) {
|
||||
return types.Sender(types.HomesteadSigner{}, tx)
|
||||
}
|
||||
|
||||
// This test simulates a scenario where a new block is imported during a
|
||||
// state reset and tests whether the pending state is in sync with the
|
||||
// block head event that initiated the resetState().
|
||||
func TestStateChangeDuringPoolReset(t *testing.T) {
|
||||
var (
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
key, _ = crypto.GenerateKey()
|
||||
address = crypto.PubkeyToAddress(key.PublicKey)
|
||||
mux = new(event.TypeMux)
|
||||
statedb, _ = state.New(common.Hash{}, db)
|
||||
trigger = false
|
||||
)
|
||||
|
||||
// setup pool with 2 transaction in it
|
||||
statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether))
|
||||
|
||||
tx0 := transaction(0, big.NewInt(100000), key)
|
||||
tx1 := transaction(1, big.NewInt(100000), key)
|
||||
|
||||
// stateFunc is used multiple times to reset the pending state.
|
||||
// when simulate is true it will create a state that indicates
|
||||
// that tx0 and tx1 are included in the chain.
|
||||
stateFunc := func() (*state.StateDB, error) {
|
||||
// delay "state change" by one. The tx pool fetches the
|
||||
// state multiple times and by delaying it a bit we simulate
|
||||
// a state change between those fetches.
|
||||
stdb := statedb
|
||||
if trigger {
|
||||
statedb, _ = state.New(common.Hash{}, db)
|
||||
// simulate that the new head block included tx0 and tx1
|
||||
statedb.SetNonce(address, 2)
|
||||
statedb.SetBalance(address, new(big.Int).Mul(common.Big1, common.Ether))
|
||||
trigger = false
|
||||
}
|
||||
return stdb, nil
|
||||
}
|
||||
|
||||
gasLimitFunc := func() *big.Int { return big.NewInt(1000000000) }
|
||||
|
||||
txpool := NewTxPool(testChainConfig(), mux, stateFunc, gasLimitFunc)
|
||||
txpool.resetState()
|
||||
|
||||
nonce := txpool.State().GetNonce(address)
|
||||
if nonce != 0 {
|
||||
t.Fatalf("Invalid nonce, want 0, got %d", nonce)
|
||||
}
|
||||
|
||||
txpool.AddBatch(types.Transactions{tx0, tx1})
|
||||
|
||||
nonce = txpool.State().GetNonce(address)
|
||||
if nonce != 2 {
|
||||
t.Fatalf("Invalid nonce, want 2, got %d", nonce)
|
||||
}
|
||||
|
||||
// trigger state change in the background
|
||||
trigger = true
|
||||
|
||||
txpool.resetState()
|
||||
|
||||
pendingTx, err := txpool.Pending()
|
||||
if err != nil {
|
||||
t.Fatalf("Could not fetch pending transactions: %v", err)
|
||||
}
|
||||
|
||||
for addr, txs := range pendingTx {
|
||||
t.Logf("%0x: %d\n", addr, len(txs))
|
||||
}
|
||||
|
||||
nonce = txpool.State().GetNonce(address)
|
||||
if nonce != 2 {
|
||||
t.Fatalf("Invalid nonce, want 2, got %d", nonce)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInvalidTransactions(t *testing.T) {
|
||||
pool, key := setupTxPool()
|
||||
|
||||
@@ -97,9 +171,10 @@ func TestTransactionQueue(t *testing.T) {
|
||||
from, _ := deriveSender(tx)
|
||||
currentState, _ := pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1000))
|
||||
pool.resetState()
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(currentState)
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected valid txs to be 1 is", len(pool.pending))
|
||||
}
|
||||
@@ -108,7 +183,7 @@ func TestTransactionQueue(t *testing.T) {
|
||||
from, _ = deriveSender(tx)
|
||||
currentState.SetNonce(from, 2)
|
||||
pool.enqueueTx(tx.Hash(), tx)
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(currentState)
|
||||
if _, ok := pool.pending[from].txs.items[tx.Nonce()]; ok {
|
||||
t.Error("expected transaction to be in tx pool")
|
||||
}
|
||||
@@ -124,11 +199,13 @@ func TestTransactionQueue(t *testing.T) {
|
||||
from, _ = deriveSender(tx1)
|
||||
currentState, _ = pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1000))
|
||||
pool.resetState()
|
||||
|
||||
pool.enqueueTx(tx1.Hash(), tx1)
|
||||
pool.enqueueTx(tx2.Hash(), tx2)
|
||||
pool.enqueueTx(tx3.Hash(), tx3)
|
||||
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(currentState)
|
||||
|
||||
if len(pool.pending) != 1 {
|
||||
t.Error("expected tx pool to be 1, got", len(pool.pending))
|
||||
@@ -225,7 +302,8 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
if err := pool.add(tx2); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
state, _ := pool.currentState()
|
||||
pool.promoteExecutables(state)
|
||||
if pool.pending[addr].Len() != 1 {
|
||||
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
|
||||
}
|
||||
@@ -236,7 +314,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
|
||||
if err := pool.add(tx3); err != nil {
|
||||
t.Error("didn't expect error", err)
|
||||
}
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(state)
|
||||
if pool.pending[addr].Len() != 1 {
|
||||
t.Error("expected 1 pending transactions, got", pool.pending[addr].Len())
|
||||
}
|
||||
@@ -295,6 +373,7 @@ func TestRemovedTxEvent(t *testing.T) {
|
||||
from, _ := deriveSender(tx)
|
||||
currentState, _ := pool.currentState()
|
||||
currentState.AddBalance(from, big.NewInt(1000000000000))
|
||||
pool.resetState()
|
||||
pool.eventMux.Post(RemovedTransactionEvent{types.Transactions{tx}})
|
||||
pool.eventMux.Post(ChainHeadEvent{nil})
|
||||
if pool.pending[from].Len() != 1 {
|
||||
@@ -452,6 +531,7 @@ func TestTransactionQueueAccountLimiting(t *testing.T) {
|
||||
|
||||
state, _ := pool.currentState()
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
pool.resetState()
|
||||
|
||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||
for i := uint64(1); i <= maxQueuedPerAccount+5; i++ {
|
||||
@@ -564,6 +644,7 @@ func TestTransactionPendingLimiting(t *testing.T) {
|
||||
|
||||
state, _ := pool.currentState()
|
||||
state.AddBalance(account, big.NewInt(1000000))
|
||||
pool.resetState()
|
||||
|
||||
// Keep queuing up transactions and make sure all above a limit are dropped
|
||||
for i := uint64(0); i < maxQueuedPerAccount+5; i++ {
|
||||
@@ -733,7 +814,7 @@ func benchmarkPendingDemotion(b *testing.B, size int) {
|
||||
// Benchmark the speed of pool validation
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pool.demoteUnexecutables()
|
||||
pool.demoteUnexecutables(state)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,7 +838,7 @@ func benchmarkFuturePromotion(b *testing.B, size int) {
|
||||
// Benchmark the speed of pool validation
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
pool.promoteExecutables()
|
||||
pool.promoteExecutables(state)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto/sha3"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
@@ -63,20 +64,12 @@ func (n BlockNonce) Uint64() uint64 {
|
||||
|
||||
// MarshalJSON implements json.Marshaler
|
||||
func (n BlockNonce) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, n)), nil
|
||||
return hexutil.Bytes(n[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Unmarshaler
|
||||
func (n *BlockNonce) UnmarshalJSON(input []byte) error {
|
||||
var b hexBytes
|
||||
if err := b.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(b) != 8 {
|
||||
return errBadNonceSize
|
||||
}
|
||||
copy((*n)[:], b)
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("BlockNonce", input, n[:])
|
||||
}
|
||||
|
||||
// Header represents a block header in the Ethereum blockchain.
|
||||
@@ -106,12 +99,12 @@ type jsonHeader struct {
|
||||
TxHash *common.Hash `json:"transactionsRoot"`
|
||||
ReceiptHash *common.Hash `json:"receiptsRoot"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Difficulty *hexBig `json:"difficulty"`
|
||||
Number *hexBig `json:"number"`
|
||||
GasLimit *hexBig `json:"gasLimit"`
|
||||
GasUsed *hexBig `json:"gasUsed"`
|
||||
Time *hexBig `json:"timestamp"`
|
||||
Extra *hexBytes `json:"extraData"`
|
||||
Difficulty *hexutil.Big `json:"difficulty"`
|
||||
Number *hexutil.Big `json:"number"`
|
||||
GasLimit *hexutil.Big `json:"gasLimit"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
Time *hexutil.Big `json:"timestamp"`
|
||||
Extra *hexutil.Bytes `json:"extraData"`
|
||||
MixDigest *common.Hash `json:"mixHash"`
|
||||
Nonce *BlockNonce `json:"nonce"`
|
||||
}
|
||||
@@ -151,12 +144,12 @@ func (h *Header) MarshalJSON() ([]byte, error) {
|
||||
TxHash: &h.TxHash,
|
||||
ReceiptHash: &h.ReceiptHash,
|
||||
Bloom: &h.Bloom,
|
||||
Difficulty: (*hexBig)(h.Difficulty),
|
||||
Number: (*hexBig)(h.Number),
|
||||
GasLimit: (*hexBig)(h.GasLimit),
|
||||
GasUsed: (*hexBig)(h.GasUsed),
|
||||
Time: (*hexBig)(h.Time),
|
||||
Extra: (*hexBytes)(&h.Extra),
|
||||
Difficulty: (*hexutil.Big)(h.Difficulty),
|
||||
Number: (*hexutil.Big)(h.Number),
|
||||
GasLimit: (*hexutil.Big)(h.GasLimit),
|
||||
GasUsed: (*hexutil.Big)(h.GasUsed),
|
||||
Time: (*hexutil.Big)(h.Time),
|
||||
Extra: (*hexutil.Bytes)(&h.Extra),
|
||||
MixDigest: &h.MixDigest,
|
||||
Nonce: &h.Nonce,
|
||||
})
|
||||
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
)
|
||||
@@ -77,20 +78,12 @@ func (b Bloom) TestBytes(test []byte) bool {
|
||||
|
||||
// MarshalJSON encodes b as a hex string with 0x prefix.
|
||||
func (b Bloom) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`"%#x"`, b[:])), nil
|
||||
return hexutil.Bytes(b[:]).MarshalJSON()
|
||||
}
|
||||
|
||||
// UnmarshalJSON b as a hex string with 0x prefix.
|
||||
func (b *Bloom) UnmarshalJSON(input []byte) error {
|
||||
var dec hexBytes
|
||||
if err := dec.UnmarshalJSON(input); err != nil {
|
||||
return err
|
||||
}
|
||||
if len(dec) != bloomLength {
|
||||
return fmt.Errorf("invalid bloom size, want %d bytes", bloomLength)
|
||||
}
|
||||
copy((*b)[:], dec)
|
||||
return nil
|
||||
return hexutil.UnmarshalJSON("Bloom", input, b[:])
|
||||
}
|
||||
|
||||
func CreateBloom(receipts Receipts) Bloom {
|
||||
|
||||
@@ -1,108 +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 types
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"math/big"
|
||||
)
|
||||
|
||||
// JSON unmarshaling utilities.
|
||||
|
||||
type hexBytes []byte
|
||||
|
||||
func (b *hexBytes) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, []byte(*b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexBytes) UnmarshalJSON(input []byte) error {
|
||||
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||
return fmt.Errorf("cannot unmarshal non-string into hexBytes")
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||
return fmt.Errorf("missing 0x prefix in hexBytes input %q", input)
|
||||
}
|
||||
dec := make(hexBytes, (len(input)-2)/2)
|
||||
if _, err := hex.Decode(dec, input[2:]); err != nil {
|
||||
return err
|
||||
}
|
||||
*b = dec
|
||||
return nil
|
||||
}
|
||||
|
||||
type hexBig big.Int
|
||||
|
||||
func (b *hexBig) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, (*big.Int)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexBig) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkHexNumber(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
dec, ok := new(big.Int).SetString(string(raw), 16)
|
||||
if !ok {
|
||||
return fmt.Errorf("invalid hex number")
|
||||
}
|
||||
*b = (hexBig)(*dec)
|
||||
return nil
|
||||
}
|
||||
|
||||
type hexUint64 uint64
|
||||
|
||||
func (b *hexUint64) MarshalJSON() ([]byte, error) {
|
||||
if b != nil {
|
||||
return []byte(fmt.Sprintf(`"0x%x"`, *(*uint64)(b))), nil
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (b *hexUint64) UnmarshalJSON(input []byte) error {
|
||||
raw, err := checkHexNumber(input)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = fmt.Sscanf(string(raw), "%x", b)
|
||||
return err
|
||||
}
|
||||
|
||||
func checkHexNumber(input []byte) (raw []byte, err error) {
|
||||
if len(input) < 2 || input[0] != '"' || input[len(input)-1] != '"' {
|
||||
return nil, fmt.Errorf("cannot unmarshal non-string into hex number")
|
||||
}
|
||||
input = input[1 : len(input)-1]
|
||||
if len(input) < 2 || input[0] != '0' || input[1] != 'x' {
|
||||
return nil, fmt.Errorf("missing 0x prefix in hex number input %q", input)
|
||||
}
|
||||
if len(input) == 2 {
|
||||
return nil, fmt.Errorf("empty hex number")
|
||||
}
|
||||
raw = input[2:]
|
||||
if len(raw)%2 != 0 {
|
||||
raw = append([]byte{'0'}, raw...)
|
||||
}
|
||||
return raw, nil
|
||||
}
|
||||
@@ -1,232 +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 types
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
var unmarshalHeaderTests = map[string]struct {
|
||||
input string
|
||||
wantHash common.Hash
|
||||
wantError error
|
||||
}{
|
||||
"block 0x1e2200": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantHash: common.HexToHash("0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48"),
|
||||
},
|
||||
"bad nonce": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c7958","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errBadNonceSize,
|
||||
},
|
||||
"missing mixHash": {
|
||||
input: `{"difficulty":"0x311ca98cebfe","extraData":"0x7777772e62772e636f6d","gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errMissingHeaderMixDigest,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"gasLimit":"0x47db3d","gasUsed":"0x43760c","hash":"0x3724bc6b9dcd4a2b3a26e0ed9b821e7380b5b3d7dec7166c7983cead62a37e48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0xbcdfc35b86bedf72f0cda046a3c16829a2ef41d1","mixHash":"0x1ccfddb506dac5afc09b6f92eb09a043ffc8e08f7592250af57b9c64c20f9b25","nonce":"0x670bd98c79585197","number":"0x1e2200","parentHash":"0xd3e13296d064e7344f20c57c57b67a022f6bf7741fa42428c2db77e91abdf1f8","receiptsRoot":"0xeeab1776c1fafbe853a8ee0c1bafe2e775a1b6fdb6ff3e9f9410ddd4514889ff","sha3Uncles":"0x5fbfa4ec8b089678c53b6798cc0d9260ea40a529e06d5300aae35596262e0eb3","size":"0x57f","stateRoot":"0x62ad2007e4a3f31ea98e5d2fd150d894887bafde36eeac7331a60ae12053ec76","timestamp":"0x579b82f2","totalDifficulty":"0x24fe813c101d00f97","transactions":["0xb293408e85735bfc78b35aa89de8b48e49641e3d82e3d52ea2d44ec42a4e88cf","0x124acc383ff2da6faa0357829084dae64945221af6f6f09da1d11688b779f939","0xee090208b6051c442ccdf9ec19f66389e604d342a6d71144c7227ce995bef46f"],"transactionsRoot":"0xce0042dd9af0c1923dd7f58ca6faa156d39d4ef39fdb65c5bcd1d4b4720096db","uncles":["0x6818a31d1f204cf640c952082940b68b8db6d1b39ee71f7efe0e3629ed5d7eb3"]}`,
|
||||
wantError: errMissingHeaderFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalHeader(t *testing.T) {
|
||||
for name, test := range unmarshalHeaderTests {
|
||||
var head *Header
|
||||
err := json.Unmarshal([]byte(test.input), &head)
|
||||
if !checkError(t, name, err, test.wantError) {
|
||||
continue
|
||||
}
|
||||
if head.Hash() != test.wantHash {
|
||||
t.Errorf("test %q: got hash %x, want %x", name, head.Hash(), test.wantHash)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalHeader(t *testing.T) {
|
||||
for name, test := range unmarshalHeaderTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Header
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Header
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled header: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: header mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalTransactionTests = map[string]struct {
|
||||
input string
|
||||
wantHash common.Hash
|
||||
wantFrom common.Address
|
||||
wantError error
|
||||
}{
|
||||
"value transfer": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantHash: common.HexToHash("0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9"),
|
||||
wantFrom: common.HexToAddress("0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689"),
|
||||
},
|
||||
/* TODO skipping this test as this type can not be tested with the current signing approach
|
||||
"bad signature fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x58","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: ErrInvalidSig,
|
||||
},
|
||||
*/
|
||||
"missing signature v": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: errMissingTxSignatureFields,
|
||||
},
|
||||
"missing signature fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","gas":"0x15f90","gasPrice":"0x4a817c800","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00"}`,
|
||||
wantError: errMissingTxSignatureFields,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"blockHash":"0x0188a05dcc825bd1a05dab91bea0c03622542683446e56302eabb46097d4ae11","blockNumber":"0x1e478d","from":"0xf36c3f6c4a2ce8d353fb92d5cd10d19ce69ae689","hash":"0xd91c08f1e27c5ce7e1f57d78d7c56a9ee446be07b9635d84d0475660ea8905e9","input":"0x","nonce":"0x58d","to":"0x88f252f674ac755feff877abf957d4aa05adce86","transactionIndex":"0x1","value":"0x19f0ec3ed71ec00","v":"0x1c","r":"0x53829f206c99b866672f987909d556cd1c2eb60e990a3425f65083977c14187b","s":"0x5cc52383e41c923ec7d63749c1f13a7236b540527ee5b9a78b3fb869a66f60e"}`,
|
||||
wantError: errMissingTxFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalTransaction(t *testing.T) {
|
||||
for name, test := range unmarshalTransactionTests {
|
||||
var tx *Transaction
|
||||
err := json.Unmarshal([]byte(test.input), &tx)
|
||||
if !checkError(t, name, err, test.wantError) {
|
||||
continue
|
||||
}
|
||||
|
||||
if tx.Hash() != test.wantHash {
|
||||
t.Errorf("test %q: got hash %x, want %x", name, tx.Hash(), test.wantHash)
|
||||
continue
|
||||
}
|
||||
from, err := Sender(HomesteadSigner{}, tx)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: From error %v", name, err)
|
||||
}
|
||||
if from != test.wantFrom {
|
||||
t.Errorf("test %q: sender mismatch: got %x, want %x", name, from, test.wantFrom)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalTransaction(t *testing.T) {
|
||||
for name, test := range unmarshalTransactionTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Transaction
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Transaction
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled transaction: %v", name, err)
|
||||
continue
|
||||
}
|
||||
proced.Hash() // hack private fields to pass deep equal
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: transaction mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var unmarshalReceiptTests = map[string]struct {
|
||||
input string
|
||||
wantError error
|
||||
}{
|
||||
"ok": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||
},
|
||||
"missing post state": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","transactionIndex":"0x1"}`,
|
||||
wantError: errMissingReceiptPostState,
|
||||
},
|
||||
"missing fields": {
|
||||
input: `{"blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","blockNumber":"0x1e773b","contractAddress":null,"cumulativeGasUsed":"0x10cea","from":"0xdf21fa922215b1a56f5a6d6294e6e36c85a0acfb","gasUsed":"0xbae2","logs":[{"address":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x000000000000000000000000df21fa922215b1a56f5a6d6294e6e36c85a0acfb","0x00000000000000000000000032be343b94f860124dc4fee278fdcbd38c102d88"],"data":"0x0000000000000000000000000000000000000000000000027cfefc4f3f392700","blockNumber":"0x1e773b","transactionIndex":"0x1","transactionHash":"0x0b4cc7844537023b709953390e3881ec5b233703a8e8824dc03e13729a1bd95a","blockHash":"0xad20a0f78d19d7857067a9c06e6411efeab7673e183e4a545f53b724bb7fabf0","logIndex":"0x0"}],"logsBloom":"0x00000000000000020000000000020000000000000000000000000000000000000000000000000000000000000000000000040000000000000100000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000010000000000000000000000000000000000000000000000010000000000000000000001000000000000000000000000000000000000000000000000000000000000000000000002002000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000","root":"0x6e8a06b2dac39ac5c9d4db5fb2a2a94ef7a6e5ec1c554079112112caf162998a","to":"0xbb9bc244d798123fde783fcc1c72d3bb8c189413"}`,
|
||||
wantError: errMissingReceiptFields,
|
||||
},
|
||||
}
|
||||
|
||||
func TestUnmarshalReceipt(t *testing.T) {
|
||||
for name, test := range unmarshalReceiptTests {
|
||||
var r *Receipt
|
||||
err := json.Unmarshal([]byte(test.input), &r)
|
||||
checkError(t, name, err, test.wantError)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshalReceipt(t *testing.T) {
|
||||
for name, test := range unmarshalReceiptTests {
|
||||
if test.wantError != nil {
|
||||
continue
|
||||
}
|
||||
var original *Receipt
|
||||
json.Unmarshal([]byte(test.input), &original)
|
||||
|
||||
blob, err := json.Marshal(original)
|
||||
if err != nil {
|
||||
t.Errorf("test %q: failed to marshal receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
var proced *Receipt
|
||||
if err := json.Unmarshal(blob, &proced); err != nil {
|
||||
t.Errorf("Test %q: failed to unmarshal marhsalled receipt: %v", name, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(original, proced) {
|
||||
t.Errorf("test %q: receipt mismatch: have %+v, want %+v", name, proced, original)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkError(t *testing.T, testname string, got, want error) bool {
|
||||
if got == nil {
|
||||
if want != nil {
|
||||
t.Errorf("test %q: got no error, want %q", testname, want)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
if want == nil {
|
||||
t.Errorf("test %q: unexpected error %q", testname, got)
|
||||
} else if got.Error() != want.Error() {
|
||||
t.Errorf("test %q: got error %q, want %q", testname, got, want)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
@@ -49,12 +50,12 @@ type Receipt struct {
|
||||
|
||||
type jsonReceipt struct {
|
||||
PostState *common.Hash `json:"root"`
|
||||
CumulativeGasUsed *hexBig `json:"cumulativeGasUsed"`
|
||||
CumulativeGasUsed *hexutil.Big `json:"cumulativeGasUsed"`
|
||||
Bloom *Bloom `json:"logsBloom"`
|
||||
Logs *vm.Logs `json:"logs"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
ContractAddress *common.Address `json:"contractAddress"`
|
||||
GasUsed *hexBig `json:"gasUsed"`
|
||||
GasUsed *hexutil.Big `json:"gasUsed"`
|
||||
}
|
||||
|
||||
// NewReceipt creates a barebone transaction receipt, copying the init fields.
|
||||
@@ -90,12 +91,12 @@ func (r *Receipt) MarshalJSON() ([]byte, error) {
|
||||
|
||||
return json.Marshal(&jsonReceipt{
|
||||
PostState: &root,
|
||||
CumulativeGasUsed: (*hexBig)(r.CumulativeGasUsed),
|
||||
CumulativeGasUsed: (*hexutil.Big)(r.CumulativeGasUsed),
|
||||
Bloom: &r.Bloom,
|
||||
Logs: &r.Logs,
|
||||
TxHash: &r.TxHash,
|
||||
ContractAddress: &r.ContractAddress,
|
||||
GasUsed: (*hexBig)(r.GasUsed),
|
||||
GasUsed: (*hexutil.Big)(r.GasUsed),
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -27,6 +27,7 @@ import (
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -69,15 +70,15 @@ type txdata struct {
|
||||
|
||||
type jsonTransaction struct {
|
||||
Hash *common.Hash `json:"hash"`
|
||||
AccountNonce *hexUint64 `json:"nonce"`
|
||||
Price *hexBig `json:"gasPrice"`
|
||||
GasLimit *hexBig `json:"gas"`
|
||||
AccountNonce *hexutil.Uint64 `json:"nonce"`
|
||||
Price *hexutil.Big `json:"gasPrice"`
|
||||
GasLimit *hexutil.Big `json:"gas"`
|
||||
Recipient *common.Address `json:"to"`
|
||||
Amount *hexBig `json:"value"`
|
||||
Payload *hexBytes `json:"input"`
|
||||
V *hexBig `json:"v"`
|
||||
R *hexBig `json:"r"`
|
||||
S *hexBig `json:"s"`
|
||||
Amount *hexutil.Big `json:"value"`
|
||||
Payload *hexutil.Bytes `json:"input"`
|
||||
V *hexutil.Big `json:"v"`
|
||||
R *hexutil.Big `json:"r"`
|
||||
S *hexutil.Big `json:"s"`
|
||||
}
|
||||
|
||||
func NewTransaction(nonce uint64, to common.Address, amount, gasLimit, gasPrice *big.Int, data []byte) *Transaction {
|
||||
@@ -170,15 +171,15 @@ func (tx *Transaction) MarshalJSON() ([]byte, error) {
|
||||
|
||||
return json.Marshal(&jsonTransaction{
|
||||
Hash: &hash,
|
||||
AccountNonce: (*hexUint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexBig)(tx.data.Price),
|
||||
GasLimit: (*hexBig)(tx.data.GasLimit),
|
||||
AccountNonce: (*hexutil.Uint64)(&tx.data.AccountNonce),
|
||||
Price: (*hexutil.Big)(tx.data.Price),
|
||||
GasLimit: (*hexutil.Big)(tx.data.GasLimit),
|
||||
Recipient: tx.data.Recipient,
|
||||
Amount: (*hexBig)(tx.data.Amount),
|
||||
Payload: (*hexBytes)(&tx.data.Payload),
|
||||
V: (*hexBig)(tx.data.V),
|
||||
R: (*hexBig)(tx.data.R),
|
||||
S: (*hexBig)(tx.data.S),
|
||||
Amount: (*hexutil.Big)(tx.data.Amount),
|
||||
Payload: (*hexutil.Bytes)(&tx.data.Payload),
|
||||
V: (*hexutil.Big)(tx.data.V),
|
||||
R: (*hexutil.Big)(tx.data.R),
|
||||
S: (*hexutil.Big)(tx.data.S),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -233,6 +234,8 @@ func (tx *Transaction) Value() *big.Int { return new(big.Int).Set(tx.data.Amo
|
||||
func (tx *Transaction) Nonce() uint64 { return tx.data.AccountNonce }
|
||||
func (tx *Transaction) CheckNonce() bool { return true }
|
||||
|
||||
// To returns the recipient address of the transaction.
|
||||
// It returns nil if the transaction is a contract creation.
|
||||
func (tx *Transaction) To() *common.Address {
|
||||
if tx.data.Recipient == nil {
|
||||
return nil
|
||||
|
||||
@@ -24,7 +24,7 @@ import (
|
||||
|
||||
// ContractRef is a reference to the contract's backing object
|
||||
type ContractRef interface {
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
ReturnGas(*big.Int)
|
||||
Address() common.Address
|
||||
Value() *big.Int
|
||||
SetCode(common.Hash, []byte)
|
||||
@@ -48,7 +48,7 @@ type Contract struct {
|
||||
CodeAddr *common.Address
|
||||
Input []byte
|
||||
|
||||
value, Gas, UsedGas, Price *big.Int
|
||||
value, Gas, UsedGas *big.Int
|
||||
|
||||
Args []byte
|
||||
|
||||
@@ -56,7 +56,7 @@ type Contract struct {
|
||||
}
|
||||
|
||||
// NewContract returns a new contract environment for the execution of EVM.
|
||||
func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.Int) *Contract {
|
||||
func NewContract(caller ContractRef, object ContractRef, value, gas *big.Int) *Contract {
|
||||
c := &Contract{CallerAddress: caller.Address(), caller: caller, self: object, Args: nil}
|
||||
|
||||
if parent, ok := caller.(*Contract); ok {
|
||||
@@ -70,9 +70,6 @@ func NewContract(caller ContractRef, object ContractRef, value, gas, price *big.
|
||||
// This pointer will be off the state transition
|
||||
c.Gas = gas //new(big.Int).Set(gas)
|
||||
c.value = new(big.Int).Set(value)
|
||||
// In most cases price and value are pointers to transaction objects
|
||||
// and we don't want the transaction's values to change.
|
||||
c.Price = new(big.Int).Set(price)
|
||||
c.UsedGas = new(big.Int)
|
||||
|
||||
return c
|
||||
@@ -114,7 +111,7 @@ func (c *Contract) Caller() common.Address {
|
||||
// caller.
|
||||
func (c *Contract) Finalise() {
|
||||
// Return the remaining gas to the caller
|
||||
c.caller.ReturnGas(c.Gas, c.Price)
|
||||
c.caller.ReturnGas(c.Gas)
|
||||
}
|
||||
|
||||
// UseGas attempts the use gas and subtracts it and returns true on success
|
||||
@@ -127,7 +124,7 @@ func (c *Contract) UseGas(gas *big.Int) (ok bool) {
|
||||
}
|
||||
|
||||
// ReturnGas adds the given gas back to itself.
|
||||
func (c *Contract) ReturnGas(gas, price *big.Int) {
|
||||
func (c *Contract) ReturnGas(gas *big.Int) {
|
||||
// Return the gas to the context
|
||||
c.Gas.Add(c.Gas, gas)
|
||||
c.UsedGas.Sub(c.UsedGas, gas)
|
||||
|
||||
@@ -17,110 +17,299 @@
|
||||
package vm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
// Environment is an EVM requirement and helper which allows access to outside
|
||||
// information such as states.
|
||||
type Environment interface {
|
||||
// The current ruleset
|
||||
ChainConfig() *params.ChainConfig
|
||||
// The state database
|
||||
Db() Database
|
||||
// Creates a restorable snapshot
|
||||
SnapshotDatabase() int
|
||||
// Set database to previous snapshot
|
||||
RevertToSnapshot(int)
|
||||
// Address of the original invoker (first occurrence of the VM invoker)
|
||||
Origin() common.Address
|
||||
// The block number this VM is invoked on
|
||||
BlockNumber() *big.Int
|
||||
// The n'th hash ago from this block number
|
||||
GetHash(uint64) common.Hash
|
||||
// The handler's address
|
||||
Coinbase() common.Address
|
||||
// The current time (block time)
|
||||
Time() *big.Int
|
||||
// Difficulty set on the current block
|
||||
Difficulty() *big.Int
|
||||
// The gas limit of the block
|
||||
GasLimit() *big.Int
|
||||
// Determines whether it's possible to transact
|
||||
CanTransfer(from common.Address, balance *big.Int) bool
|
||||
// Transfers amount from one account to the other
|
||||
Transfer(from, to Account, amount *big.Int)
|
||||
// Adds a LOG to the state
|
||||
AddLog(*Log)
|
||||
// Type of the VM
|
||||
Vm() Vm
|
||||
// Get the curret calling depth
|
||||
Depth() int
|
||||
// Set the current calling depth
|
||||
SetDepth(i int)
|
||||
// Call another contract
|
||||
Call(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
|
||||
// Take another's contract code and execute within our own context
|
||||
CallCode(me ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error)
|
||||
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||
DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error)
|
||||
// Create a new contract
|
||||
Create(me ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error)
|
||||
type (
|
||||
CanTransferFunc func(StateDB, common.Address, *big.Int) bool
|
||||
TransferFunc func(StateDB, common.Address, common.Address, *big.Int)
|
||||
// GetHashFunc returns the nth block hash in the blockchain
|
||||
// and is used by the BLOCKHASH EVM op code.
|
||||
GetHashFunc func(uint64) common.Hash
|
||||
)
|
||||
|
||||
// Context provides the EVM with auxilary information. Once provided it shouldn't be modified.
|
||||
type Context struct {
|
||||
// CanTransfer returns whether the account contains
|
||||
// sufficient ether to transfer the value
|
||||
CanTransfer CanTransferFunc
|
||||
// Transfer transfers ether from one account to the other
|
||||
Transfer TransferFunc
|
||||
// GetHash returns the hash corresponding to n
|
||||
GetHash GetHashFunc
|
||||
|
||||
// Message information
|
||||
Origin common.Address // Provides information for ORIGIN
|
||||
GasPrice *big.Int // Provides information for GASPRICE
|
||||
|
||||
// Block information
|
||||
Coinbase common.Address // Provides information for COINBASE
|
||||
GasLimit *big.Int // Provides information for GASLIMIT
|
||||
BlockNumber *big.Int // Provides information for NUMBER
|
||||
Time *big.Int // Provides information for TIME
|
||||
Difficulty *big.Int // Provides information for DIFFICULTY
|
||||
}
|
||||
|
||||
// Vm is the basic interface for an implementation of the EVM.
|
||||
type Vm interface {
|
||||
// Run should execute the given contract with the input given in in
|
||||
// and return the contract execution return bytes or an error if it
|
||||
// failed.
|
||||
Run(c *Contract, in []byte) ([]byte, error)
|
||||
// Environment provides information about external sources for the EVM
|
||||
//
|
||||
// The Environment should never be reused and is not thread safe.
|
||||
type Environment struct {
|
||||
// Context provides auxiliary blockchain related information
|
||||
Context
|
||||
// StateDB gives access to the underlying state
|
||||
StateDB StateDB
|
||||
// Depth is the current call stack
|
||||
Depth int
|
||||
|
||||
// evm is the ethereum virtual machine
|
||||
evm Vm
|
||||
// chainConfig contains information about the current chain
|
||||
chainConfig *params.ChainConfig
|
||||
vmConfig Config
|
||||
}
|
||||
|
||||
// Database is a EVM database for full state querying.
|
||||
type Database interface {
|
||||
GetAccount(common.Address) Account
|
||||
CreateAccount(common.Address) Account
|
||||
|
||||
AddBalance(common.Address, *big.Int)
|
||||
GetBalance(common.Address) *big.Int
|
||||
|
||||
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)
|
||||
|
||||
AddRefund(*big.Int)
|
||||
GetRefund() *big.Int
|
||||
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
Suicide(common.Address) bool
|
||||
HasSuicided(common.Address) bool
|
||||
|
||||
// Exist reports whether the given account exists in state.
|
||||
// Notably this should also return true for suicided accounts.
|
||||
Exist(common.Address) bool
|
||||
// Empty returns whether the given account is empty. Empty
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
// NewEnvironment retutrns a new EVM environment.
|
||||
func NewEnvironment(context Context, statedb StateDB, chainConfig *params.ChainConfig, vmConfig Config) *Environment {
|
||||
env := &Environment{
|
||||
Context: context,
|
||||
StateDB: statedb,
|
||||
vmConfig: vmConfig,
|
||||
chainConfig: chainConfig,
|
||||
}
|
||||
env.evm = New(env, vmConfig)
|
||||
return env
|
||||
}
|
||||
|
||||
// Account represents a contract or basic ethereum account.
|
||||
type Account interface {
|
||||
SubBalance(amount *big.Int)
|
||||
AddBalance(amount *big.Int)
|
||||
SetBalance(*big.Int)
|
||||
SetNonce(uint64)
|
||||
Balance() *big.Int
|
||||
Address() common.Address
|
||||
ReturnGas(*big.Int, *big.Int)
|
||||
SetCode(common.Hash, []byte)
|
||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||
Value() *big.Int
|
||||
// Call executes the contract associated with the addr with the given input as paramaters. It also handles any
|
||||
// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
|
||||
// case of an execution error or failed value transfer.
|
||||
func (env *Environment) Call(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, DepthError
|
||||
}
|
||||
if !env.Context.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
var (
|
||||
to Account
|
||||
snapshotPreTransfer = env.StateDB.Snapshot()
|
||||
)
|
||||
if !env.StateDB.Exist(addr) {
|
||||
if Precompiled[addr.Str()] == nil && env.ChainConfig().IsEIP158(env.BlockNumber) && value.BitLen() == 0 {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
to = env.StateDB.CreateAccount(addr)
|
||||
} else {
|
||||
to = env.StateDB.GetAccount(addr)
|
||||
}
|
||||
env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
// 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 {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// CallCode executes the contract associated with the addr with the given input as paramaters. It also handles any
|
||||
// necessary value transfer required and takes the necessary steps to create accounts and reverses the state in
|
||||
// case of an execution error or failed value transfer.
|
||||
//
|
||||
// CallCode differs from Call in the sense that it executes the given address' code with the caller as context.
|
||||
func (env *Environment) CallCode(caller ContractRef, addr common.Address, input []byte, gas, value *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, DepthError
|
||||
}
|
||||
if !env.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, fmt.Errorf("insufficient funds to transfer value. Req %v, has %v", value, env.StateDB.GetBalance(caller.Address()))
|
||||
}
|
||||
|
||||
var (
|
||||
snapshotPreTransfer = env.StateDB.Snapshot()
|
||||
to = env.StateDB.GetAccount(caller.Address())
|
||||
)
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// DelegateCall executes the contract associated with the addr with the given input as paramaters.
|
||||
// It reverses the state in case of an execution error.
|
||||
//
|
||||
// DelegateCall differs from CallCode in the sense that it executes the given address' code with the caller as context
|
||||
// and the caller is set to the caller of the caller.
|
||||
func (env *Environment) DelegateCall(caller ContractRef, addr common.Address, input []byte, gas *big.Int) ([]byte, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
return nil, DepthError
|
||||
}
|
||||
|
||||
var (
|
||||
snapshot = env.StateDB.Snapshot()
|
||||
to = env.StateDB.GetAccount(caller.Address())
|
||||
)
|
||||
|
||||
// Iinitialise a new contract and make initialise the delegate values
|
||||
contract := NewContract(caller, to, caller.Value(), gas).AsDelegate()
|
||||
contract.SetCallCode(&addr, env.StateDB.GetCodeHash(addr), env.StateDB.GetCode(addr))
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, input)
|
||||
if err != nil {
|
||||
contract.UseGas(contract.Gas)
|
||||
|
||||
env.StateDB.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
return ret, err
|
||||
}
|
||||
|
||||
// Create creates a new contract using code as deployment code.
|
||||
func (env *Environment) Create(caller ContractRef, code []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
if env.vmConfig.NoRecursion && env.Depth > 0 {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, nil
|
||||
}
|
||||
|
||||
// Depth check execution. Fail if we're trying to execute above the
|
||||
// limit.
|
||||
if env.Depth > int(params.CallCreateDepth.Int64()) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, DepthError
|
||||
}
|
||||
if !env.CanTransfer(env.StateDB, caller.Address(), value) {
|
||||
caller.ReturnGas(gas)
|
||||
|
||||
return nil, common.Address{}, ErrInsufficientBalance
|
||||
}
|
||||
|
||||
// Create a new account on the state
|
||||
nonce := env.StateDB.GetNonce(caller.Address())
|
||||
env.StateDB.SetNonce(caller.Address(), nonce+1)
|
||||
|
||||
snapshotPreTransfer := env.StateDB.Snapshot()
|
||||
var (
|
||||
addr = crypto.CreateAddress(caller.Address(), nonce)
|
||||
to = env.StateDB.CreateAccount(addr)
|
||||
)
|
||||
if env.ChainConfig().IsEIP158(env.BlockNumber) {
|
||||
env.StateDB.SetNonce(addr, 1)
|
||||
}
|
||||
env.Transfer(env.StateDB, caller.Address(), to.Address(), value)
|
||||
|
||||
// initialise a new contract and set the code that is to be used by the
|
||||
// E The contract is a scoped environment for this execution context
|
||||
// only.
|
||||
contract := NewContract(caller, to, value, gas)
|
||||
contract.SetCallCode(&addr, crypto.Keccak256Hash(code), code)
|
||||
defer contract.Finalise()
|
||||
|
||||
ret, err := env.EVM().Run(contract, nil)
|
||||
// check whether the max code size has been exceeded
|
||||
maxCodeSizeExceeded := len(ret) > params.MaxCodeSize
|
||||
// if the contract creation ran successfully and no errors were returned
|
||||
// calculate the gas required to store the code. If the code could not
|
||||
// be stored due to not enough gas set an error and let it be handled
|
||||
// by the error checking condition below.
|
||||
if err == nil && !maxCodeSizeExceeded {
|
||||
dataGas := big.NewInt(int64(len(ret)))
|
||||
dataGas.Mul(dataGas, params.CreateDataGas)
|
||||
if contract.UseGas(dataGas) {
|
||||
env.StateDB.SetCode(addr, ret)
|
||||
} else {
|
||||
err = CodeStoreOutOfGasError
|
||||
}
|
||||
}
|
||||
|
||||
// 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 maxCodeSizeExceeded ||
|
||||
(err != nil && (env.ChainConfig().IsHomestead(env.BlockNumber) || err != CodeStoreOutOfGasError)) {
|
||||
contract.UseGas(contract.Gas)
|
||||
env.StateDB.RevertToSnapshot(snapshotPreTransfer)
|
||||
|
||||
// Nothing should be returned when an error is thrown.
|
||||
return nil, addr, err
|
||||
}
|
||||
// If the vm returned with an error the return value should be set to nil.
|
||||
// This isn't consensus critical but merely to for behaviour reasons such as
|
||||
// tests, RPC calls, etc.
|
||||
if err != nil {
|
||||
ret = nil
|
||||
}
|
||||
|
||||
return ret, addr, err
|
||||
}
|
||||
|
||||
// ChainConfig returns the environment's chain configuration
|
||||
func (env *Environment) ChainConfig() *params.ChainConfig { return env.chainConfig }
|
||||
|
||||
// EVM returns the environments EVM
|
||||
func (env *Environment) EVM() Vm { return env.evm }
|
||||
|
||||
@@ -23,7 +23,10 @@ import (
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
var OutOfGasError = errors.New("Out of gas")
|
||||
var CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
|
||||
var DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
|
||||
var TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
|
||||
var (
|
||||
OutOfGasError = errors.New("Out of gas")
|
||||
CodeStoreOutOfGasError = errors.New("Contract creation code storage out of gas")
|
||||
DepthError = fmt.Errorf("Max call depth exceeded (%d)", params.CallCreateDepth)
|
||||
TraceLimitReachedError = errors.New("The number of logs reached the specified limit")
|
||||
ErrInsufficientBalance = errors.New("insufficient balance for transfer")
|
||||
)
|
||||
|
||||
@@ -28,14 +28,14 @@ import (
|
||||
|
||||
type programInstruction interface {
|
||||
// executes the program instruction and allows the instruction to modify the state of the program
|
||||
do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||
do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error)
|
||||
// returns whether the program instruction halts the execution of the JIT
|
||||
halts() bool
|
||||
// Returns the current op code (debugging purposes)
|
||||
Op() OpCode
|
||||
}
|
||||
|
||||
type instrFn func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack)
|
||||
type instrFn func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack)
|
||||
|
||||
type instruction struct {
|
||||
op OpCode
|
||||
@@ -59,9 +59,9 @@ func jump(mapping map[uint64]uint64, destinations map[uint64]struct{}, contract
|
||||
return mapping[to.Uint64()], nil
|
||||
}
|
||||
|
||||
func (instr instruction) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func (instr instruction) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// calculate the new memory size and gas price for the current executing opcode
|
||||
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, env.Db(), memory, stack)
|
||||
newMemSize, cost, err := jitCalculateGasAndSize(env, contract, instr, memory, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -115,26 +115,26 @@ func (instr instruction) Op() OpCode {
|
||||
return instr.op
|
||||
}
|
||||
|
||||
func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opStaticJump(instr instruction, pc *uint64, ret *big.Int, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
ret.Set(instr.data)
|
||||
}
|
||||
|
||||
func opAdd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAdd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Add(x, y)))
|
||||
}
|
||||
|
||||
func opSub(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSub(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Sub(x, y)))
|
||||
}
|
||||
|
||||
func opMul(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMul(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(U256(x.Mul(x, y)))
|
||||
}
|
||||
|
||||
func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) != 0 {
|
||||
stack.push(U256(x.Div(x, y)))
|
||||
@@ -143,7 +143,7 @@ func opDiv(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||
}
|
||||
}
|
||||
|
||||
func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSdiv(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
@@ -163,7 +163,7 @@ func opSdiv(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||
}
|
||||
}
|
||||
|
||||
func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
stack.push(new(big.Int))
|
||||
@@ -172,7 +172,7 @@ func opMod(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||
}
|
||||
}
|
||||
|
||||
func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
|
||||
if y.Cmp(common.Big0) == 0 {
|
||||
@@ -192,12 +192,12 @@ func opSmod(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||
}
|
||||
}
|
||||
|
||||
func opExp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
base, exponent := stack.pop(), stack.pop()
|
||||
stack.push(math.Exp(base, exponent))
|
||||
}
|
||||
|
||||
func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSignExtend(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
back := stack.pop()
|
||||
if back.Cmp(big.NewInt(31)) < 0 {
|
||||
bit := uint(back.Uint64()*8 + 7)
|
||||
@@ -214,12 +214,12 @@ func opSignExtend(instr instruction, pc *uint64, env Environment, contract *Cont
|
||||
}
|
||||
}
|
||||
|
||||
func opNot(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opNot(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x := stack.pop()
|
||||
stack.push(U256(x.Not(x)))
|
||||
}
|
||||
|
||||
func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opLt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
@@ -228,7 +228,7 @@ func opLt(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||
}
|
||||
}
|
||||
|
||||
func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
@@ -237,7 +237,7 @@ func opGt(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||
}
|
||||
}
|
||||
|
||||
func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSlt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(S256(y)) < 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
@@ -246,7 +246,7 @@ func opSlt(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||
}
|
||||
}
|
||||
|
||||
func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSgt(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := S256(stack.pop()), S256(stack.pop())
|
||||
if x.Cmp(y) > 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
@@ -255,7 +255,7 @@ func opSgt(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||
}
|
||||
}
|
||||
|
||||
func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opEq(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
if x.Cmp(y) == 0 {
|
||||
stack.push(big.NewInt(1))
|
||||
@@ -264,7 +264,7 @@ func opEq(instr instruction, pc *uint64, env Environment, contract *Contract, me
|
||||
}
|
||||
}
|
||||
|
||||
func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opIszero(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x := stack.pop()
|
||||
if x.Cmp(common.Big0) > 0 {
|
||||
stack.push(new(big.Int))
|
||||
@@ -273,19 +273,19 @@ func opIszero(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
}
|
||||
}
|
||||
|
||||
func opAnd(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAnd(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.And(x, y))
|
||||
}
|
||||
func opOr(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opOr(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Or(x, y))
|
||||
}
|
||||
func opXor(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opXor(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y := stack.pop(), stack.pop()
|
||||
stack.push(x.Xor(x, y))
|
||||
}
|
||||
func opByte(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opByte(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
th, val := stack.pop(), stack.pop()
|
||||
if th.Cmp(big.NewInt(32)) < 0 {
|
||||
byte := big.NewInt(int64(common.LeftPadBytes(val.Bytes(), 32)[th.Int64()]))
|
||||
@@ -294,7 +294,7 @@ func opByte(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAddmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
add := x.Add(x, y)
|
||||
@@ -304,7 +304,7 @@ func opAddmod(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMulmod(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
x, y, z := stack.pop(), stack.pop(), stack.pop()
|
||||
if z.Cmp(Zero) > 0 {
|
||||
mul := x.Mul(x, y)
|
||||
@@ -315,45 +315,45 @@ func opMulmod(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
}
|
||||
}
|
||||
|
||||
func opSha3(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSha3(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
offset, size := stack.pop(), stack.pop()
|
||||
hash := crypto.Keccak256(memory.Get(offset.Int64(), size.Int64()))
|
||||
|
||||
stack.push(common.BytesToBig(hash))
|
||||
}
|
||||
|
||||
func opAddress(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opAddress(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(common.Bytes2Big(contract.Address().Bytes()))
|
||||
}
|
||||
|
||||
func opBalance(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opBalance(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
balance := env.Db().GetBalance(addr)
|
||||
balance := env.StateDB.GetBalance(addr)
|
||||
|
||||
stack.push(new(big.Int).Set(balance))
|
||||
}
|
||||
|
||||
func opOrigin(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(env.Origin().Big())
|
||||
func opOrigin(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(env.Origin.Big())
|
||||
}
|
||||
|
||||
func opCaller(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCaller(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(contract.Caller().Big())
|
||||
}
|
||||
|
||||
func opCallValue(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCallValue(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(contract.value))
|
||||
}
|
||||
|
||||
func opCalldataLoad(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataLoad(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(common.Bytes2Big(getData(contract.Input, stack.pop(), common.Big32)))
|
||||
}
|
||||
|
||||
func opCalldataSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(big.NewInt(int64(len(contract.Input))))
|
||||
}
|
||||
|
||||
func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCalldataCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
@@ -362,18 +362,18 @@ func opCalldataCopy(instr instruction, pc *uint64, env Environment, contract *Co
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), getData(contract.Input, cOff, l))
|
||||
}
|
||||
|
||||
func opExtCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExtCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
addr := common.BigToAddress(stack.pop())
|
||||
l := big.NewInt(int64(env.Db().GetCodeSize(addr)))
|
||||
l := big.NewInt(int64(env.StateDB.GetCodeSize(addr)))
|
||||
stack.push(l)
|
||||
}
|
||||
|
||||
func opCodeSize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCodeSize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
l := big.NewInt(int64(len(contract.Code)))
|
||||
stack.push(l)
|
||||
}
|
||||
|
||||
func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
var (
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
@@ -384,70 +384,70 @@ func opCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
}
|
||||
|
||||
func opExtCodeCopy(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opExtCodeCopy(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
var (
|
||||
addr = common.BigToAddress(stack.pop())
|
||||
mOff = stack.pop()
|
||||
cOff = stack.pop()
|
||||
l = stack.pop()
|
||||
)
|
||||
codeCopy := getData(env.Db().GetCode(addr), cOff, l)
|
||||
codeCopy := getData(env.StateDB.GetCode(addr), cOff, l)
|
||||
|
||||
memory.Set(mOff.Uint64(), l.Uint64(), codeCopy)
|
||||
}
|
||||
|
||||
func opGasprice(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(contract.Price))
|
||||
func opGasprice(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(env.GasPrice))
|
||||
}
|
||||
|
||||
func opBlockhash(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opBlockhash(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
num := stack.pop()
|
||||
|
||||
n := new(big.Int).Sub(env.BlockNumber(), common.Big257)
|
||||
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber()) < 0 {
|
||||
n := new(big.Int).Sub(env.BlockNumber, common.Big257)
|
||||
if num.Cmp(n) > 0 && num.Cmp(env.BlockNumber) < 0 {
|
||||
stack.push(env.GetHash(num.Uint64()).Big())
|
||||
} else {
|
||||
stack.push(new(big.Int))
|
||||
}
|
||||
}
|
||||
|
||||
func opCoinbase(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(env.Coinbase().Big())
|
||||
func opCoinbase(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(env.Coinbase.Big())
|
||||
}
|
||||
|
||||
func opTimestamp(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Time())))
|
||||
func opTimestamp(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Time)))
|
||||
}
|
||||
|
||||
func opNumber(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.BlockNumber())))
|
||||
func opNumber(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.BlockNumber)))
|
||||
}
|
||||
|
||||
func opDifficulty(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Difficulty())))
|
||||
func opDifficulty(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.Difficulty)))
|
||||
}
|
||||
|
||||
func opGasLimit(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.GasLimit())))
|
||||
func opGasLimit(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(U256(new(big.Int).Set(env.GasLimit)))
|
||||
}
|
||||
|
||||
func opPop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opPop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.pop()
|
||||
}
|
||||
|
||||
func opPush(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opPush(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
}
|
||||
|
||||
func opDup(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDup(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.dup(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opSwap(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSwap(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.swap(int(instr.data.Int64()))
|
||||
}
|
||||
|
||||
func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opLog(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
n := int(instr.data.Int64())
|
||||
topics := make([]common.Hash, n)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
@@ -456,77 +456,77 @@ func opLog(instr instruction, pc *uint64, env Environment, contract *Contract, m
|
||||
}
|
||||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
|
||||
env.AddLog(log)
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||
env.StateDB.AddLog(log)
|
||||
}
|
||||
|
||||
func opMload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
offset := stack.pop()
|
||||
val := common.BigD(memory.Get(offset.Int64(), 32))
|
||||
stack.push(val)
|
||||
}
|
||||
|
||||
func opMstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
// pop value of the stack
|
||||
mStart, val := stack.pop(), stack.pop()
|
||||
memory.Set(mStart.Uint64(), 32, common.BigToBytes(val, 256))
|
||||
}
|
||||
|
||||
func opMstore8(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMstore8(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
off, val := stack.pop().Int64(), stack.pop().Int64()
|
||||
memory.store[off] = byte(val & 0xff)
|
||||
}
|
||||
|
||||
func opSload(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSload(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := env.Db().GetState(contract.Address(), loc).Big()
|
||||
val := env.StateDB.GetState(contract.Address(), loc).Big()
|
||||
stack.push(val)
|
||||
}
|
||||
|
||||
func opSstore(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opSstore(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
loc := common.BigToHash(stack.pop())
|
||||
val := stack.pop()
|
||||
env.Db().SetState(contract.Address(), loc, common.BigToHash(val))
|
||||
env.StateDB.SetState(contract.Address(), loc, common.BigToHash(val))
|
||||
}
|
||||
|
||||
func opJump(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJump(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
func opJumpi(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJumpi(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
func opJumpdest(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opJumpdest(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
|
||||
func opPc(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opPc(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(instr.data))
|
||||
}
|
||||
|
||||
func opMsize(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opMsize(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(big.NewInt(int64(memory.Len())))
|
||||
}
|
||||
|
||||
func opGas(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opGas(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.push(new(big.Int).Set(contract.Gas))
|
||||
}
|
||||
|
||||
func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCreate(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
var (
|
||||
value = stack.pop()
|
||||
offset, size = stack.pop(), stack.pop()
|
||||
input = memory.Get(offset.Int64(), size.Int64())
|
||||
gas = new(big.Int).Set(contract.Gas)
|
||||
)
|
||||
if env.ChainConfig().IsEIP150(env.BlockNumber()) {
|
||||
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)
|
||||
_, addr, suberr := env.Create(contract, input, gas, 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.ChainConfig().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))
|
||||
@@ -535,7 +535,7 @@ func opCreate(instr instruction, pc *uint64, env Environment, contract *Contract
|
||||
}
|
||||
}
|
||||
|
||||
func opCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
@@ -554,7 +554,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.Call(contract, address, args, gas, contract.Price, value)
|
||||
ret, err := env.Call(contract, address, args, gas, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
@@ -566,7 +566,7 @@ func opCall(instr instruction, pc *uint64, env Environment, contract *Contract,
|
||||
}
|
||||
}
|
||||
|
||||
func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opCallCode(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
gas := stack.pop()
|
||||
// pop gas and value of the stack.
|
||||
addr, value := stack.pop(), stack.pop()
|
||||
@@ -585,7 +585,7 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||
gas.Add(gas, params.CallStipend)
|
||||
}
|
||||
|
||||
ret, err := env.CallCode(contract, address, args, gas, contract.Price, value)
|
||||
ret, err := env.CallCode(contract, address, args, gas, value)
|
||||
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
@@ -597,12 +597,12 @@ func opCallCode(instr instruction, pc *uint64, env Environment, contract *Contra
|
||||
}
|
||||
}
|
||||
|
||||
func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opDelegateCall(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
gas, to, inOffset, inSize, outOffset, outSize := stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop(), stack.pop()
|
||||
|
||||
toAddr := common.BigToAddress(to)
|
||||
args := memory.Get(inOffset.Int64(), inSize.Int64())
|
||||
ret, err := env.DelegateCall(contract, toAddr, args, gas, contract.Price)
|
||||
ret, err := env.DelegateCall(contract, toAddr, args, gas)
|
||||
if err != nil {
|
||||
stack.push(new(big.Int))
|
||||
} else {
|
||||
@@ -611,23 +611,23 @@ func opDelegateCall(instr instruction, pc *uint64, env Environment, contract *Co
|
||||
}
|
||||
}
|
||||
|
||||
func opReturn(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opReturn(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
func opStop(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
func opStop(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
}
|
||||
|
||||
func opSuicide(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
balance := env.Db().GetBalance(contract.Address())
|
||||
env.Db().AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
func opSuicide(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
balance := env.StateDB.GetBalance(contract.Address())
|
||||
env.StateDB.AddBalance(common.BigToAddress(stack.pop()), balance)
|
||||
|
||||
env.Db().Suicide(contract.Address())
|
||||
env.StateDB.Suicide(contract.Address())
|
||||
}
|
||||
|
||||
// following functions are used by the instruction jump table
|
||||
|
||||
// make log instruction function
|
||||
func makeLog(size int) instrFn {
|
||||
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
topics := make([]common.Hash, size)
|
||||
mStart, mSize := stack.pop(), stack.pop()
|
||||
for i := 0; i < size; i++ {
|
||||
@@ -635,14 +635,14 @@ func makeLog(size int) instrFn {
|
||||
}
|
||||
|
||||
d := memory.Get(mStart.Int64(), mSize.Int64())
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber().Uint64())
|
||||
env.AddLog(log)
|
||||
log := NewLog(contract.Address(), topics, d, env.BlockNumber.Uint64())
|
||||
env.StateDB.AddLog(log)
|
||||
}
|
||||
}
|
||||
|
||||
// make push instruction function
|
||||
func makePush(size uint64, bsize *big.Int) instrFn {
|
||||
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
byts := getData(contract.Code, new(big.Int).SetUint64(*pc+1), bsize)
|
||||
stack.push(common.Bytes2Big(byts))
|
||||
*pc += size
|
||||
@@ -651,7 +651,7 @@ func makePush(size uint64, bsize *big.Int) instrFn {
|
||||
|
||||
// make push instruction function
|
||||
func makeDup(size int64) instrFn {
|
||||
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.dup(int(size))
|
||||
}
|
||||
}
|
||||
@@ -660,7 +660,7 @@ func makeDup(size int64) instrFn {
|
||||
func makeSwap(size int64) instrFn {
|
||||
// switch n + 1 otherwise n would be swapped with n
|
||||
size += 1
|
||||
return func(instr instruction, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
return func(instr instruction, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) {
|
||||
stack.swap(int(size))
|
||||
}
|
||||
}
|
||||
|
||||
97
core/vm/interface.go
Normal file
97
core/vm/interface.go
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2014 The go-ethereum Authors
|
||||
// This file is part of the go-ethereum library.
|
||||
//
|
||||
// The go-ethereum library is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Lesser General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// The go-ethereum library is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Lesser General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Lesser General Public License
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
// Vm is the basic interface for an implementation of the EVM.
|
||||
type Vm interface {
|
||||
// Run should execute the given contract with the input given in in
|
||||
// and return the contract execution return bytes or an error if it
|
||||
// failed.
|
||||
Run(c *Contract, in []byte) ([]byte, error)
|
||||
}
|
||||
|
||||
// StateDB is an EVM database for full state querying.
|
||||
type StateDB interface {
|
||||
GetAccount(common.Address) Account
|
||||
CreateAccount(common.Address) Account
|
||||
|
||||
SubBalance(common.Address, *big.Int)
|
||||
AddBalance(common.Address, *big.Int)
|
||||
GetBalance(common.Address) *big.Int
|
||||
|
||||
GetNonce(common.Address) uint64
|
||||
SetNonce(common.Address, uint64)
|
||||
|
||||
GetCodeHash(common.Address) common.Hash
|
||||
GetCode(common.Address) []byte
|
||||
SetCode(common.Address, []byte)
|
||||
GetCodeSize(common.Address) int
|
||||
|
||||
AddRefund(*big.Int)
|
||||
GetRefund() *big.Int
|
||||
|
||||
GetState(common.Address, common.Hash) common.Hash
|
||||
SetState(common.Address, common.Hash, common.Hash)
|
||||
|
||||
Suicide(common.Address) bool
|
||||
HasSuicided(common.Address) bool
|
||||
|
||||
// Exist reports whether the given account exists in state.
|
||||
// Notably this should also return true for suicided accounts.
|
||||
Exist(common.Address) bool
|
||||
// Empty returns whether the given account is empty. Empty
|
||||
// is defined according to EIP161 (balance = nonce = code = 0).
|
||||
Empty(common.Address) bool
|
||||
|
||||
RevertToSnapshot(int)
|
||||
Snapshot() int
|
||||
|
||||
AddLog(*Log)
|
||||
}
|
||||
|
||||
// Account represents a contract or basic ethereum account.
|
||||
type Account interface {
|
||||
SubBalance(amount *big.Int)
|
||||
AddBalance(amount *big.Int)
|
||||
SetBalance(*big.Int)
|
||||
SetNonce(uint64)
|
||||
Balance() *big.Int
|
||||
Address() common.Address
|
||||
ReturnGas(*big.Int)
|
||||
SetCode(common.Hash, []byte)
|
||||
ForEachStorage(cb func(key, value common.Hash) bool)
|
||||
Value() *big.Int
|
||||
}
|
||||
|
||||
// CallContext provides a basic interface for the EVM calling conventions. The EVM Environment
|
||||
// depends on this context being implemented for doing subcalls and initialising new EVM contracts.
|
||||
type CallContext interface {
|
||||
// Call another contract
|
||||
Call(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Take another's contract code and execute within our own context
|
||||
CallCode(env *Environment, me ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error)
|
||||
// Same as CallCode except sender and value is propagated from parent to child scope
|
||||
DelegateCall(env *Environment, me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error)
|
||||
// Create a new contract
|
||||
Create(env *Environment, me ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error)
|
||||
}
|
||||
@@ -299,11 +299,11 @@ func CompileProgram(program *Program) (err error) {
|
||||
|
||||
// RunProgram runs the program given the environment and contract and returns an
|
||||
// error if the execution failed (non-consensus)
|
||||
func RunProgram(program *Program, env Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
func RunProgram(program *Program, env *Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
return runProgram(program, 0, NewMemory(), newstack(), env, contract, input)
|
||||
}
|
||||
|
||||
func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env *Environment, contract *Contract, input []byte) ([]byte, error) {
|
||||
contract.Input = input
|
||||
|
||||
var (
|
||||
@@ -319,7 +319,7 @@ func runProgram(program *Program, pcstart uint64, mem *Memory, stack *Stack, env
|
||||
}()
|
||||
}
|
||||
|
||||
homestead := env.ChainConfig().IsHomestead(env.BlockNumber())
|
||||
homestead := env.ChainConfig().IsHomestead(env.BlockNumber)
|
||||
for pc < uint64(len(program.instructions)) {
|
||||
instrCount++
|
||||
|
||||
@@ -357,7 +357,7 @@ func validDest(dests map[uint64]struct{}, dest *big.Int) bool {
|
||||
|
||||
// jitCalculateGasAndSize 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 jitCalculateGasAndSize(env Environment, contract *Contract, instr instruction, statedb Database, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
func jitCalculateGasAndSize(env *Environment, contract *Contract, instr instruction, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
@@ -408,7 +408,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := statedb.GetState(contract.Address(), common.BigToHash(x))
|
||||
val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
@@ -417,7 +417,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||
if common.EmptyHash(val) && !common.EmptyHash(common.BigToHash(y)) {
|
||||
g = params.SstoreSetGas
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
statedb.AddRefund(params.SstoreRefundGas)
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
} else {
|
||||
@@ -425,8 +425,8 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||
}
|
||||
gas.Set(g)
|
||||
case SUICIDE:
|
||||
if !statedb.HasSuicided(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case MLOAD:
|
||||
newMemSize = calcMemSize(stack.peek(), u256(32))
|
||||
@@ -463,7 +463,7 @@ func jitCalculateGasAndSize(env Environment, contract *Contract, instr instructi
|
||||
gas.Add(gas, stack.data[stack.len()-1])
|
||||
|
||||
if op == CALL {
|
||||
if !env.Db().Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||
if !env.StateDB.Exist(common.BigToAddress(stack.data[stack.len()-2])) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,10 +19,8 @@ package vm
|
||||
import (
|
||||
"math/big"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
@@ -86,8 +84,8 @@ func TestCompiling(t *testing.T) {
|
||||
func TestResetInput(t *testing.T) {
|
||||
var sender account
|
||||
|
||||
env := NewEnv(&Config{EnableJit: true, ForceJit: true})
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{})
|
||||
contract := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
contract.CodeAddr = &common.Address{}
|
||||
|
||||
program := NewProgram([]byte{})
|
||||
@@ -135,7 +133,7 @@ func (account) SetBalance(*big.Int) {}
|
||||
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) ReturnGas(*big.Int) {}
|
||||
func (account) SetCode(common.Hash, []byte) {}
|
||||
func (account) ForEachStorage(cb func(key, value common.Hash) bool) {}
|
||||
|
||||
@@ -145,70 +143,18 @@ func runVmBench(test vmBench, b *testing.B) {
|
||||
if test.precompile && !test.forcejit {
|
||||
NewProgram(test.code)
|
||||
}
|
||||
env := NewEnv(&Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
|
||||
env := NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: !test.nojit, ForceJit: test.forcejit})
|
||||
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000), big.NewInt(0))
|
||||
context := NewContract(sender, sender, big.NewInt(100), big.NewInt(10000))
|
||||
context.Code = test.code
|
||||
context.CodeAddr = &common.Address{}
|
||||
_, err := env.Vm().Run(context, test.input)
|
||||
_, err := env.EVM().Run(context, test.input)
|
||||
if err != nil {
|
||||
b.Error(err)
|
||||
b.FailNow()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Env struct {
|
||||
gasLimit *big.Int
|
||||
depth int
|
||||
evm *EVM
|
||||
}
|
||||
|
||||
func NewEnv(config *Config) *Env {
|
||||
env := &Env{gasLimit: big.NewInt(10000), depth: 0}
|
||||
env.evm = New(env, *config)
|
||||
return env
|
||||
}
|
||||
|
||||
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) }
|
||||
|
||||
//func (self *Env) PrevHash() []byte { return self.parent }
|
||||
func (self *Env) Coinbase() common.Address { return common.Address{} }
|
||||
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 }
|
||||
func (self *Env) GasLimit() *big.Int { return self.gasLimit }
|
||||
func (self *Env) VmType() Type { return StdVmTy }
|
||||
func (self *Env) GetHash(n uint64) common.Hash {
|
||||
return common.BytesToHash(crypto.Keccak256([]byte(big.NewInt(int64(n)).String())))
|
||||
}
|
||||
func (self *Env) AddLog(log *Log) {
|
||||
}
|
||||
func (self *Env) Depth() int { return self.depth }
|
||||
func (self *Env) SetDepth(i int) { self.depth = i }
|
||||
func (self *Env) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return true
|
||||
}
|
||||
func (self *Env) Transfer(from, to Account, amount *big.Int) {}
|
||||
func (self *Env) Call(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) CallCode(caller ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (self *Env) Create(caller ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return nil, common.Address{}, nil
|
||||
}
|
||||
func (self *Env) DelegateCall(me ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
156
core/vm/log.go
156
core/vm/log.go
@@ -23,57 +23,79 @@ import (
|
||||
"io"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
)
|
||||
|
||||
var errMissingLogFields = errors.New("missing required JSON log fields")
|
||||
|
||||
// Log represents a contract log event. These events are generated by the LOG
|
||||
// opcode and stored/indexed by the node.
|
||||
// Log represents a contract log event. These events are generated by the LOG opcode and
|
||||
// stored/indexed by the node.
|
||||
type Log struct {
|
||||
// Consensus fields.
|
||||
Address common.Address // address of the contract that generated the event
|
||||
Topics []common.Hash // list of topics provided by the contract.
|
||||
Data []byte // supplied by the contract, usually ABI-encoded
|
||||
|
||||
// Derived fields (don't reorder!).
|
||||
// Derived fields. These fields are filled in by the node
|
||||
// but not secured by consensus.
|
||||
BlockNumber uint64 // block in which the transaction was included
|
||||
TxHash common.Hash // hash of the transaction
|
||||
TxIndex uint // index of the transaction in the block
|
||||
BlockHash common.Hash // hash of the block in which the transaction was included
|
||||
Index uint // index of the log in the receipt
|
||||
|
||||
// The Removed field is true if this log was reverted due to a chain reorganisation.
|
||||
// You must pay attention to this field if you receive logs through a filter query.
|
||||
Removed bool
|
||||
}
|
||||
|
||||
type rlpLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
}
|
||||
|
||||
type rlpStorageLog struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
BlockNumber uint64
|
||||
TxHash common.Hash
|
||||
TxIndex uint
|
||||
BlockHash common.Hash
|
||||
Index uint
|
||||
}
|
||||
|
||||
type jsonLog struct {
|
||||
Address *common.Address `json:"address"`
|
||||
Topics *[]common.Hash `json:"topics"`
|
||||
Data string `json:"data"`
|
||||
BlockNumber string `json:"blockNumber"`
|
||||
TxIndex string `json:"transactionIndex"`
|
||||
Data *hexutil.Bytes `json:"data"`
|
||||
BlockNumber *hexutil.Uint64 `json:"blockNumber"`
|
||||
TxIndex *hexutil.Uint `json:"transactionIndex"`
|
||||
TxHash *common.Hash `json:"transactionHash"`
|
||||
BlockHash *common.Hash `json:"blockHash"`
|
||||
Index string `json:"logIndex"`
|
||||
Index *hexutil.Uint `json:"logIndex"`
|
||||
Removed bool `json:"removed"`
|
||||
}
|
||||
|
||||
func NewLog(address common.Address, topics []common.Hash, data []byte, number uint64) *Log {
|
||||
return &Log{Address: address, Topics: topics, Data: data, BlockNumber: number}
|
||||
}
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *Log) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, []interface{}{l.Address, l.Topics, l.Data})
|
||||
return rlp.Encode(w, rlpLog{Address: l.Address, Topics: l.Topics, Data: l.Data})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (l *Log) DecodeRLP(s *rlp.Stream) error {
|
||||
var log struct {
|
||||
Address common.Address
|
||||
Topics []common.Hash
|
||||
Data []byte
|
||||
var dec rlpLog
|
||||
err := s.Decode(&dec)
|
||||
if err == nil {
|
||||
l.Address, l.Topics, l.Data = dec.Address, dec.Topics, dec.Data
|
||||
}
|
||||
if err := s.Decode(&log); err != nil {
|
||||
return err
|
||||
}
|
||||
l.Address, l.Topics, l.Data = log.Address, log.Topics, log.Data
|
||||
return nil
|
||||
return err
|
||||
}
|
||||
|
||||
func (l *Log) String() string {
|
||||
@@ -81,54 +103,88 @@ func (l *Log) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements json.Marshaler.
|
||||
func (r *Log) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(&jsonLog{
|
||||
Address: &r.Address,
|
||||
Topics: &r.Topics,
|
||||
Data: fmt.Sprintf("0x%x", r.Data),
|
||||
BlockNumber: fmt.Sprintf("0x%x", r.BlockNumber),
|
||||
TxIndex: fmt.Sprintf("0x%x", r.TxIndex),
|
||||
TxHash: &r.TxHash,
|
||||
BlockHash: &r.BlockHash,
|
||||
Index: fmt.Sprintf("0x%x", r.Index),
|
||||
})
|
||||
func (l *Log) MarshalJSON() ([]byte, error) {
|
||||
jslog := &jsonLog{
|
||||
Address: &l.Address,
|
||||
Topics: &l.Topics,
|
||||
Data: (*hexutil.Bytes)(&l.Data),
|
||||
TxIndex: (*hexutil.Uint)(&l.TxIndex),
|
||||
TxHash: &l.TxHash,
|
||||
Index: (*hexutil.Uint)(&l.Index),
|
||||
Removed: l.Removed,
|
||||
}
|
||||
// Set block information for mined logs.
|
||||
if (l.BlockHash != common.Hash{}) {
|
||||
jslog.BlockHash = &l.BlockHash
|
||||
jslog.BlockNumber = (*hexutil.Uint64)(&l.BlockNumber)
|
||||
}
|
||||
return json.Marshal(jslog)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements json.Umarshaler.
|
||||
func (r *Log) UnmarshalJSON(input []byte) error {
|
||||
func (l *Log) UnmarshalJSON(input []byte) error {
|
||||
var dec jsonLog
|
||||
if err := json.Unmarshal(input, &dec); err != nil {
|
||||
return err
|
||||
}
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == "" || dec.BlockNumber == "" ||
|
||||
dec.TxIndex == "" || dec.TxHash == nil || dec.BlockHash == nil || dec.Index == "" {
|
||||
if dec.Address == nil || dec.Topics == nil || dec.Data == nil ||
|
||||
dec.TxIndex == nil || dec.TxHash == nil || dec.Index == nil {
|
||||
return errMissingLogFields
|
||||
}
|
||||
declog := Log{
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
TxHash: *dec.TxHash,
|
||||
BlockHash: *dec.BlockHash,
|
||||
Address: *dec.Address,
|
||||
Topics: *dec.Topics,
|
||||
Data: *dec.Data,
|
||||
TxHash: *dec.TxHash,
|
||||
TxIndex: uint(*dec.TxIndex),
|
||||
Index: uint(*dec.Index),
|
||||
Removed: dec.Removed,
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.Data, "0x%x", &declog.Data); err != nil {
|
||||
return fmt.Errorf("invalid hex log data")
|
||||
// Block information may be missing if the log is received through
|
||||
// the pending log filter, so it's handled specially here.
|
||||
if dec.BlockHash != nil && dec.BlockNumber != nil {
|
||||
declog.BlockHash = *dec.BlockHash
|
||||
declog.BlockNumber = uint64(*dec.BlockNumber)
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.BlockNumber, "0x%x", &declog.BlockNumber); err != nil {
|
||||
return fmt.Errorf("invalid hex log block number")
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.TxIndex, "0x%x", &declog.TxIndex); err != nil {
|
||||
return fmt.Errorf("invalid hex log tx index")
|
||||
}
|
||||
if _, err := fmt.Sscanf(dec.Index, "0x%x", &declog.Index); err != nil {
|
||||
return fmt.Errorf("invalid hex log index")
|
||||
}
|
||||
*r = declog
|
||||
*l = declog
|
||||
return nil
|
||||
}
|
||||
|
||||
type Logs []*Log
|
||||
|
||||
// LogForStorage is a wrapper around a Log that flattens and parses the entire
|
||||
// content of a log, as opposed to only the consensus fields originally (by hiding
|
||||
// the rlp interface methods).
|
||||
// LogForStorage is a wrapper around a Log that flattens and parses the entire content of
|
||||
// a log including non-consensus fields.
|
||||
type LogForStorage Log
|
||||
|
||||
// EncodeRLP implements rlp.Encoder.
|
||||
func (l *LogForStorage) EncodeRLP(w io.Writer) error {
|
||||
return rlp.Encode(w, rlpStorageLog{
|
||||
Address: l.Address,
|
||||
Topics: l.Topics,
|
||||
Data: l.Data,
|
||||
BlockNumber: l.BlockNumber,
|
||||
TxHash: l.TxHash,
|
||||
TxIndex: l.TxIndex,
|
||||
BlockHash: l.BlockHash,
|
||||
Index: l.Index,
|
||||
})
|
||||
}
|
||||
|
||||
// DecodeRLP implements rlp.Decoder.
|
||||
func (l *LogForStorage) DecodeRLP(s *rlp.Stream) error {
|
||||
var dec rlpStorageLog
|
||||
err := s.Decode(&dec)
|
||||
if err == nil {
|
||||
*l = LogForStorage{
|
||||
Address: dec.Address,
|
||||
Topics: dec.Topics,
|
||||
Data: dec.Data,
|
||||
BlockNumber: dec.BlockNumber,
|
||||
TxHash: dec.TxHash,
|
||||
TxIndex: dec.TxIndex,
|
||||
BlockHash: dec.BlockHash,
|
||||
Index: dec.Index,
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -18,15 +18,81 @@ package vm
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/davecgh/go-spew/spew"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
)
|
||||
|
||||
var unmarshalLogTests = map[string]struct {
|
||||
input string
|
||||
want *Log
|
||||
wantError error
|
||||
}{
|
||||
"ok": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x000000000000000000000000000000000000000000000001a055690d9db80000","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
want: &Log{
|
||||
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
|
||||
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
|
||||
BlockNumber: 2019236,
|
||||
Data: hexutil.MustDecode("0x000000000000000000000000000000000000000000000001a055690d9db80000"),
|
||||
Index: 2,
|
||||
TxIndex: 3,
|
||||
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"empty data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
want: &Log{
|
||||
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
|
||||
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
|
||||
BlockNumber: 2019236,
|
||||
Data: []byte{},
|
||||
Index: 2,
|
||||
TxIndex: 3,
|
||||
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
common.HexToHash("0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"missing block fields (pending logs)": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","data":"0x","logIndex":"0x0","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
want: &Log{
|
||||
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
|
||||
BlockHash: common.Hash{},
|
||||
BlockNumber: 0,
|
||||
Data: []byte{},
|
||||
Index: 0,
|
||||
TxIndex: 3,
|
||||
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
},
|
||||
},
|
||||
},
|
||||
"Removed: true": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","data":"0x","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3","removed":true}`,
|
||||
want: &Log{
|
||||
Address: common.HexToAddress("0xecf8f87f810ecf450940c9f60066b4a7a501d6a7"),
|
||||
BlockHash: common.HexToHash("0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056"),
|
||||
BlockNumber: 2019236,
|
||||
Data: []byte{},
|
||||
Index: 2,
|
||||
TxIndex: 3,
|
||||
TxHash: common.HexToHash("0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e"),
|
||||
Topics: []common.Hash{
|
||||
common.HexToHash("0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef"),
|
||||
},
|
||||
Removed: true,
|
||||
},
|
||||
},
|
||||
"missing data": {
|
||||
input: `{"address":"0xecf8f87f810ecf450940c9f60066b4a7a501d6a7","blockHash":"0x656c34545f90a730a19008c0e7a7cd4fb3895064b48d6d69761bd5abad681056","blockNumber":"0x1ecfa4","logIndex":"0x2","topics":["0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef","0x00000000000000000000000080b2c9d7cbbf30a1b0fc8983c647d754c6525615","0x000000000000000000000000f9dff387dcb5cc4cca5b91adb07a95f54e9f1bb6"],"transactionHash":"0x3b198bfd5d2907285af009e9ae84a0ecd63677110d89d7e030251acb87f6487e","transactionIndex":"0x3"}`,
|
||||
@@ -35,10 +101,16 @@ var unmarshalLogTests = map[string]struct {
|
||||
}
|
||||
|
||||
func TestUnmarshalLog(t *testing.T) {
|
||||
dumper := spew.ConfigState{DisableMethods: true, Indent: " "}
|
||||
for name, test := range unmarshalLogTests {
|
||||
var log *Log
|
||||
err := json.Unmarshal([]byte(test.input), &log)
|
||||
checkError(t, name, err, test.wantError)
|
||||
if test.wantError == nil && err == nil {
|
||||
if !reflect.DeepEqual(log, test.want) {
|
||||
t.Errorf("test %q:\nGOT %sWANT %s", name, dumper.Sdump(log), dumper.Sdump(test.want))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ type StructLog struct {
|
||||
// Note that reference types are actual VM data structures; make copies
|
||||
// if you need to retain them beyond the current call.
|
||||
type Tracer interface {
|
||||
CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error
|
||||
}
|
||||
|
||||
// StructLogger is an EVM state logger and implements Tracer.
|
||||
@@ -94,7 +94,7 @@ func NewStructLogger(cfg *LogConfig) *StructLogger {
|
||||
// captureState logs a new structured log message and pushes it out to the environment
|
||||
//
|
||||
// captureState also tracks SSTORE ops to track dirty values.
|
||||
func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
func (l *StructLogger) CaptureState(env *Environment, pc uint64, op OpCode, gas, cost *big.Int, memory *Memory, stack *Stack, contract *Contract, depth int, err error) error {
|
||||
// check if already accumulated the specified number of logs
|
||||
if l.cfg.Limit != 0 && l.cfg.Limit <= len(l.logs) {
|
||||
return TraceLimitReachedError
|
||||
@@ -144,7 +144,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
|
||||
storage = make(Storage)
|
||||
// Get the contract account and loop over each storage entry. This may involve looping over
|
||||
// the trie and is a very expensive process.
|
||||
env.Db().GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
|
||||
env.StateDB.GetAccount(contract.Address()).ForEachStorage(func(key, value common.Hash) bool {
|
||||
storage[key] = value
|
||||
// Return true, indicating we'd like to continue.
|
||||
return true
|
||||
@@ -155,7 +155,7 @@ func (l *StructLogger) CaptureState(env Environment, pc uint64, op OpCode, gas,
|
||||
}
|
||||
}
|
||||
// create a new snaptshot of the EVM.
|
||||
log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth(), err}
|
||||
log := StructLog{pc, op, new(big.Int).Set(gas), cost, mem, stck, storage, env.Depth, err}
|
||||
|
||||
l.logs = append(l.logs, log)
|
||||
return nil
|
||||
|
||||
@@ -21,16 +21,17 @@ import (
|
||||
"testing"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
)
|
||||
|
||||
type dummyContractRef struct {
|
||||
calledForEach bool
|
||||
}
|
||||
|
||||
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(common.Hash, []byte) {}
|
||||
func (dummyContractRef) ReturnGas(*big.Int) {}
|
||||
func (dummyContractRef) Address() common.Address { return common.Address{} }
|
||||
func (dummyContractRef) Value() *big.Int { return new(big.Int) }
|
||||
func (dummyContractRef) SetCode(common.Hash, []byte) {}
|
||||
func (d *dummyContractRef) ForEachStorage(callback func(key, value common.Hash) bool) {
|
||||
d.calledForEach = true
|
||||
}
|
||||
@@ -40,28 +41,22 @@ func (d *dummyContractRef) SetBalance(*big.Int) {}
|
||||
func (d *dummyContractRef) SetNonce(uint64) {}
|
||||
func (d *dummyContractRef) Balance() *big.Int { return new(big.Int) }
|
||||
|
||||
type dummyEnv struct {
|
||||
*Env
|
||||
type dummyStateDB struct {
|
||||
NoopStateDB
|
||||
ref *dummyContractRef
|
||||
}
|
||||
|
||||
func newDummyEnv(ref *dummyContractRef) *dummyEnv {
|
||||
return &dummyEnv{
|
||||
Env: NewEnv(&Config{EnableJit: false, ForceJit: false}),
|
||||
ref: ref,
|
||||
}
|
||||
}
|
||||
func (d dummyEnv) GetAccount(common.Address) Account {
|
||||
func (d dummyStateDB) GetAccount(common.Address) Account {
|
||||
return d.ref
|
||||
}
|
||||
|
||||
func TestStoreCapture(t *testing.T) {
|
||||
var (
|
||||
env = NewEnv(&Config{EnableJit: false, ForceJit: false})
|
||||
env = NewEnvironment(Context{}, nil, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int), new(big.Int))
|
||||
contract = NewContract(&dummyContractRef{}, &dummyContractRef{}, new(big.Int), new(big.Int))
|
||||
)
|
||||
stack.push(big.NewInt(1))
|
||||
stack.push(big.NewInt(0))
|
||||
@@ -83,8 +78,8 @@ func TestStorageCapture(t *testing.T) {
|
||||
t.Skip("implementing this function is difficult. it requires all sort of interfaces to be implemented which isn't trivial. The value (the actual test) isn't worth it")
|
||||
var (
|
||||
ref = &dummyContractRef{}
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int), new(big.Int))
|
||||
env = newDummyEnv(ref)
|
||||
contract = NewContract(ref, ref, new(big.Int), new(big.Int))
|
||||
env = NewEnvironment(Context{}, dummyStateDB{ref: ref}, params.TestChainConfig, Config{EnableJit: false, ForceJit: false})
|
||||
logger = NewStructLogger(nil)
|
||||
mem = NewMemory()
|
||||
stack = newstack()
|
||||
|
||||
68
core/vm/noop.go
Normal file
68
core/vm/noop.go
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 vm
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
)
|
||||
|
||||
func NoopCanTransfer(db StateDB, from common.Address, balance *big.Int) bool {
|
||||
return true
|
||||
}
|
||||
func NoopTransfer(db StateDB, from, to common.Address, amount *big.Int) {}
|
||||
|
||||
type NoopEVMCallContext struct{}
|
||||
|
||||
func (NoopEVMCallContext) Call(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (NoopEVMCallContext) CallCode(caller ContractRef, addr common.Address, data []byte, gas, value *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
func (NoopEVMCallContext) Create(caller ContractRef, data []byte, gas, value *big.Int) ([]byte, common.Address, error) {
|
||||
return nil, common.Address{}, nil
|
||||
}
|
||||
func (NoopEVMCallContext) DelegateCall(me ContractRef, addr common.Address, data []byte, gas *big.Int) ([]byte, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
type NoopStateDB struct{}
|
||||
|
||||
func (NoopStateDB) GetAccount(common.Address) Account { return nil }
|
||||
func (NoopStateDB) CreateAccount(common.Address) Account { return nil }
|
||||
func (NoopStateDB) SubBalance(common.Address, *big.Int) {}
|
||||
func (NoopStateDB) AddBalance(common.Address, *big.Int) {}
|
||||
func (NoopStateDB) GetBalance(common.Address) *big.Int { return nil }
|
||||
func (NoopStateDB) GetNonce(common.Address) uint64 { return 0 }
|
||||
func (NoopStateDB) SetNonce(common.Address, uint64) {}
|
||||
func (NoopStateDB) GetCodeHash(common.Address) common.Hash { return common.Hash{} }
|
||||
func (NoopStateDB) GetCode(common.Address) []byte { return nil }
|
||||
func (NoopStateDB) SetCode(common.Address, []byte) {}
|
||||
func (NoopStateDB) GetCodeSize(common.Address) int { return 0 }
|
||||
func (NoopStateDB) AddRefund(*big.Int) {}
|
||||
func (NoopStateDB) GetRefund() *big.Int { return nil }
|
||||
func (NoopStateDB) GetState(common.Address, common.Hash) common.Hash { return common.Hash{} }
|
||||
func (NoopStateDB) SetState(common.Address, common.Hash, common.Hash) {}
|
||||
func (NoopStateDB) Suicide(common.Address) bool { return false }
|
||||
func (NoopStateDB) HasSuicided(common.Address) bool { return false }
|
||||
func (NoopStateDB) Exist(common.Address) bool { return false }
|
||||
func (NoopStateDB) Empty(common.Address) bool { return false }
|
||||
func (NoopStateDB) RevertToSnapshot(int) {}
|
||||
func (NoopStateDB) Snapshot() int { return 0 }
|
||||
func (NoopStateDB) AddLog(*Log) {}
|
||||
@@ -23,92 +23,22 @@ 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 {
|
||||
chainConfig *params.ChainConfig
|
||||
depth int
|
||||
state *state.StateDB
|
||||
func NewEnv(cfg *Config, state *state.StateDB) *vm.Environment {
|
||||
context := vm.Context{
|
||||
CanTransfer: core.CanTransfer,
|
||||
Transfer: core.Transfer,
|
||||
GetHash: func(uint64) common.Hash { return common.Hash{} },
|
||||
|
||||
origin common.Address
|
||||
coinbase common.Address
|
||||
|
||||
number *big.Int
|
||||
time *big.Int
|
||||
difficulty *big.Int
|
||||
gasLimit *big.Int
|
||||
|
||||
getHashFn func(uint64) common.Hash
|
||||
|
||||
evm *vm.EVM
|
||||
}
|
||||
|
||||
// NewEnv returns a new vm.Environment
|
||||
func NewEnv(cfg *Config, state *state.StateDB) vm.Environment {
|
||||
env := &Env{
|
||||
chainConfig: cfg.ChainConfig,
|
||||
state: state,
|
||||
origin: cfg.Origin,
|
||||
coinbase: cfg.Coinbase,
|
||||
number: cfg.BlockNumber,
|
||||
time: cfg.Time,
|
||||
difficulty: cfg.Difficulty,
|
||||
gasLimit: cfg.GasLimit,
|
||||
Origin: cfg.Origin,
|
||||
Coinbase: cfg.Coinbase,
|
||||
BlockNumber: cfg.BlockNumber,
|
||||
Time: cfg.Time,
|
||||
Difficulty: cfg.Difficulty,
|
||||
GasLimit: cfg.GasLimit,
|
||||
GasPrice: new(big.Int),
|
||||
}
|
||||
env.evm = vm.New(env, vm.Config{
|
||||
Debug: cfg.Debug,
|
||||
EnableJit: !cfg.DisableJit,
|
||||
ForceJit: !cfg.DisableJit,
|
||||
})
|
||||
|
||||
return env
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
func (self *Env) AddLog(log *vm.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *Env) Depth() int { return self.depth }
|
||||
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) SnapshotDatabase() int {
|
||||
return self.state.Snapshot()
|
||||
}
|
||||
func (self *Env) RevertToSnapshot(snapshot int) {
|
||||
self.state.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
func (self *Env) Transfer(from, to vm.Account, amount *big.Int) {
|
||||
core.Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *Env) Call(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.Call(self, caller, addr, data, gas, price, value)
|
||||
}
|
||||
func (self *Env) CallCode(caller vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return core.CallCode(self, caller, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *Env) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return core.DelegateCall(self, me, addr, data, gas, price)
|
||||
}
|
||||
|
||||
func (self *Env) Create(caller vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return core.Create(self, caller, data, gas, price, value)
|
||||
return vm.NewEnvironment(context, cfg.State, cfg.ChainConfig, cfg.EVMConfig)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ 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"
|
||||
@@ -49,6 +50,7 @@ type Config struct {
|
||||
Value *big.Int
|
||||
DisableJit bool // "disable" so it's enabled by default
|
||||
Debug bool
|
||||
EVMConfig vm.Config
|
||||
|
||||
State *state.StateDB
|
||||
GetHashFn func(n uint64) common.Hash
|
||||
@@ -123,13 +125,37 @@ func Execute(code, input []byte, cfg *Config) ([]byte, *state.StateDB, error) {
|
||||
receiver.Address(),
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.GasPrice,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
return ret, cfg.State, err
|
||||
}
|
||||
|
||||
// Create executes the code using the EVM create method
|
||||
func Create(input []byte, cfg *Config) ([]byte, common.Address, error) {
|
||||
if cfg == nil {
|
||||
cfg = new(Config)
|
||||
}
|
||||
setDefaults(cfg)
|
||||
|
||||
if cfg.State == nil {
|
||||
db, _ := ethdb.NewMemDatabase()
|
||||
cfg.State, _ = state.New(common.Hash{}, db)
|
||||
}
|
||||
var (
|
||||
vmenv = NewEnv(cfg, cfg.State)
|
||||
sender = cfg.State.CreateAccount(cfg.Origin)
|
||||
)
|
||||
|
||||
// Call the code with the given configuration.
|
||||
return vmenv.Create(
|
||||
sender,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.Value,
|
||||
)
|
||||
}
|
||||
|
||||
// Call executes the code given by the contract's address. It will return the
|
||||
// EVM's return value or an error if it failed.
|
||||
//
|
||||
@@ -147,7 +173,6 @@ func Call(address common.Address, input []byte, cfg *Config) ([]byte, error) {
|
||||
address,
|
||||
input,
|
||||
cfg.GasLimit,
|
||||
cfg.GasPrice,
|
||||
cfg.Value,
|
||||
)
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ type jumpSeg struct {
|
||||
gas *big.Int
|
||||
}
|
||||
|
||||
func (j jumpSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func (j jumpSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
if !contract.UseGas(j.gas) {
|
||||
return nil, OutOfGasError
|
||||
}
|
||||
@@ -42,7 +42,7 @@ type pushSeg struct {
|
||||
gas *big.Int
|
||||
}
|
||||
|
||||
func (s pushSeg) do(program *Program, pc *uint64, env Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
func (s pushSeg) do(program *Program, pc *uint64, env *Environment, contract *Contract, memory *Memory, stack *Stack) ([]byte, error) {
|
||||
// Use the calculated gas. When insufficient gas is present, use all gas and return an
|
||||
// Out Of Gas error
|
||||
if !contract.UseGas(s.gas) {
|
||||
|
||||
@@ -1,32 +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 vm
|
||||
|
||||
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
|
||||
}
|
||||
@@ -30,10 +30,17 @@ import (
|
||||
|
||||
// Config are the configuration options for the EVM
|
||||
type Config struct {
|
||||
Debug bool
|
||||
// Debug enabled debugging EVM options
|
||||
Debug bool
|
||||
// EnableJit enabled the JIT VM
|
||||
EnableJit bool
|
||||
ForceJit bool
|
||||
Tracer Tracer
|
||||
// ForceJit forces the JIT VM
|
||||
ForceJit bool
|
||||
// Tracer is the op code logger
|
||||
Tracer Tracer
|
||||
// NoRecursion disabled EVM call, callcode,
|
||||
// delegate call and create.
|
||||
NoRecursion bool
|
||||
}
|
||||
|
||||
// EVM is used to run Ethereum based contracts and will utilise the
|
||||
@@ -41,26 +48,26 @@ type Config struct {
|
||||
// The EVM will run the byte code VM or JIT VM based on the passed
|
||||
// configuration.
|
||||
type EVM struct {
|
||||
env Environment
|
||||
env *Environment
|
||||
jumpTable vmJumpTable
|
||||
cfg Config
|
||||
gasTable params.GasTable
|
||||
}
|
||||
|
||||
// New returns a new instance of the EVM.
|
||||
func New(env Environment, cfg Config) *EVM {
|
||||
func New(env *Environment, cfg Config) *EVM {
|
||||
return &EVM{
|
||||
env: env,
|
||||
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber()),
|
||||
jumpTable: newJumpTable(env.ChainConfig(), env.BlockNumber),
|
||||
cfg: cfg,
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber()),
|
||||
gasTable: env.ChainConfig().GasTable(env.BlockNumber),
|
||||
}
|
||||
}
|
||||
|
||||
// Run loops and evaluates the contract's code with the given input data
|
||||
func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
evm.env.SetDepth(evm.env.Depth() + 1)
|
||||
defer evm.env.SetDepth(evm.env.Depth() - 1)
|
||||
evm.env.Depth++
|
||||
defer func() { evm.env.Depth-- }()
|
||||
|
||||
if contract.CodeAddr != nil {
|
||||
if p := Precompiled[contract.CodeAddr.Str()]; p != nil {
|
||||
@@ -117,10 +124,9 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
code = contract.Code
|
||||
instrCount = 0
|
||||
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
statedb = evm.env.Db() // current state
|
||||
op OpCode // current opcode
|
||||
mem = NewMemory() // bound memory
|
||||
stack = newstack() // local stack
|
||||
// For optimisation reason we're using uint64 as the program counter.
|
||||
// It's theoretically possible to go above 2^64. The YP defines the PC to be uint256. Practically much less so feasible.
|
||||
pc = uint64(0) // program counter
|
||||
@@ -146,7 +152,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
// User defer pattern to check for an error and, based on the error being nil or not, use all gas and return.
|
||||
defer func() {
|
||||
if err != nil && evm.cfg.Debug {
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), err)
|
||||
evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -174,7 +180,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
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.gasTable, evm.env, contract, caller, op, statedb, mem, stack)
|
||||
newMemSize, cost, err = calculateGasAndSize(evm.gasTable, evm.env, contract, caller, op, mem, stack)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -189,7 +195,7 @@ func (evm *EVM) Run(contract *Contract, input []byte) (ret []byte, err error) {
|
||||
mem.Resize(newMemSize.Uint64())
|
||||
// Add a log message
|
||||
if evm.cfg.Debug {
|
||||
err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth(), nil)
|
||||
err = evm.cfg.Tracer.CaptureState(evm.env, pc, op, contract.Gas, cost, mem, stack, contract, evm.env.Depth, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -242,7 +248,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(gasTable params.GasTable, 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, mem *Memory, stack *Stack) (*big.Int, *big.Int, error) {
|
||||
var (
|
||||
gas = new(big.Int)
|
||||
newMemSize *big.Int = new(big.Int)
|
||||
@@ -260,21 +266,21 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||
gas.Set(gasTable.Suicide)
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-1])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
|
||||
if eip158 {
|
||||
// if empty and transfers value
|
||||
if env.Db().Empty(address) && statedb.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
if env.StateDB.Empty(address) && env.StateDB.GetBalance(contract.Address()).BitLen() > 0 {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
} else if !env.Db().Exist(address) {
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, gasTable.CreateBySuicide)
|
||||
}
|
||||
}
|
||||
|
||||
if !statedb.HasSuicided(contract.Address()) {
|
||||
statedb.AddRefund(params.SuicideRefundGas)
|
||||
if !env.StateDB.HasSuicided(contract.Address()) {
|
||||
env.StateDB.AddRefund(params.SuicideRefundGas)
|
||||
}
|
||||
case EXTCODESIZE:
|
||||
gas.Set(gasTable.ExtcodeSize)
|
||||
@@ -323,7 +329,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||
|
||||
var g *big.Int
|
||||
y, x := stack.data[stack.len()-2], stack.data[stack.len()-1]
|
||||
val := statedb.GetState(contract.Address(), common.BigToHash(x))
|
||||
val := env.StateDB.GetState(contract.Address(), common.BigToHash(x))
|
||||
|
||||
// This checks for 3 scenario's and calculates gas accordingly
|
||||
// 1. From a zero-value address to a non-zero value (NEW VALUE)
|
||||
@@ -333,7 +339,7 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||
// 0 => non 0
|
||||
g = params.SstoreSetGas
|
||||
} else if !common.EmptyHash(val) && common.EmptyHash(common.BigToHash(y)) {
|
||||
statedb.AddRefund(params.SstoreRefundGas)
|
||||
env.StateDB.AddRefund(params.SstoreRefundGas)
|
||||
|
||||
g = params.SstoreClearGas
|
||||
} else {
|
||||
@@ -394,13 +400,13 @@ func calculateGasAndSize(gasTable params.GasTable, env Environment, contract *Co
|
||||
if op == CALL {
|
||||
var (
|
||||
address = common.BigToAddress(stack.data[len(stack.data)-2])
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber())
|
||||
eip158 = env.ChainConfig().IsEIP158(env.BlockNumber)
|
||||
)
|
||||
if eip158 {
|
||||
if env.Db().Empty(address) && transfersValue {
|
||||
if env.StateDB.Empty(address) && transfersValue {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
} else if !env.Db().Exist(address) {
|
||||
} else if !env.StateDB.Exist(address) {
|
||||
gas.Add(gas, params.CallNewAccountGas)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,10 +17,3 @@
|
||||
// +build !evmjit
|
||||
|
||||
package vm
|
||||
|
||||
import "fmt"
|
||||
|
||||
func NewJitVm(env Environment) VirtualMachine {
|
||||
fmt.Printf("Warning! EVM JIT not enabled.\n")
|
||||
return New(env, Config{})
|
||||
}
|
||||
|
||||
101
core/vm_env.go
101
core/vm_env.go
@@ -15,104 +15,3 @@
|
||||
// along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
package core
|
||||
|
||||
import (
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"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
|
||||
// up to the limit defined by the Yellow Paper and uses the given block chain
|
||||
// to query for information.
|
||||
func GetHashFn(ref common.Hash, chain *BlockChain) func(n uint64) common.Hash {
|
||||
return func(n uint64) common.Hash {
|
||||
for block := chain.GetBlockByHash(ref); block != nil; block = chain.GetBlock(block.ParentHash(), block.NumberU64()-1) {
|
||||
if block.NumberU64() == n {
|
||||
return block.Hash()
|
||||
}
|
||||
}
|
||||
|
||||
return common.Hash{}
|
||||
}
|
||||
}
|
||||
|
||||
type VMEnv struct {
|
||||
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
|
||||
getHashFn func(uint64) common.Hash // getHashFn callback is used to retrieve block hashes
|
||||
}
|
||||
|
||||
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,
|
||||
state: state,
|
||||
header: header,
|
||||
msg: msg,
|
||||
getHashFn: GetHashFn(header.ParentHash, chain),
|
||||
}
|
||||
|
||||
env.evm = vm.New(env, cfg)
|
||||
return env
|
||||
}
|
||||
|
||||
func (self *VMEnv) ChainConfig() *params.ChainConfig { return self.chainConfig }
|
||||
func (self *VMEnv) Vm() vm.Vm { return self.evm }
|
||||
func (self *VMEnv) Origin() common.Address { return self.msg.From() }
|
||||
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)
|
||||
}
|
||||
|
||||
func (self *VMEnv) AddLog(log *vm.Log) {
|
||||
self.state.AddLog(log)
|
||||
}
|
||||
func (self *VMEnv) CanTransfer(from common.Address, balance *big.Int) bool {
|
||||
return self.state.GetBalance(from).Cmp(balance) >= 0
|
||||
}
|
||||
|
||||
func (self *VMEnv) SnapshotDatabase() int {
|
||||
return self.state.Snapshot()
|
||||
}
|
||||
|
||||
func (self *VMEnv) RevertToSnapshot(snapshot int) {
|
||||
self.state.RevertToSnapshot(snapshot)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Transfer(from, to vm.Account, amount *big.Int) {
|
||||
Transfer(from, to, amount)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Call(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return Call(self, me, addr, data, gas, price, value)
|
||||
}
|
||||
func (self *VMEnv) CallCode(me vm.ContractRef, addr common.Address, data []byte, gas, price, value *big.Int) ([]byte, error) {
|
||||
return CallCode(self, me, addr, data, gas, price, value)
|
||||
}
|
||||
|
||||
func (self *VMEnv) DelegateCall(me vm.ContractRef, addr common.Address, data []byte, gas, price *big.Int) ([]byte, error) {
|
||||
return DelegateCall(self, me, addr, data, gas, price)
|
||||
}
|
||||
|
||||
func (self *VMEnv) Create(me vm.ContractRef, data []byte, gas, price, value *big.Int) ([]byte, common.Address, error) {
|
||||
return Create(self, me, data, gas, price, value)
|
||||
}
|
||||
|
||||
27
eth/api.go
27
eth/api.go
@@ -18,6 +18,7 @@ package eth
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -25,6 +26,7 @@ import (
|
||||
"math/big"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/ethereum/ethash"
|
||||
@@ -217,8 +219,14 @@ func (api *PrivateAdminAPI) ExportChain(file string) (bool, error) {
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
var writer io.Writer = out
|
||||
if strings.HasSuffix(file, ".gz") {
|
||||
writer = gzip.NewWriter(writer)
|
||||
defer writer.(*gzip.Writer).Close()
|
||||
}
|
||||
|
||||
// Export the blockchain
|
||||
if err := api.eth.BlockChain().Export(out); err != nil {
|
||||
if err := api.eth.BlockChain().Export(writer); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
@@ -243,8 +251,15 @@ func (api *PrivateAdminAPI) ImportChain(file string) (bool, error) {
|
||||
}
|
||||
defer in.Close()
|
||||
|
||||
var reader io.Reader = in
|
||||
if strings.HasSuffix(file, ".gz") {
|
||||
if reader, err = gzip.NewReader(reader); err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
// Run actual the import in pre-configured batches
|
||||
stream := rlp.NewStream(in, 0)
|
||||
stream := rlp.NewStream(reader, 0)
|
||||
|
||||
blocks, index := make([]*types.Block, 0, 2500), 0
|
||||
for batch := 0; ; batch++ {
|
||||
@@ -515,9 +530,11 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("sender retrieval failed: %v", err)
|
||||
}
|
||||
context := core.NewEVMContext(msg, block.Header(), api.eth.BlockChain())
|
||||
|
||||
// Mutate the state if we haven't reached the tracing transaction yet
|
||||
if uint64(idx) < txIndex {
|
||||
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{})
|
||||
vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{})
|
||||
_, _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("mutation failed: %v", err)
|
||||
@@ -525,8 +542,8 @@ func (api *PrivateDebugAPI) TraceTransaction(ctx context.Context, txHash common.
|
||||
stateDb.DeleteSuicides()
|
||||
continue
|
||||
}
|
||||
// Otherwise trace the transaction and return
|
||||
vmenv := core.NewEnv(stateDb, api.config, api.eth.BlockChain(), msg, block.Header(), vm.Config{Debug: true, Tracer: tracer})
|
||||
|
||||
vmenv := vm.NewEnvironment(context, stateDb, api.config, vm.Config{Debug: true, Tracer: tracer})
|
||||
ret, gas, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(tx.Gas()))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tracing failed: %v", err)
|
||||
|
||||
@@ -56,7 +56,7 @@ func (b *EthApiBackend) SetHead(number uint64) {
|
||||
func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Header, error) {
|
||||
// Pending block is only known by the miner
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
block, _ := b.eth.miner.Pending()
|
||||
block := b.eth.miner.PendingBlock()
|
||||
return block.Header(), nil
|
||||
}
|
||||
// Otherwise resolve and return the block
|
||||
@@ -69,7 +69,7 @@ func (b *EthApiBackend) HeaderByNumber(ctx context.Context, blockNr rpc.BlockNum
|
||||
func (b *EthApiBackend) BlockByNumber(ctx context.Context, blockNr rpc.BlockNumber) (*types.Block, error) {
|
||||
// Pending block is only known by the miner
|
||||
if blockNr == rpc.PendingBlockNumber {
|
||||
block, _ := b.eth.miner.Pending()
|
||||
block := b.eth.miner.PendingBlock()
|
||||
return block, nil
|
||||
}
|
||||
// Otherwise resolve and return the block
|
||||
@@ -106,12 +106,14 @@ func (b *EthApiBackend) GetTd(blockHash common.Hash) *big.Int {
|
||||
return b.eth.blockchain.GetTdByHash(blockHash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (vm.Environment, func() error, error) {
|
||||
func (b *EthApiBackend) GetVMEnv(ctx context.Context, msg core.Message, state ethapi.State, header *types.Header) (*vm.Environment, func() error, error) {
|
||||
statedb := state.(EthApiState).state
|
||||
from := statedb.GetOrNewStateObject(msg.From())
|
||||
from.SetBalance(common.MaxBig)
|
||||
vmError := func() error { return nil }
|
||||
return core.NewEnv(statedb, b.eth.chainConfig, b.eth.blockchain, msg, header, vm.Config{}), vmError, nil
|
||||
|
||||
context := core.NewEVMContext(msg, header, b.eth.BlockChain())
|
||||
return vm.NewEnvironment(context, statedb, b.eth.chainConfig, vm.Config{}), vmError, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) SendTx(ctx context.Context, signedTx *types.Transaction) error {
|
||||
@@ -129,15 +131,20 @@ func (b *EthApiBackend) RemoveTx(txHash common.Hash) {
|
||||
b.eth.txPool.Remove(txHash)
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolTransactions() types.Transactions {
|
||||
func (b *EthApiBackend) GetPoolTransactions() (types.Transactions, error) {
|
||||
b.eth.txMu.Lock()
|
||||
defer b.eth.txMu.Unlock()
|
||||
|
||||
pending, err := b.eth.txPool.Pending()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var txs types.Transactions
|
||||
for _, batch := range b.eth.txPool.Pending() {
|
||||
for _, batch := range pending {
|
||||
txs = append(txs, batch...)
|
||||
}
|
||||
return txs
|
||||
return txs, nil
|
||||
}
|
||||
|
||||
func (b *EthApiBackend) GetPoolTransaction(hash common.Hash) *types.Transaction {
|
||||
|
||||
@@ -31,8 +31,6 @@ import (
|
||||
"github.com/ethereum/ethash"
|
||||
"github.com/ethereum/go-ethereum/accounts"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/httpclient"
|
||||
"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/eth/downloader"
|
||||
@@ -106,6 +104,7 @@ type Config struct {
|
||||
|
||||
type LesServer interface {
|
||||
Start(srvr *p2p.Server)
|
||||
Synced()
|
||||
Stop()
|
||||
Protocols() []p2p.Protocol
|
||||
}
|
||||
@@ -127,7 +126,6 @@ type Ethereum struct {
|
||||
|
||||
eventMux *event.TypeMux
|
||||
pow *ethash.Ethash
|
||||
httpclient *httpclient.HTTPClient
|
||||
accountManager *accounts.Manager
|
||||
|
||||
ApiBackend *EthApiBackend
|
||||
@@ -148,6 +146,7 @@ type Ethereum struct {
|
||||
|
||||
func (s *Ethereum) AddLesServer(ls LesServer) {
|
||||
s.lesServer = ls
|
||||
s.protocolManager.lesServer = ls
|
||||
}
|
||||
|
||||
// New creates a new Ethereum object (including the
|
||||
@@ -173,7 +172,6 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
pow: pow,
|
||||
shutdownChan: make(chan bool),
|
||||
stopDbUpgrade: stopDbUpgrade,
|
||||
httpclient: httpclient.New(config.DocRoot),
|
||||
netVersionId: config.NetworkId,
|
||||
NatSpec: config.NatSpec,
|
||||
PowTest: config.PowTest,
|
||||
@@ -218,6 +216,8 @@ func New(ctx *node.ServiceContext, config *Config) (*Ethereum, error) {
|
||||
|
||||
eth.chainConfig = config.ChainConfig
|
||||
|
||||
glog.V(logger.Info).Infoln("Chain config:", eth.chainConfig)
|
||||
|
||||
eth.blockchain, err = core.NewBlockChain(chainDb, eth.chainConfig, eth.pow, eth.EventMux())
|
||||
if err != nil {
|
||||
if err == core.ErrNoGenesis {
|
||||
@@ -354,10 +354,6 @@ func (s *Ethereum) APIs() []rpc.API {
|
||||
Version: "1.0",
|
||||
Service: s.netRPCService,
|
||||
Public: true,
|
||||
}, {
|
||||
Namespace: "admin",
|
||||
Version: "1.0",
|
||||
Service: ethreg.NewPrivateRegistarAPI(s.chainConfig, s.blockchain, s.chainDb, s.txPool, s.accountManager),
|
||||
},
|
||||
}...)
|
||||
}
|
||||
@@ -525,12 +521,6 @@ func (self *Ethereum) StopAutoDAG() {
|
||||
glog.V(logger.Info).Infof("Automatic pregeneration of ethash DAG OFF (ethash dir: %s)", ethash.DefaultDir)
|
||||
}
|
||||
|
||||
// HTTPClient returns the light http client used for fetching offchain docs
|
||||
// (natspec, source for verification)
|
||||
func (self *Ethereum) HTTPClient() *httpclient.HTTPClient {
|
||||
return self.httpclient
|
||||
}
|
||||
|
||||
// dagFiles(epoch) returns the two alternative DAG filenames (not a path)
|
||||
// 1) <revision>-<hex(seedhash[8])> 2) full-R<revision>-<hex(seedhash[8])>
|
||||
func dagFiles(epoch uint64) (string, string) {
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
@@ -28,7 +27,9 @@ import (
|
||||
"golang.org/x/net/context"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"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/event"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@@ -45,7 +46,7 @@ type filter struct {
|
||||
deadline *time.Timer // filter is inactiv when deadline triggers
|
||||
hashes []common.Hash
|
||||
crit FilterCriteria
|
||||
logs []Log
|
||||
logs []*vm.Log
|
||||
s *Subscription // associated subscription in event system
|
||||
}
|
||||
|
||||
@@ -239,11 +240,17 @@ func (api *PublicFilterAPI) Logs(ctx context.Context, crit FilterCriteria) (*rpc
|
||||
return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
|
||||
}
|
||||
|
||||
rpcSub := notifier.CreateSubscription()
|
||||
var (
|
||||
rpcSub = notifier.CreateSubscription()
|
||||
matchedLogs = make(chan []*vm.Log)
|
||||
)
|
||||
|
||||
logsSub, err := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
go func() {
|
||||
matchedLogs := make(chan []Log)
|
||||
logsSub := api.events.SubscribeLogs(crit, matchedLogs)
|
||||
|
||||
for {
|
||||
select {
|
||||
@@ -276,22 +283,24 @@ type FilterCriteria struct {
|
||||
// used to retrieve logs when the state changes. This method cannot be
|
||||
// used to fetch logs that are already stored in the state.
|
||||
//
|
||||
// Default criteria for the from and to block are "latest".
|
||||
// Using "latest" as block number will return logs for mined blocks.
|
||||
// Using "pending" as block number returns logs for not yet mined (pending) blocks.
|
||||
// In case logs are removed (chain reorg) previously returned logs are returned
|
||||
// again but with the removed property set to true.
|
||||
//
|
||||
// In case "fromBlock" > "toBlock" an error is returned.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_newfilter
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
||||
var (
|
||||
logs = make(chan []Log)
|
||||
logsSub = api.events.SubscribeLogs(crit, logs)
|
||||
)
|
||||
|
||||
if crit.FromBlock == nil {
|
||||
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
crit.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) (rpc.ID, error) {
|
||||
logs := make(chan []*vm.Log)
|
||||
logsSub, err := api.events.SubscribeLogs(crit, logs)
|
||||
if err != nil {
|
||||
return rpc.ID(""), err
|
||||
}
|
||||
|
||||
api.filtersMu.Lock()
|
||||
api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]Log, 0), s: logsSub}
|
||||
api.filters[logsSub.ID] = &filter{typ: LogsSubscription, crit: crit, deadline: time.NewTimer(deadline), logs: make([]*vm.Log, 0), s: logsSub}
|
||||
api.filtersMu.Unlock()
|
||||
|
||||
go func() {
|
||||
@@ -312,13 +321,13 @@ func (api *PublicFilterAPI) NewFilter(crit FilterCriteria) rpc.ID {
|
||||
}
|
||||
}()
|
||||
|
||||
return logsSub.ID
|
||||
return logsSub.ID, nil
|
||||
}
|
||||
|
||||
// GetLogs returns logs matching the given argument that are stored within the state.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getlogs
|
||||
func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]Log, error) {
|
||||
func (api *PublicFilterAPI) GetLogs(ctx context.Context, crit FilterCriteria) ([]*vm.Log, error) {
|
||||
if crit.FromBlock == nil {
|
||||
crit.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
@@ -357,34 +366,44 @@ func (api *PublicFilterAPI) UninstallFilter(id rpc.ID) bool {
|
||||
// If the filter could not be found an empty array of logs is returned.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterlogs
|
||||
func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]Log, error) {
|
||||
func (api *PublicFilterAPI) GetFilterLogs(ctx context.Context, id rpc.ID) ([]*vm.Log, error) {
|
||||
api.filtersMu.Lock()
|
||||
f, found := api.filters[id]
|
||||
api.filtersMu.Unlock()
|
||||
|
||||
if !found || f.typ != LogsSubscription {
|
||||
return []Log{}, nil
|
||||
return nil, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
filter := New(api.backend, api.useMipMap)
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
if f.crit.FromBlock != nil {
|
||||
filter.SetBeginBlock(f.crit.FromBlock.Int64())
|
||||
} else {
|
||||
filter.SetBeginBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
if f.crit.ToBlock != nil {
|
||||
filter.SetEndBlock(f.crit.ToBlock.Int64())
|
||||
} else {
|
||||
filter.SetEndBlock(rpc.LatestBlockNumber.Int64())
|
||||
}
|
||||
filter.SetAddresses(f.crit.Addresses)
|
||||
filter.SetTopics(f.crit.Topics)
|
||||
|
||||
logs, err := filter.Find(ctx)
|
||||
return returnLogs(logs), err
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
|
||||
// GetFilterChanges returns the logs for the filter with the given id since
|
||||
// last time is was called. This can be used for polling.
|
||||
//
|
||||
// For pending transaction and block filters the result is []common.Hash.
|
||||
// (pending)Log filters return []Log. If the filter could not be found
|
||||
// []interface{}{} is returned.
|
||||
// (pending)Log filters return []Log.
|
||||
//
|
||||
// https://github.com/ethereum/wiki/wiki/JSON-RPC#eth_getfilterchanges
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) (interface{}, error) {
|
||||
api.filtersMu.Lock()
|
||||
defer api.filtersMu.Unlock()
|
||||
|
||||
@@ -400,15 +419,15 @@ func (api *PublicFilterAPI) GetFilterChanges(id rpc.ID) interface{} {
|
||||
case PendingTransactionsSubscription, BlocksSubscription:
|
||||
hashes := f.hashes
|
||||
f.hashes = nil
|
||||
return returnHashes(hashes)
|
||||
case PendingLogsSubscription, LogsSubscription:
|
||||
return returnHashes(hashes), nil
|
||||
case LogsSubscription:
|
||||
logs := f.logs
|
||||
f.logs = nil
|
||||
return returnLogs(logs)
|
||||
return returnLogs(logs), nil
|
||||
}
|
||||
}
|
||||
|
||||
return []interface{}{}
|
||||
return []interface{}{}, fmt.Errorf("filter not found")
|
||||
}
|
||||
|
||||
// returnHashes is a helper that will return an empty hash array case the given hash array is nil,
|
||||
@@ -422,9 +441,9 @@ func returnHashes(hashes []common.Hash) []common.Hash {
|
||||
|
||||
// returnLogs is a helper that will return an empty log array in case the given logs array is nil,
|
||||
// otherwise the given logs array is returned.
|
||||
func returnLogs(logs []Log) []Log {
|
||||
func returnLogs(logs []*vm.Log) []*vm.Log {
|
||||
if logs == nil {
|
||||
return []Log{}
|
||||
return []*vm.Log{}
|
||||
}
|
||||
return logs
|
||||
}
|
||||
@@ -443,15 +462,11 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if raw.From == nil || raw.From.Int64() < 0 {
|
||||
args.FromBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.From != nil {
|
||||
args.FromBlock = big.NewInt(raw.From.Int64())
|
||||
}
|
||||
|
||||
if raw.ToBlock == nil || raw.ToBlock.Int64() < 0 {
|
||||
args.ToBlock = big.NewInt(rpc.LatestBlockNumber.Int64())
|
||||
} else {
|
||||
if raw.ToBlock != nil {
|
||||
args.ToBlock = big.NewInt(raw.ToBlock.Int64())
|
||||
}
|
||||
|
||||
@@ -459,52 +474,28 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
|
||||
if raw.Addresses != nil {
|
||||
// raw.Address can contain a single address or an array of addresses
|
||||
var addresses []common.Address
|
||||
if strAddrs, ok := raw.Addresses.([]interface{}); ok {
|
||||
for i, addr := range strAddrs {
|
||||
switch rawAddr := raw.Addresses.(type) {
|
||||
case []interface{}:
|
||||
for i, addr := range rawAddr {
|
||||
if strAddr, ok := addr.(string); ok {
|
||||
if len(strAddr) >= 2 && strAddr[0] == '0' && (strAddr[1] == 'x' || strAddr[1] == 'X') {
|
||||
strAddr = strAddr[2:]
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(strAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
addr, err := decodeAddress(strAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address at index %d: %v", i, err)
|
||||
}
|
||||
args.Addresses = append(args.Addresses, addr)
|
||||
} else {
|
||||
return fmt.Errorf("invalid address on index %d", i)
|
||||
return fmt.Errorf("non-string address at index %d", i)
|
||||
}
|
||||
}
|
||||
} else if singleAddr, ok := raw.Addresses.(string); ok {
|
||||
if len(singleAddr) >= 2 && singleAddr[0] == '0' && (singleAddr[1] == 'x' || singleAddr[1] == 'X') {
|
||||
singleAddr = singleAddr[2:]
|
||||
case string:
|
||||
addr, err := decodeAddress(rawAddr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid address: %v", err)
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(singleAddr); err == nil {
|
||||
addresses = append(addresses, common.BytesToAddress(decAddr))
|
||||
} else {
|
||||
return fmt.Errorf("invalid address given")
|
||||
}
|
||||
} else {
|
||||
return errors.New("invalid address(es) given")
|
||||
args.Addresses = []common.Address{addr}
|
||||
default:
|
||||
return errors.New("invalid addresses in query")
|
||||
}
|
||||
args.Addresses = addresses
|
||||
}
|
||||
|
||||
// helper function which parses a string to a topic hash
|
||||
topicConverter := func(raw string) (common.Hash, error) {
|
||||
if len(raw) == 0 {
|
||||
return common.Hash{}, nil
|
||||
}
|
||||
if len(raw) >= 2 && raw[0] == '0' && (raw[1] == 'x' || raw[1] == 'X') {
|
||||
raw = raw[2:]
|
||||
}
|
||||
if len(raw) != 2*common.HashLength {
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
if decAddr, err := hex.DecodeString(raw); err == nil {
|
||||
return common.BytesToHash(decAddr), nil
|
||||
}
|
||||
return common.Hash{}, errors.New("invalid topic(s)")
|
||||
}
|
||||
|
||||
// topics is an array consisting of strings and/or arrays of strings.
|
||||
@@ -512,20 +503,25 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
if len(raw.Topics) > 0 {
|
||||
args.Topics = make([][]common.Hash, len(raw.Topics))
|
||||
for i, t := range raw.Topics {
|
||||
if t == nil { // ignore topic when matching logs
|
||||
switch topic := t.(type) {
|
||||
case nil:
|
||||
// ignore topic when matching logs
|
||||
args.Topics[i] = []common.Hash{common.Hash{}}
|
||||
} else if topic, ok := t.(string); ok { // match specific topic
|
||||
top, err := topicConverter(topic)
|
||||
|
||||
case string:
|
||||
// match specific topic
|
||||
top, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
args.Topics[i] = []common.Hash{top}
|
||||
} else if topics, ok := t.([]interface{}); ok { // or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topics {
|
||||
case []interface{}:
|
||||
// or case e.g. [null, "topic0", "topic1"]
|
||||
for _, rawTopic := range topic {
|
||||
if rawTopic == nil {
|
||||
args.Topics[i] = append(args.Topics[i], common.Hash{})
|
||||
} else if topic, ok := rawTopic.(string); ok {
|
||||
parsed, err := topicConverter(topic)
|
||||
parsed, err := decodeTopic(topic)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -534,7 +530,7 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
default:
|
||||
return fmt.Errorf("invalid topic(s)")
|
||||
}
|
||||
}
|
||||
@@ -542,3 +538,19 @@ func (args *FilterCriteria) UnmarshalJSON(data []byte) error {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decodeAddress(s string) (common.Address, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.AddressLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToAddress(b), err
|
||||
}
|
||||
|
||||
func decodeTopic(s string) (common.Hash, error) {
|
||||
b, err := hexutil.Decode(s)
|
||||
if err == nil && len(b) != common.HashLength {
|
||||
err = fmt.Errorf("hex has invalid length %d after decoding", len(b))
|
||||
}
|
||||
return common.BytesToHash(b), err
|
||||
}
|
||||
|
||||
@@ -42,11 +42,11 @@ func TestUnmarshalJSONNewFilterArgs(t *testing.T) {
|
||||
if err := json.Unmarshal([]byte("{}"), &test0); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if test0.FromBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.FromBlock)
|
||||
if test0.FromBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.FromBlock)
|
||||
}
|
||||
if test0.ToBlock.Int64() != rpc.LatestBlockNumber.Int64() {
|
||||
t.Fatalf("expected %d, got %d", rpc.LatestBlockNumber, test0.ToBlock)
|
||||
if test0.ToBlock != nil {
|
||||
t.Fatalf("expected nil, got %d", test0.ToBlock)
|
||||
}
|
||||
if len(test0.Addresses) != 0 {
|
||||
t.Fatalf("expected 0 addresses, got %d", len(test0.Addresses))
|
||||
|
||||
@@ -20,9 +20,12 @@ import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"math/big"
|
||||
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/core"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
@@ -36,7 +39,7 @@ type Backend interface {
|
||||
GetReceipts(ctx context.Context, blockHash common.Hash) (types.Receipts, error)
|
||||
}
|
||||
|
||||
// Filter can be used to retrieve and filter logs
|
||||
// Filter can be used to retrieve and filter logs.
|
||||
type Filter struct {
|
||||
backend Backend
|
||||
useMipMap bool
|
||||
@@ -83,7 +86,7 @@ func (f *Filter) SetTopics(topics [][]common.Hash) {
|
||||
}
|
||||
|
||||
// Run filters logs with the current parameters set
|
||||
func (f *Filter) Find(ctx context.Context) ([]Log, error) {
|
||||
func (f *Filter) Find(ctx context.Context) ([]*vm.Log, error) {
|
||||
head, _ := f.backend.HeaderByNumber(ctx, rpc.LatestBlockNumber)
|
||||
if head == nil {
|
||||
return nil, nil
|
||||
@@ -108,7 +111,7 @@ func (f *Filter) Find(ctx context.Context) ([]Log, error) {
|
||||
return f.mipFind(beginBlockNo, endBlockNo, 0), nil
|
||||
}
|
||||
|
||||
func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) {
|
||||
func (f *Filter) mipFind(start, end uint64, depth int) (logs []*vm.Log) {
|
||||
level := core.MIPMapLevels[depth]
|
||||
// normalise numerator so we can work in level specific batches and
|
||||
// work with the proper range checks
|
||||
@@ -139,7 +142,7 @@ func (f *Filter) mipFind(start, end uint64, depth int) (logs []Log) {
|
||||
return logs
|
||||
}
|
||||
|
||||
func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, err error) {
|
||||
func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []*vm.Log, err error) {
|
||||
for i := start; i <= end; i++ {
|
||||
header, err := f.backend.HeaderByNumber(ctx, rpc.BlockNumber(i))
|
||||
if header == nil || err != nil {
|
||||
@@ -154,15 +157,11 @@ func (f *Filter) getLogs(ctx context.Context, start, end uint64) (logs []Log, er
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var unfiltered []Log
|
||||
var unfiltered []*vm.Log
|
||||
for _, receipt := range receipts {
|
||||
rl := make([]Log, len(receipt.Logs))
|
||||
for i, l := range receipt.Logs {
|
||||
rl[i] = Log{l, false}
|
||||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
unfiltered = append(unfiltered, ([]*vm.Log)(receipt.Logs)...)
|
||||
}
|
||||
logs = append(logs, filterLogs(unfiltered, f.addresses, f.topics)...)
|
||||
logs = append(logs, filterLogs(unfiltered, nil, nil, f.addresses, f.topics)...)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +178,18 @@ func includes(addresses []common.Address, a common.Address) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func filterLogs(logs []Log, addresses []common.Address, topics [][]common.Hash) []Log {
|
||||
var ret []Log
|
||||
|
||||
// Filter the logs for interesting stuff
|
||||
// filterLogs creates a slice of logs matching the given criteria.
|
||||
func filterLogs(logs []*vm.Log, fromBlock, toBlock *big.Int, addresses []common.Address, topics [][]common.Hash) []*vm.Log {
|
||||
var ret []*vm.Log
|
||||
Logs:
|
||||
for _, log := range logs {
|
||||
if fromBlock != nil && fromBlock.Int64() >= 0 && fromBlock.Uint64() > log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
if toBlock != nil && toBlock.Int64() >= 0 && toBlock.Uint64() < log.BlockNumber {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(addresses) > 0 && !includes(addresses, log.Address) {
|
||||
continue
|
||||
}
|
||||
@@ -211,7 +216,6 @@ Logs:
|
||||
continue Logs
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, log)
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
package filters
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"sync"
|
||||
@@ -43,48 +42,29 @@ const (
|
||||
UnknownSubscription Type = iota
|
||||
// LogsSubscription queries for new or removed (chain reorg) logs
|
||||
LogsSubscription
|
||||
// PendingLogsSubscription queries for logs for the pending block
|
||||
// PendingLogsSubscription queries for logs in pending blocks
|
||||
PendingLogsSubscription
|
||||
// MinedAndPendingLogsSubscription queries for logs in mined and pending blocks.
|
||||
MinedAndPendingLogsSubscription
|
||||
// PendingTransactionsSubscription queries tx hashes for pending
|
||||
// transactions entering the pending state
|
||||
PendingTransactionsSubscription
|
||||
// BlocksSubscription queries hashes for blocks that are imported
|
||||
BlocksSubscription
|
||||
// LastSubscription keeps track of the last index
|
||||
LastIndexSubscription
|
||||
)
|
||||
|
||||
var (
|
||||
ErrInvalidSubscriptionID = errors.New("invalid id")
|
||||
)
|
||||
|
||||
// Log is a helper that can hold additional information about vm.Log
|
||||
// necessary for the RPC interface.
|
||||
type Log struct {
|
||||
*vm.Log
|
||||
Removed bool `json:"removed"`
|
||||
}
|
||||
|
||||
func (l *Log) MarshalJSON() ([]byte, error) {
|
||||
fields := map[string]interface{}{
|
||||
"address": l.Address,
|
||||
"data": fmt.Sprintf("0x%x", l.Data),
|
||||
"blockNumber": fmt.Sprintf("%#x", l.BlockNumber),
|
||||
"logIndex": fmt.Sprintf("%#x", l.Index),
|
||||
"blockHash": l.BlockHash,
|
||||
"transactionHash": l.TxHash,
|
||||
"transactionIndex": fmt.Sprintf("%#x", l.TxIndex),
|
||||
"topics": l.Topics,
|
||||
"removed": l.Removed,
|
||||
}
|
||||
|
||||
return json.Marshal(fields)
|
||||
}
|
||||
|
||||
type subscription struct {
|
||||
id rpc.ID
|
||||
typ Type
|
||||
created time.Time
|
||||
logsCrit FilterCriteria
|
||||
logs chan []Log
|
||||
logs chan []*vm.Log
|
||||
hashes chan common.Hash
|
||||
headers chan *types.Header
|
||||
installed chan struct{} // closed when the filter is installed
|
||||
@@ -169,8 +149,65 @@ func (es *EventSystem) subscribe(sub *subscription) *Subscription {
|
||||
}
|
||||
|
||||
// SubscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel. Default value for the from and to
|
||||
// block is "latest". If the fromBlock > toBlock an error is returned.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []*vm.Log) (*Subscription, error) {
|
||||
var from, to rpc.BlockNumber
|
||||
if crit.FromBlock == nil {
|
||||
from = rpc.LatestBlockNumber
|
||||
} else {
|
||||
from = rpc.BlockNumber(crit.FromBlock.Int64())
|
||||
}
|
||||
if crit.ToBlock == nil {
|
||||
to = rpc.LatestBlockNumber
|
||||
} else {
|
||||
to = rpc.BlockNumber(crit.ToBlock.Int64())
|
||||
}
|
||||
|
||||
// only interested in pending logs
|
||||
if from == rpc.PendingBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribePendingLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in new mined logs
|
||||
if from == rpc.LatestBlockNumber && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// only interested in mined logs within a specific block range
|
||||
if from >= 0 && to >= 0 && to >= from {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
// interested in mined logs from a specific block number, new logs and pending logs
|
||||
if from >= rpc.LatestBlockNumber && to == rpc.PendingBlockNumber {
|
||||
return es.subscribeMinedPendingLogs(crit, logs), nil
|
||||
}
|
||||
// interested in logs from a specific block number to new mined blocks
|
||||
if from >= 0 && to == rpc.LatestBlockNumber {
|
||||
return es.subscribeLogs(crit, logs), nil
|
||||
}
|
||||
return nil, fmt.Errorf("invalid from and to block combination: from > to")
|
||||
}
|
||||
|
||||
// subscribeMinedPendingLogs creates a subscription that returned mined and
|
||||
// pending logs that match the given criteria.
|
||||
func (es *EventSystem) subscribeMinedPendingLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: MinedAndPendingLogsSubscription,
|
||||
logsCrit: crit,
|
||||
created: time.Now(),
|
||||
logs: logs,
|
||||
hashes: make(chan common.Hash),
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// subscribeLogs creates a subscription that will write all logs matching the
|
||||
// given criteria to the given logs channel.
|
||||
func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
func (es *EventSystem) subscribeLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: LogsSubscription,
|
||||
@@ -186,9 +223,9 @@ func (es *EventSystem) SubscribeLogs(crit FilterCriteria, logs chan []Log) *Subs
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingLogs creates a subscription that will write pending logs matching the
|
||||
// given criteria to the given channel.
|
||||
func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log) *Subscription {
|
||||
// subscribePendingLogs creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) subscribePendingLogs(crit FilterCriteria, logs chan []*vm.Log) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingLogsSubscription,
|
||||
@@ -204,23 +241,6 @@ func (es *EventSystem) SubscribePendingLogs(crit FilterCriteria, logs chan []Log
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a sbuscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribeNewHeads creates a subscription that writes the header of a block that is
|
||||
// imported in the chain.
|
||||
func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscription {
|
||||
@@ -228,7 +248,7 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
||||
id: rpc.NewID(),
|
||||
typ: BlocksSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []Log),
|
||||
logs: make(chan []*vm.Log),
|
||||
hashes: make(chan common.Hash),
|
||||
headers: headers,
|
||||
installed: make(chan struct{}),
|
||||
@@ -238,6 +258,23 @@ func (es *EventSystem) SubscribeNewHeads(headers chan *types.Header) *Subscripti
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
// SubscribePendingTxEvents creates a subscription that writes transaction hashes for
|
||||
// transactions that enter the transaction pool.
|
||||
func (es *EventSystem) SubscribePendingTxEvents(hashes chan common.Hash) *Subscription {
|
||||
sub := &subscription{
|
||||
id: rpc.NewID(),
|
||||
typ: PendingTransactionsSubscription,
|
||||
created: time.Now(),
|
||||
logs: make(chan []*vm.Log),
|
||||
hashes: hashes,
|
||||
headers: make(chan *types.Header),
|
||||
installed: make(chan struct{}),
|
||||
err: make(chan error),
|
||||
}
|
||||
|
||||
return es.subscribe(sub)
|
||||
}
|
||||
|
||||
type filterIndex map[Type]map[rpc.ID]*subscription
|
||||
|
||||
// broadcast event to filters that match criteria.
|
||||
@@ -251,7 +288,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
if len(e) > 0 {
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(e, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -260,7 +297,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
case core.RemovedLogsEvent:
|
||||
for _, f := range filters[LogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, true), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(e.Logs, f.logsCrit.FromBlock, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -268,7 +305,7 @@ func (es *EventSystem) broadcast(filters filterIndex, ev *event.Event) {
|
||||
case core.PendingLogsEvent:
|
||||
for _, f := range filters[PendingLogsSubscription] {
|
||||
if ev.Time.After(f.created) {
|
||||
if matchedLogs := filterLogs(convertLogs(e.Logs, false), f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
if matchedLogs := filterLogs(e.Logs, nil, f.logsCrit.ToBlock, f.logsCrit.Addresses, f.logsCrit.Topics); len(matchedLogs) > 0 {
|
||||
f.logs <- matchedLogs
|
||||
}
|
||||
}
|
||||
@@ -333,26 +370,23 @@ func (es *EventSystem) lightFilterNewHead(newHeader *types.Header, callBack func
|
||||
}
|
||||
|
||||
// filter logs of a single header in light client mode
|
||||
func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []Log {
|
||||
//fmt.Println("lightFilterLogs", header.Number.Uint64(), remove)
|
||||
func (es *EventSystem) lightFilterLogs(header *types.Header, addresses []common.Address, topics [][]common.Hash, remove bool) []*vm.Log {
|
||||
if bloomFilter(header.Bloom, addresses, topics) {
|
||||
//fmt.Println("bloom match")
|
||||
// Get the logs of the block
|
||||
ctx, _ := context.WithTimeout(context.Background(), time.Second*5)
|
||||
receipts, err := es.backend.GetReceipts(ctx, header.Hash())
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
var unfiltered []Log
|
||||
var unfiltered []*vm.Log
|
||||
for _, receipt := range receipts {
|
||||
rl := make([]Log, len(receipt.Logs))
|
||||
for i, l := range receipt.Logs {
|
||||
rl[i] = Log{l, remove}
|
||||
for _, log := range receipt.Logs {
|
||||
logcopy := *log
|
||||
logcopy.Removed = remove
|
||||
unfiltered = append(unfiltered, &logcopy)
|
||||
}
|
||||
unfiltered = append(unfiltered, rl...)
|
||||
}
|
||||
logs := filterLogs(unfiltered, addresses, topics)
|
||||
//fmt.Println("found", len(logs))
|
||||
logs := filterLogs(unfiltered, nil, nil, addresses, topics)
|
||||
return logs
|
||||
}
|
||||
return nil
|
||||
@@ -364,6 +398,11 @@ func (es *EventSystem) eventLoop() {
|
||||
index = make(filterIndex)
|
||||
sub = es.mux.Subscribe(core.PendingLogsEvent{}, core.RemovedLogsEvent{}, vm.Logs{}, core.TxPreEvent{}, core.ChainEvent{})
|
||||
)
|
||||
|
||||
for i := UnknownSubscription; i < LastIndexSubscription; i++ {
|
||||
index[i] = make(map[rpc.ID]*subscription)
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
case ev, active := <-sub.Chan():
|
||||
@@ -372,23 +411,23 @@ func (es *EventSystem) eventLoop() {
|
||||
}
|
||||
es.broadcast(index, ev)
|
||||
case f := <-es.install:
|
||||
if _, found := index[f.typ]; !found {
|
||||
index[f.typ] = make(map[rpc.ID]*subscription)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
index[LogsSubscription][f.id] = f
|
||||
index[PendingLogsSubscription][f.id] = f
|
||||
} else {
|
||||
index[f.typ][f.id] = f
|
||||
}
|
||||
index[f.typ][f.id] = f
|
||||
close(f.installed)
|
||||
case f := <-es.uninstall:
|
||||
delete(index[f.typ], f.id)
|
||||
if f.typ == MinedAndPendingLogsSubscription {
|
||||
// the type are logs and pending logs subscriptions
|
||||
delete(index[LogsSubscription], f.id)
|
||||
delete(index[PendingLogsSubscription], f.id)
|
||||
} else {
|
||||
delete(index[f.typ], f.id)
|
||||
}
|
||||
close(f.err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// convertLogs is a helper utility that converts vm.Logs to []filter.Log.
|
||||
func convertLogs(in vm.Logs, removed bool) []Log {
|
||||
logs := make([]Log, len(in))
|
||||
for i, l := range in {
|
||||
logs[i] = Log{l, removed}
|
||||
}
|
||||
return logs
|
||||
}
|
||||
|
||||
@@ -34,13 +34,6 @@ import (
|
||||
"github.com/ethereum/go-ethereum/rpc"
|
||||
)
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
type testBackend struct {
|
||||
mux *event.TypeMux
|
||||
db ethdb.Database
|
||||
@@ -81,6 +74,11 @@ func TestBlockSubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
genesis = core.WriteGenesisBlockForTesting(db)
|
||||
chain, _ = core.GenerateChain(params.TestChainConfig, genesis, db, 10, func(i int, gen *core.BlockGen) {})
|
||||
chainEvents = []core.ChainEvent{}
|
||||
@@ -130,6 +128,11 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
transactions = []*types.Transaction{
|
||||
types.NewTransaction(0, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
types.NewTransaction(1, common.HexToAddress("0xb794f5ea0ba39494ce83a213fffba74279579268"), new(big.Int), new(big.Int), new(big.Int), nil),
|
||||
@@ -150,9 +153,13 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
}
|
||||
|
||||
for {
|
||||
h := api.GetFilterChanges(fid0).([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
results, err := api.GetFilterChanges(fid0)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to retrieve logs: %v", err)
|
||||
}
|
||||
|
||||
h := results.([]common.Hash)
|
||||
hashes = append(hashes, h...)
|
||||
if len(hashes) >= len(transactions) {
|
||||
break
|
||||
}
|
||||
@@ -167,11 +174,86 @@ func TestPendingTxFilter(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilterCreation test whether a given filter criteria makes sense.
|
||||
// If not it must return an error.
|
||||
func TestLogFilterCreation(t *testing.T) {
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
success bool
|
||||
}{
|
||||
// defaults
|
||||
{FilterCriteria{}, true},
|
||||
// valid block number range
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, true},
|
||||
// "mined" block range to pending
|
||||
{FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, true},
|
||||
// new mined and pending blocks
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, true},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(1)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)}, false},
|
||||
// from block "higher" than to block
|
||||
{FilterCriteria{FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, false},
|
||||
}
|
||||
)
|
||||
|
||||
for i, test := range testCases {
|
||||
_, err := api.NewFilter(test.crit)
|
||||
if test.success && err != nil {
|
||||
t.Errorf("expected filter creation for case %d to success, got %v", i, err)
|
||||
}
|
||||
if !test.success && err == nil {
|
||||
t.Errorf("expected testcase %d to fail with an error", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestInvalidLogFilterCreation tests whether invalid filter log criteria results in an error
|
||||
// when the filter is created.
|
||||
func TestInvalidLogFilterCreation(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
)
|
||||
|
||||
// different situations where log filter creation should fail.
|
||||
// Reason: fromBlock > toBlock
|
||||
testCases := []FilterCriteria{
|
||||
0: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
|
||||
1: {FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
2: {FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(100)},
|
||||
}
|
||||
|
||||
for i, test := range testCases {
|
||||
if _, err := api.NewFilter(test); err == nil {
|
||||
t.Errorf("Expected NewFilter for case #%d to fail", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestLogFilter tests whether log filters match the correct logs that are posted to the event mux.
|
||||
func TestLogFilter(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
@@ -180,8 +262,8 @@ func TestLogFilter(t *testing.T) {
|
||||
secondTopic = common.HexToHash("0x2222222222222222222222222222222222222222222222222222222222222222")
|
||||
notUsedTopic = common.HexToHash("0x9999999999999999999999999999999999999999999999999999999999999999")
|
||||
|
||||
// posted twice, once as vm.Logs and once as core.PendingLogsEvent
|
||||
allLogs = vm.Logs{
|
||||
// Note, these are used for comparison of the test cases.
|
||||
vm.NewLog(firstAddr, []common.Hash{}, []byte(""), 0),
|
||||
vm.NewLog(firstAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
vm.NewLog(secondAddr, []common.Hash{firstTopic}, []byte(""), 1),
|
||||
@@ -189,45 +271,64 @@ func TestLogFilter(t *testing.T) {
|
||||
vm.NewLog(thirdAddress, []common.Hash{secondTopic}, []byte(""), 3),
|
||||
}
|
||||
|
||||
expectedCase7 = vm.Logs{allLogs[3], allLogs[4], allLogs[0], allLogs[1], allLogs[2], allLogs[3], allLogs[4]}
|
||||
expectedCase11 = vm.Logs{allLogs[1], allLogs[2], allLogs[1], allLogs[2]}
|
||||
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
expected vm.Logs
|
||||
id rpc.ID
|
||||
}{
|
||||
// match all
|
||||
{FilterCriteria{}, allLogs, ""},
|
||||
0: {FilterCriteria{}, allLogs, ""},
|
||||
// match none due to no matching addresses
|
||||
{FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
1: {FilterCriteria{Addresses: []common.Address{common.Address{}, notUsedAddress}, Topics: [][]common.Hash{allLogs[0].Topics}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses, ignore topics
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
2: {FilterCriteria{Addresses: []common.Address{firstAddr}}, allLogs[:2], ""},
|
||||
// match none due to no matching topics (match with address)
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
3: {FilterCriteria{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{[]common.Hash{notUsedTopic}}}, vm.Logs{}, ""},
|
||||
// match logs based on addresses and topics
|
||||
{FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
4: {FilterCriteria{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[3:5], ""},
|
||||
// match logs based on multiple addresses and "or" topics
|
||||
{FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// block numbers are ignored for filters created with New***Filter, these return all logs that match the given criterias when the state changes
|
||||
{FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)}, allLogs[:2], ""},
|
||||
5: {FilterCriteria{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{[]common.Hash{firstTopic, secondTopic}}}, allLogs[2:5], ""},
|
||||
// logs in the pending block
|
||||
6: {FilterCriteria{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, allLogs[:2], ""},
|
||||
// mined logs with block num >= 2 or pending logs
|
||||
7: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())}, expectedCase7, ""},
|
||||
// all "mined" logs with block num >= 2
|
||||
8: {FilterCriteria{FromBlock: big.NewInt(2), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs[3:], ""},
|
||||
// all "mined" logs
|
||||
9: {FilterCriteria{ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())}, allLogs, ""},
|
||||
// all "mined" logs with 1>= block num <=2 and topic secondTopic
|
||||
10: {FilterCriteria{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2), Topics: [][]common.Hash{[]common.Hash{secondTopic}}}, allLogs[3:4], ""},
|
||||
// all "mined" and pending logs with topic firstTopic
|
||||
11: {FilterCriteria{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64()), Topics: [][]common.Hash{[]common.Hash{firstTopic}}}, expectedCase11, ""},
|
||||
}
|
||||
|
||||
err error
|
||||
)
|
||||
|
||||
// create all filters
|
||||
for i := range testCases {
|
||||
testCases[i].id = api.NewFilter(testCases[i].crit)
|
||||
testCases[i].id, _ = api.NewFilter(testCases[i].crit)
|
||||
}
|
||||
|
||||
// raise events
|
||||
time.Sleep(1 * time.Second)
|
||||
if err = mux.Post(allLogs); err != nil {
|
||||
if err := mux.Post(allLogs); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := mux.Post(core.PendingLogsEvent{Logs: allLogs}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i, tt := range testCases {
|
||||
var fetched []Log
|
||||
var fetched []*vm.Log
|
||||
for { // fetch all expected logs
|
||||
fetched = append(fetched, api.GetFilterChanges(tt.id).([]Log)...)
|
||||
results, err := api.GetFilterChanges(tt.id)
|
||||
if err != nil {
|
||||
t.Fatalf("Unable to fetch logs: %v", err)
|
||||
}
|
||||
|
||||
fetched = append(fetched, results.([]*vm.Log)...)
|
||||
if len(fetched) >= len(tt.expected) {
|
||||
break
|
||||
}
|
||||
@@ -244,10 +345,9 @@ func TestLogFilter(t *testing.T) {
|
||||
if fetched[l].Removed {
|
||||
t.Errorf("expected log not to be removed for log %d in case %d", l, i)
|
||||
}
|
||||
if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
|
||||
if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
|
||||
t.Errorf("invalid log on index %d for case %d", l, i)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,6 +357,11 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
var (
|
||||
mux = new(event.TypeMux)
|
||||
db, _ = ethdb.NewMemDatabase()
|
||||
backend = &testBackend{mux, db}
|
||||
api = NewPublicFilterAPI(backend, false)
|
||||
|
||||
firstAddr = common.HexToAddress("0x1111111111111111111111111111111111111111")
|
||||
secondAddr = common.HexToAddress("0x2222222222222222222222222222222222222222")
|
||||
thirdAddress = common.HexToAddress("0x3333333333333333333333333333333333333333")
|
||||
@@ -292,7 +397,7 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
testCases = []struct {
|
||||
crit FilterCriteria
|
||||
expected vm.Logs
|
||||
c chan []Log
|
||||
c chan []*vm.Log
|
||||
sub *Subscription
|
||||
}{
|
||||
// match all
|
||||
@@ -318,15 +423,15 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
// on slow machines this could otherwise lead to missing events when the subscription is created after
|
||||
// (some) events are posted.
|
||||
for i := range testCases {
|
||||
testCases[i].c = make(chan []Log)
|
||||
testCases[i].sub = api.events.SubscribePendingLogs(testCases[i].crit, testCases[i].c)
|
||||
testCases[i].c = make(chan []*vm.Log)
|
||||
testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
|
||||
}
|
||||
|
||||
for n, test := range testCases {
|
||||
i := n
|
||||
tt := test
|
||||
go func() {
|
||||
var fetched []Log
|
||||
var fetched []*vm.Log
|
||||
fetchLoop:
|
||||
for {
|
||||
logs := <-tt.c
|
||||
@@ -344,7 +449,7 @@ func TestPendingLogsSubscription(t *testing.T) {
|
||||
if fetched[l].Removed {
|
||||
t.Errorf("expected log not to be removed for log %d in case %d", l, i)
|
||||
}
|
||||
if !reflect.DeepEqual(fetched[l].Log, tt.expected[l]) {
|
||||
if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
|
||||
t.Errorf("invalid log on index %d for case %d", l, i)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ import (
|
||||
"github.com/ethereum/go-ethereum/crypto"
|
||||
"github.com/ethereum/go-ethereum/ethdb"
|
||||
"github.com/ethereum/go-ethereum/params"
|
||||
"github.com/ethereum/go-ethereum/event"
|
||||
)
|
||||
|
||||
func makeReceipt(addr common.Address) *types.Receipt {
|
||||
@@ -51,6 +52,7 @@ func BenchmarkMipmaps(b *testing.B) {
|
||||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr1 = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
@@ -126,6 +128,7 @@ func TestFilters(t *testing.T) {
|
||||
|
||||
var (
|
||||
db, _ = ethdb.NewLDBDatabase(dir, 0, 0)
|
||||
mux = new(event.TypeMux)
|
||||
backend = &testBackend{mux, db}
|
||||
key1, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
|
||||
addr = crypto.PubkeyToAddress(key1.PublicKey)
|
||||
|
||||
@@ -87,6 +87,8 @@ type ProtocolManager struct {
|
||||
quitSync chan struct{}
|
||||
noMorePeers chan struct{}
|
||||
|
||||
lesServer LesServer
|
||||
|
||||
// wait group is used for graceful shutdowns during downloading
|
||||
// and processing
|
||||
wg sync.WaitGroup
|
||||
@@ -171,7 +173,7 @@ func NewProtocolManager(config *params.ChainConfig, fastSync bool, networkId int
|
||||
return blockchain.CurrentBlock().NumberU64()
|
||||
}
|
||||
inserter := func(blocks types.Blocks) (int, error) {
|
||||
atomic.StoreUint32(&manager.synced, 1) // Mark initial sync done on any fetcher import
|
||||
manager.setSynced() // Mark initial sync done on any fetcher import
|
||||
return manager.insertChain(blocks)
|
||||
}
|
||||
manager.fetcher = fetcher.New(blockchain.GetBlockByHash, validator, manager.BroadcastBlock, heighter, inserter, manager.removePeer)
|
||||
|
||||
@@ -93,7 +93,7 @@ type testTxPool struct {
|
||||
|
||||
// AddBatch appends a batch of transactions to the pool, and notifies any
|
||||
// listeners if the addition channel is non nil
|
||||
func (p *testTxPool) AddBatch(txs []*types.Transaction) {
|
||||
func (p *testTxPool) AddBatch(txs []*types.Transaction) error {
|
||||
p.lock.Lock()
|
||||
defer p.lock.Unlock()
|
||||
|
||||
@@ -101,10 +101,12 @@ func (p *testTxPool) AddBatch(txs []*types.Transaction) {
|
||||
if p.added != nil {
|
||||
p.added <- txs
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pending returns all the transactions known to the pool
|
||||
func (p *testTxPool) Pending() map[common.Address]types.Transactions {
|
||||
func (p *testTxPool) Pending() (map[common.Address]types.Transactions, error) {
|
||||
p.lock.RLock()
|
||||
defer p.lock.RUnlock()
|
||||
|
||||
@@ -116,7 +118,7 @@ func (p *testTxPool) Pending() map[common.Address]types.Transactions {
|
||||
for _, batch := range batches {
|
||||
sort.Sort(types.TxByNonce(batch))
|
||||
}
|
||||
return batches
|
||||
return batches, nil
|
||||
}
|
||||
|
||||
// newTestTransaction create a new dummy transaction.
|
||||
|
||||
@@ -98,11 +98,11 @@ var errorToString = map[int]string{
|
||||
|
||||
type txPool interface {
|
||||
// AddBatch should add the given transactions to the pool.
|
||||
AddBatch([]*types.Transaction)
|
||||
AddBatch([]*types.Transaction) error
|
||||
|
||||
// Pending should return pending transactions.
|
||||
// The slice should be modifiable by the caller.
|
||||
Pending() map[common.Address]types.Transactions
|
||||
Pending() (map[common.Address]types.Transactions, error)
|
||||
}
|
||||
|
||||
// statusData is the network packet for the status message.
|
||||
|
||||
12
eth/sync.go
12
eth/sync.go
@@ -46,7 +46,8 @@ type txsync struct {
|
||||
// syncTransactions starts sending all currently pending transactions to the given peer.
|
||||
func (pm *ProtocolManager) syncTransactions(p *peer) {
|
||||
var txs types.Transactions
|
||||
for _, batch := range pm.txpool.Pending() {
|
||||
pending, _ := pm.txpool.Pending()
|
||||
for _, batch := range pending {
|
||||
txs = append(txs, batch...)
|
||||
}
|
||||
if len(txs) == 0 {
|
||||
@@ -180,7 +181,7 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||
if err := pm.downloader.Synchronise(peer.id, pHead, pTd, mode); err != nil {
|
||||
return
|
||||
}
|
||||
atomic.StoreUint32(&pm.synced, 1) // Mark initial sync done
|
||||
pm.setSynced() // Mark initial sync done
|
||||
|
||||
// If fast sync was enabled, and we synced up, disable it
|
||||
if atomic.LoadUint32(&pm.fastSync) == 1 {
|
||||
@@ -191,3 +192,10 @@ func (pm *ProtocolManager) synchronise(peer *peer) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// setSynced sets the synced flag and notifies the light server if present
|
||||
func (pm *ProtocolManager) setSynced() {
|
||||
if atomic.SwapUint32(&pm.synced, 1) == 0 && pm.lesServer != nil {
|
||||
pm.lesServer.Synced()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
|
||||
"github.com/ethereum/go-ethereum"
|
||||
"github.com/ethereum/go-ethereum/common"
|
||||
"github.com/ethereum/go-ethereum/common/hexutil"
|
||||
"github.com/ethereum/go-ethereum/core/types"
|
||||
"github.com/ethereum/go-ethereum/core/vm"
|
||||
"github.com/ethereum/go-ethereum/rlp"
|
||||
@@ -80,6 +81,8 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||
err := ec.c.CallContext(ctx, &raw, method, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
} else if len(raw) == 0 {
|
||||
return nil, ethereum.NotFound
|
||||
}
|
||||
// Decode header and transactions.
|
||||
var head *types.Header
|
||||
@@ -111,7 +114,7 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||
for i := range reqs {
|
||||
reqs[i] = rpc.BatchElem{
|
||||
Method: "eth_getUncleByBlockHashAndIndex",
|
||||
Args: []interface{}{body.Hash, fmt.Sprintf("%#x", i)},
|
||||
Args: []interface{}{body.Hash, hexutil.EncodeUint64(uint64(i))},
|
||||
Result: &uncles[i],
|
||||
}
|
||||
}
|
||||
@@ -122,6 +125,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||
if reqs[i].Error != nil {
|
||||
return nil, reqs[i].Error
|
||||
}
|
||||
if uncles[i] == nil {
|
||||
return nil, fmt.Errorf("got null header for uncle %d of block %x", i, body.Hash[:])
|
||||
}
|
||||
}
|
||||
}
|
||||
return types.NewBlockWithHeader(head).WithBody(body.Transactions, uncles), nil
|
||||
@@ -131,6 +137,9 @@ func (ec *Client) getBlock(ctx context.Context, method string, args ...interface
|
||||
func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.Header, error) {
|
||||
var head *types.Header
|
||||
err := ec.c.CallContext(ctx, &head, "eth_getBlockByHash", hash, false)
|
||||
if err == nil && head == nil {
|
||||
err = ethereum.NotFound
|
||||
}
|
||||
return head, err
|
||||
}
|
||||
|
||||
@@ -139,26 +148,38 @@ func (ec *Client) HeaderByHash(ctx context.Context, hash common.Hash) (*types.He
|
||||
func (ec *Client) HeaderByNumber(ctx context.Context, number *big.Int) (*types.Header, error) {
|
||||
var head *types.Header
|
||||
err := ec.c.CallContext(ctx, &head, "eth_getBlockByNumber", toBlockNumArg(number), false)
|
||||
if err == nil && head == nil {
|
||||
err = ethereum.NotFound
|
||||
}
|
||||
return head, err
|
||||
}
|
||||
|
||||
// TransactionByHash returns the transaction with the given hash.
|
||||
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (*types.Transaction, error) {
|
||||
var tx *types.Transaction
|
||||
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByHash", hash)
|
||||
if err == nil {
|
||||
if _, r, _ := tx.RawSignatureValues(); r == nil {
|
||||
return nil, fmt.Errorf("server returned transaction without signature")
|
||||
}
|
||||
func (ec *Client) TransactionByHash(ctx context.Context, hash common.Hash) (tx *types.Transaction, isPending bool, err error) {
|
||||
var raw json.RawMessage
|
||||
err = ec.c.CallContext(ctx, &raw, "eth_getTransactionByHash", hash)
|
||||
if err != nil {
|
||||
return nil, false, err
|
||||
} else if len(raw) == 0 {
|
||||
return nil, false, ethereum.NotFound
|
||||
}
|
||||
return tx, err
|
||||
if err := json.Unmarshal(raw, tx); err != nil {
|
||||
return nil, false, err
|
||||
} else if _, r, _ := tx.RawSignatureValues(); r == nil {
|
||||
return nil, false, fmt.Errorf("server returned transaction without signature")
|
||||
}
|
||||
var block struct{ BlockHash *common.Hash }
|
||||
if err := json.Unmarshal(raw, &block); err != nil {
|
||||
return nil, false, err
|
||||
}
|
||||
return tx, block.BlockHash == nil, nil
|
||||
}
|
||||
|
||||
// TransactionCount returns the total number of transactions in the given block.
|
||||
func (ec *Client) TransactionCount(ctx context.Context, blockHash common.Hash) (uint, error) {
|
||||
var num rpc.HexNumber
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByHash", blockHash)
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TransactionInBlock returns a single transaction at index in the given block.
|
||||
@@ -166,11 +187,9 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
|
||||
var tx *types.Transaction
|
||||
err := ec.c.CallContext(ctx, &tx, "eth_getTransactionByBlockHashAndIndex", blockHash, index)
|
||||
if err == nil {
|
||||
var signer types.Signer = types.HomesteadSigner{}
|
||||
if tx.Protected() {
|
||||
signer = types.NewEIP155Signer(tx.ChainId())
|
||||
}
|
||||
if _, r, _ := types.SignatureValues(signer, tx); r == nil {
|
||||
if tx == nil {
|
||||
return nil, ethereum.NotFound
|
||||
} else if _, r, _ := tx.RawSignatureValues(); r == nil {
|
||||
return nil, fmt.Errorf("server returned transaction without signature")
|
||||
}
|
||||
}
|
||||
@@ -182,8 +201,12 @@ func (ec *Client) TransactionInBlock(ctx context.Context, blockHash common.Hash,
|
||||
func (ec *Client) TransactionReceipt(ctx context.Context, txHash common.Hash) (*types.Receipt, error) {
|
||||
var r *types.Receipt
|
||||
err := ec.c.CallContext(ctx, &r, "eth_getTransactionReceipt", txHash)
|
||||
if err == nil && r != nil && len(r.PostState) == 0 {
|
||||
return nil, fmt.Errorf("server returned receipt without post state")
|
||||
if err == nil {
|
||||
if r == nil {
|
||||
return nil, ethereum.NotFound
|
||||
} else if len(r.PostState) == 0 {
|
||||
return nil, fmt.Errorf("server returned receipt without post state")
|
||||
}
|
||||
}
|
||||
return r, err
|
||||
}
|
||||
@@ -192,15 +215,15 @@ func toBlockNumArg(number *big.Int) string {
|
||||
if number == nil {
|
||||
return "latest"
|
||||
}
|
||||
return fmt.Sprintf("%#x", number)
|
||||
return hexutil.EncodeBig(number)
|
||||
}
|
||||
|
||||
type rpcProgress struct {
|
||||
StartingBlock rpc.HexNumber
|
||||
CurrentBlock rpc.HexNumber
|
||||
HighestBlock rpc.HexNumber
|
||||
PulledStates rpc.HexNumber
|
||||
KnownStates rpc.HexNumber
|
||||
StartingBlock hexutil.Uint64
|
||||
CurrentBlock hexutil.Uint64
|
||||
HighestBlock hexutil.Uint64
|
||||
PulledStates hexutil.Uint64
|
||||
KnownStates hexutil.Uint64
|
||||
}
|
||||
|
||||
// SyncProgress retrieves the current progress of the sync algorithm. If there's
|
||||
@@ -220,11 +243,11 @@ func (ec *Client) SyncProgress(ctx context.Context) (*ethereum.SyncProgress, err
|
||||
return nil, err
|
||||
}
|
||||
return ðereum.SyncProgress{
|
||||
StartingBlock: progress.StartingBlock.Uint64(),
|
||||
CurrentBlock: progress.CurrentBlock.Uint64(),
|
||||
HighestBlock: progress.HighestBlock.Uint64(),
|
||||
PulledStates: progress.PulledStates.Uint64(),
|
||||
KnownStates: progress.KnownStates.Uint64(),
|
||||
StartingBlock: uint64(progress.StartingBlock),
|
||||
CurrentBlock: uint64(progress.CurrentBlock),
|
||||
HighestBlock: uint64(progress.HighestBlock),
|
||||
PulledStates: uint64(progress.PulledStates),
|
||||
KnownStates: uint64(progress.KnownStates),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -239,7 +262,7 @@ func (ec *Client) SubscribeNewHead(ctx context.Context, ch chan<- *types.Header)
|
||||
// BalanceAt returns the wei balance of the given account.
|
||||
// The block number can be nil, in which case the balance is taken from the latest known block.
|
||||
func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (*big.Int, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, toBlockNumArg(blockNumber))
|
||||
return (*big.Int)(&result), err
|
||||
}
|
||||
@@ -247,7 +270,7 @@ func (ec *Client) BalanceAt(ctx context.Context, account common.Address, blockNu
|
||||
// StorageAt returns the value of key in the contract storage of the given account.
|
||||
// The block number can be nil, in which case the value is taken from the latest known block.
|
||||
func (ec *Client) StorageAt(ctx context.Context, account common.Address, key common.Hash, blockNumber *big.Int) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
@@ -255,7 +278,7 @@ func (ec *Client) StorageAt(ctx context.Context, account common.Address, key com
|
||||
// CodeAt returns the contract code of the given account.
|
||||
// The block number can be nil, in which case the code is taken from the latest known block.
|
||||
func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumber *big.Int) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, toBlockNumArg(blockNumber))
|
||||
return result, err
|
||||
}
|
||||
@@ -263,9 +286,9 @@ func (ec *Client) CodeAt(ctx context.Context, account common.Address, blockNumbe
|
||||
// NonceAt returns the account nonce of the given account.
|
||||
// The block number can be nil, in which case the nonce is taken from the latest known block.
|
||||
func (ec *Client) NonceAt(ctx context.Context, account common.Address, blockNumber *big.Int) (uint64, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, toBlockNumArg(blockNumber))
|
||||
return result.Uint64(), err
|
||||
return uint64(result), err
|
||||
}
|
||||
|
||||
// Filters
|
||||
@@ -286,7 +309,7 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
||||
arg := map[string]interface{}{
|
||||
"fromBlock": toBlockNumArg(q.FromBlock),
|
||||
"toBlock": toBlockNumArg(q.ToBlock),
|
||||
"addresses": q.Addresses,
|
||||
"address": q.Addresses,
|
||||
"topics": q.Topics,
|
||||
}
|
||||
if q.FromBlock == nil {
|
||||
@@ -299,21 +322,21 @@ func toFilterArg(q ethereum.FilterQuery) interface{} {
|
||||
|
||||
// PendingBalanceAt returns the wei balance of the given account in the pending state.
|
||||
func (ec *Client) PendingBalanceAt(ctx context.Context, account common.Address) (*big.Int, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getBalance", account, "pending")
|
||||
return (*big.Int)(&result), err
|
||||
}
|
||||
|
||||
// PendingStorageAt returns the value of key in the contract storage of the given account in the pending state.
|
||||
func (ec *Client) PendingStorageAt(ctx context.Context, account common.Address, key common.Hash) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getStorageAt", account, key, "pending")
|
||||
return result, err
|
||||
}
|
||||
|
||||
// PendingCodeAt returns the contract code of the given account in the pending state.
|
||||
func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]byte, error) {
|
||||
var result rpc.HexBytes
|
||||
var result hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getCode", account, "pending")
|
||||
return result, err
|
||||
}
|
||||
@@ -321,16 +344,16 @@ func (ec *Client) PendingCodeAt(ctx context.Context, account common.Address) ([]
|
||||
// PendingNonceAt returns the account nonce of the given account in the pending state.
|
||||
// This is the nonce that should be used for the next transaction.
|
||||
func (ec *Client) PendingNonceAt(ctx context.Context, account common.Address) (uint64, error) {
|
||||
var result rpc.HexNumber
|
||||
var result hexutil.Uint64
|
||||
err := ec.c.CallContext(ctx, &result, "eth_getTransactionCount", account, "pending")
|
||||
return result.Uint64(), err
|
||||
return uint64(result), err
|
||||
}
|
||||
|
||||
// PendingTransactionCount returns the total number of transactions in the pending state.
|
||||
func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
var num rpc.HexNumber
|
||||
var num hexutil.Uint
|
||||
err := ec.c.CallContext(ctx, &num, "eth_getBlockTransactionCountByNumber", "pending")
|
||||
return num.Uint(), err
|
||||
return uint(num), err
|
||||
}
|
||||
|
||||
// TODO: SubscribePendingTransactions (needs server side)
|
||||
@@ -344,29 +367,29 @@ func (ec *Client) PendingTransactionCount(ctx context.Context) (uint, error) {
|
||||
// case the code is taken from the latest known block. Note that state from very old
|
||||
// blocks might not be available.
|
||||
func (ec *Client) CallContract(ctx context.Context, msg ethereum.CallMsg, blockNumber *big.Int) ([]byte, error) {
|
||||
var hex string
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), toBlockNumArg(blockNumber))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return hex, nil
|
||||
}
|
||||
|
||||
// PendingCallContract executes a message call transaction using the EVM.
|
||||
// The state seen by the contract call is the pending state.
|
||||
func (ec *Client) PendingCallContract(ctx context.Context, msg ethereum.CallMsg) ([]byte, error) {
|
||||
var hex string
|
||||
var hex hexutil.Bytes
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_call", toCallArg(msg), "pending")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return common.FromHex(hex), nil
|
||||
return hex, nil
|
||||
}
|
||||
|
||||
// SuggestGasPrice retrieves the currently suggested gas price to allow a timely
|
||||
// execution of a transaction.
|
||||
func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
var hex rpc.HexNumber
|
||||
var hex hexutil.Big
|
||||
if err := ec.c.CallContext(ctx, &hex, "eth_gasPrice"); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -378,7 +401,7 @@ func (ec *Client) SuggestGasPrice(ctx context.Context) (*big.Int, error) {
|
||||
// the true gas limit requirement as other transactions may be added or removed by miners,
|
||||
// but it should provide a basis for setting a reasonable default.
|
||||
func (ec *Client) EstimateGas(ctx context.Context, msg ethereum.CallMsg) (*big.Int, error) {
|
||||
var hex rpc.HexNumber
|
||||
var hex hexutil.Big
|
||||
err := ec.c.CallContext(ctx, &hex, "eth_estimateGas", toCallArg(msg))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -404,16 +427,16 @@ func toCallArg(msg ethereum.CallMsg) interface{} {
|
||||
"to": msg.To,
|
||||
}
|
||||
if len(msg.Data) > 0 {
|
||||
arg["data"] = fmt.Sprintf("%#x", msg.Data)
|
||||
arg["data"] = hexutil.Bytes(msg.Data)
|
||||
}
|
||||
if msg.Value != nil {
|
||||
arg["value"] = fmt.Sprintf("%#x", msg.Value)
|
||||
arg["value"] = (*hexutil.Big)(msg.Value)
|
||||
}
|
||||
if msg.Gas != nil {
|
||||
arg["gas"] = fmt.Sprintf("%#x", msg.Gas)
|
||||
arg["gas"] = (*hexutil.Big)(msg.Gas)
|
||||
}
|
||||
if msg.GasPrice != nil {
|
||||
arg["gasPrice"] = fmt.Sprintf("%#x", msg.GasPrice)
|
||||
arg["gasPrice"] = (*hexutil.Big)(msg.GasPrice)
|
||||
}
|
||||
return arg
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user